renshi 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +189 -0
- data/lib/renshi/attribute_expressions/else.rb +11 -0
- data/lib/renshi/attribute_expressions/elsif.rb +26 -0
- data/lib/renshi/attribute_expressions/for.rb +11 -0
- data/lib/renshi/attribute_expressions/if.rb +26 -0
- data/lib/renshi/attribute_expressions/while.rb +14 -0
- data/lib/renshi/attribute_expressions.rb +17 -0
- data/lib/renshi/frameworks/rails.rb +27 -0
- data/lib/renshi/frameworks.rb +9 -0
- data/lib/renshi/node.rb +34 -0
- data/lib/renshi/parser.rb +145 -0
- data/lib/renshi/statement.rb +17 -0
- data/lib/renshi.rb +18 -0
- data/spec/data/attribute_values_parsed.ren +4 -0
- data/spec/data/for.ren +5 -0
- data/spec/data/hello_world1.ren +16 -0
- data/spec/data/if_1.ren +7 -0
- data/spec/data/if_2.ren +7 -0
- data/spec/data/if_3.ren +10 -0
- data/spec/data/ifelse.ren +13 -0
- data/spec/data/ifelsifelse.ren +16 -0
- data/spec/data/while.ren +6 -0
- data/spec/data/while_lt.ren +6 -0
- data/spec/data/white_space.ren +5 -0
- data/spec/else_spec.rb +18 -0
- data/spec/elsif_spec.rb +19 -0
- data/spec/for_spec.rb +14 -0
- data/spec/if_spec.rb +31 -0
- data/spec/node_spec.rb +70 -0
- data/spec/parser_spec.rb +36 -0
- data/spec/renshi_spec.rb +5 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/while_spec.rb +13 -0
- data/spec/xml_entities_in_expressions_spec.rb +13 -0
- metadata +99 -0
data/README
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
Renshi is a templating language for Ruby which is unobtrusive in HTML and XHTML structures.
|
2
|
+
|
3
|
+
$ Code
|
4
|
+
======
|
5
|
+
To output data on the page stick a $ in front of your expression
|
6
|
+
|
7
|
+
$foo
|
8
|
+
$Time.now
|
9
|
+
|
10
|
+
If there's a space in your expression or you need formatting around your expression use ${}
|
11
|
+
|
12
|
+
${foo},$Time.now
|
13
|
+
|
14
|
+
To insert code in your template use $[]
|
15
|
+
|
16
|
+
$[foo = "hello world"]
|
17
|
+
|
18
|
+
$foo
|
19
|
+
|
20
|
+
Attribute Expressions
|
21
|
+
===================
|
22
|
+
Renshi has a small library of expressions which can be inserted into HTML or XML elements as attributes.
|
23
|
+
Attribute expressions follow the form of
|
24
|
+
|
25
|
+
r:expression_name="expression" - the expression value is treated as a Ruby expression .
|
26
|
+
|
27
|
+
* Current Attribute Expressions
|
28
|
+
* If
|
29
|
+
* Elsif
|
30
|
+
* Else
|
31
|
+
* While
|
32
|
+
* For
|
33
|
+
|
34
|
+
|
35
|
+
* If
|
36
|
+
<span r:if="true">hello!</span> would render "<span>hello</span>"
|
37
|
+
|
38
|
+
<span r:if="false">goodbye!</span> will render "".
|
39
|
+
|
40
|
+
* Elsif
|
41
|
+
|
42
|
+
<span r:if="false">hello!</span>
|
43
|
+
<span r:elsif="true">goodbye</span>
|
44
|
+
|
45
|
+
* Else
|
46
|
+
|
47
|
+
<span r:if="false">hello!</span>
|
48
|
+
<span r:elsif="false">goodbye</span>
|
49
|
+
<span r:else>neither</span>
|
50
|
+
|
51
|
+
r:elsif and r:else must be adjacent (a sibling) to the element.
|
52
|
+
|
53
|
+
|
54
|
+
For example, the below is fine, because the if and else statements are adjacent siblings in the tree of elements.
|
55
|
+
|
56
|
+
<div id="menu" r:if="user.is_administrator?">
|
57
|
+
<div id="admin_menu"> ... </div>
|
58
|
+
<div id="menu" r:else>
|
59
|
+
<div id="user_menu"> ... </div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
but this is not:
|
63
|
+
|
64
|
+
<div id="menu" r:if="user.is_administrator?">
|
65
|
+
<div id="admin_menu"> ... </div>
|
66
|
+
<div id="register">
|
67
|
+
<div id="register"> ... </div>
|
68
|
+
</div>
|
69
|
+
<div id="menu" r:else>
|
70
|
+
<div id="user_menu"> ... </div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
but this is:
|
74
|
+
|
75
|
+
<div id="menu" r:if="user.is_administrator?">
|
76
|
+
<div id="admin_menu"> ... </div>
|
77
|
+
<div id="menu" r:else>
|
78
|
+
<div id="user_menu"> ... </div>
|
79
|
+
</div>
|
80
|
+
<div id="register">
|
81
|
+
<div id="register"> ... </div>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
|
85
|
+
* While
|
86
|
+
|
87
|
+
<div r:while="foo != 2" id="content$foo">
|
88
|
+
hello$foo
|
89
|
+
$[foo = foo + 1]
|
90
|
+
</div>
|
91
|
+
|
92
|
+
renders:
|
93
|
+
|
94
|
+
<div id="content0">
|
95
|
+
hello0
|
96
|
+
|
97
|
+
</div><div id="content1">
|
98
|
+
hello1
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
* For
|
103
|
+
|
104
|
+
<div id="content$foo" r:for="foo in foos">
|
105
|
+
hello$foo
|
106
|
+
</div>
|
107
|
+
|
108
|
+
renders:
|
109
|
+
|
110
|
+
<div id="content0">
|
111
|
+
hello0
|
112
|
+
</div><div id="content1">
|
113
|
+
hello1
|
114
|
+
</div><div id="content2">
|
115
|
+
hello2
|
116
|
+
</div>
|
117
|
+
|
118
|
+
Further elements will appear as I have time to make them or others want to contribute them. There isn't much to making them.
|
119
|
+
|
120
|
+
See renshi/lib/renshi/attribute_expressions for how they work.
|
121
|
+
|
122
|
+
Planned element expressions:
|
123
|
+
|
124
|
+
* r:each
|
125
|
+
* r:unless
|
126
|
+
* r:def - something like genshi's py:def
|
127
|
+
|
128
|
+
Other Rules
|
129
|
+
===========
|
130
|
+
$ values inside of attributes are only interpreted as Renshi variables for regular attributes (not renshi attributes).
|
131
|
+
|
132
|
+
E.g. <div r:if="'abc' =~ /$abc/" id="content$foo"> the r:if statement would treat $ exactly as Ruby does, while the id attribute value is transformed.
|
133
|
+
|
134
|
+
|
135
|
+
Miscellaneous
|
136
|
+
=============
|
137
|
+
To print a $ use $$, e.g. $$10.95 comes out as $10.95
|
138
|
+
|
139
|
+
Renshi documents are appended with the suffix of .ren
|
140
|
+
|
141
|
+
e.g. index.html.ren or index.ren
|
142
|
+
|
143
|
+
|
144
|
+
Framework Integration
|
145
|
+
=====================
|
146
|
+
|
147
|
+
* Rails
|
148
|
+
Simply require the renshi gem in your code, and Renshi will make itself available.
|
149
|
+
|
150
|
+
There is a Rails 2.3.2 example app in the examples directory. Go there, start up the application, and visit http://localhost:3000/hello
|
151
|
+
|
152
|
+
I'd welcome integrations for Sinatra and Merb or XYZ framework. See renshi/lib/renshi/frameworks
|
153
|
+
|
154
|
+
|
155
|
+
Gem Building
|
156
|
+
============
|
157
|
+
run
|
158
|
+
`rake gem`
|
159
|
+
then
|
160
|
+
`sudo gem install pkg/renshi-0.0.2.gem`
|
161
|
+
|
162
|
+
|
163
|
+
Development
|
164
|
+
===========
|
165
|
+
Please report any bugs at the http://renshi.lighthouseapp.com .
|
166
|
+
|
167
|
+
Contributions are welcome. Right now it's just me.
|
168
|
+
|
169
|
+
Why Renshi?
|
170
|
+
===========
|
171
|
+
Firstly, it doesn't need a reason. It's a fun project.
|
172
|
+
|
173
|
+
But ...
|
174
|
+
|
175
|
+
I've always found ERB to be a bit cumbersome - <%= is quite tiring to type out when you realise it could be much shorter. I used to think that Velocity, in Java,
|
176
|
+
was the most fun templating language I'd used, and I had wanted something equally as concise for Ruby.
|
177
|
+
|
178
|
+
A real need for it emerged in a project which relied upon an external design house handing us HTML. Converting it incessantly into HAML was nightmarish. A colleague
|
179
|
+
mentioned Genshi in Python as ideal, which was when the idea of Renshi was conceived.
|
180
|
+
|
181
|
+
English to Japanese
|
182
|
+
===================
|
183
|
+
Renshi (錬士 : れんし ?): instructor.
|
184
|
+
|
185
|
+
In Japanese 'renshi' refers to a martial arts master - http://en.wikipedia.org/wiki/Renshi. 'Ren' means 'polished' and 'shi' means 'person'.
|
186
|
+
|
187
|
+
It apparently also means 'to know another' in Mandarin.
|
188
|
+
|
189
|
+
The project was originally inspired by Genshi, http://genshi.edgewall.org - though it is significantly different in its approach.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Renshi
|
2
|
+
module AttributeExpressions
|
3
|
+
class Elsif
|
4
|
+
def evaluate(expression, node)
|
5
|
+
node.open_clause("elsif (#{expression})")
|
6
|
+
|
7
|
+
sibling = node.next_real
|
8
|
+
|
9
|
+
if sibling
|
10
|
+
sibling_commands = sibling.commands
|
11
|
+
|
12
|
+
if sibling_commands.first
|
13
|
+
expression = sibling_commands.first[0][2..-1]
|
14
|
+
if expression == "else"
|
15
|
+
node.remove_attribute("r:elsif")
|
16
|
+
return
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
node.close_clause("end")
|
22
|
+
node.remove_attribute("r:elsif")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Renshi
|
2
|
+
module AttributeExpressions
|
3
|
+
class If
|
4
|
+
def evaluate(expression, node)
|
5
|
+
node.open_clause("if (#{expression})")
|
6
|
+
|
7
|
+
sibling = node.next_real
|
8
|
+
|
9
|
+
if sibling
|
10
|
+
sibling_commands = sibling.commands
|
11
|
+
|
12
|
+
if sibling_commands.first
|
13
|
+
expression = sibling_commands.first[0][2..-1]
|
14
|
+
if expression == "else" or expression == "elsif"
|
15
|
+
node.remove_attribute("r:if")
|
16
|
+
return
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
node.close_clause("end")
|
22
|
+
node.remove_attribute("r:if")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Renshi
|
2
|
+
module AttributeExpressions
|
3
|
+
class While
|
4
|
+
include Renshi::AttributeExpressions
|
5
|
+
|
6
|
+
def evaluate(expression, node)
|
7
|
+
expression = encode_xml_entities(expression)
|
8
|
+
node.open_clause("while (#{expression})")
|
9
|
+
node.close_clause("end")
|
10
|
+
node.remove_attribute("r:while")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'renshi/attribute_expressions/if'
|
2
|
+
require 'renshi/attribute_expressions/elsif'
|
3
|
+
require 'renshi/attribute_expressions/else'
|
4
|
+
require 'renshi/attribute_expressions/while'
|
5
|
+
require 'renshi/attribute_expressions/for'
|
6
|
+
|
7
|
+
module Renshi
|
8
|
+
module AttributeExpressions
|
9
|
+
|
10
|
+
def encode_xml_entities(expression)
|
11
|
+
expression.gsub!("<", Renshi::Parser::XML_LT)
|
12
|
+
expression.gsub!(">", Renshi::Parser::XML_GT)
|
13
|
+
expression.gsub!("&", Renshi::Parser::XML_AMP)
|
14
|
+
return expression
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Renshi
|
2
|
+
module Frameworks
|
3
|
+
module Rails
|
4
|
+
if defined? ActionView::TemplateHandler
|
5
|
+
class CompilablePlugin
|
6
|
+
include ActionView::TemplateHandlers::Compilable
|
7
|
+
|
8
|
+
def compile(template)
|
9
|
+
if template.respond_to? :source
|
10
|
+
source = template.source
|
11
|
+
else
|
12
|
+
source = template
|
13
|
+
end
|
14
|
+
|
15
|
+
out = Renshi::Parser.parse(source)
|
16
|
+
return out
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if defined? ActionView::TemplateHandler
|
22
|
+
ActionView::Template.register_template_handler(:ren, Renshi::Frameworks::Rails::CompilablePlugin)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/lib/renshi/node.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'renshi/statement'
|
2
|
+
module Renshi
|
3
|
+
#helper methods for Renshi on the Nokogiri XMLNode
|
4
|
+
module Node
|
5
|
+
def commands
|
6
|
+
commands = []
|
7
|
+
for key in self.keys
|
8
|
+
commands << [key, self.attributes[key]] if key[0..1] == "r:"
|
9
|
+
end
|
10
|
+
return commands
|
11
|
+
end
|
12
|
+
|
13
|
+
#opens a clause on an element, for example an if statement on a div
|
14
|
+
def open_clause(opening)
|
15
|
+
self.before("#{Renshi::Parser::STRING_END} #{opening}; #{Renshi::Parser::STRING_START}")
|
16
|
+
end
|
17
|
+
|
18
|
+
#typically an end statement
|
19
|
+
def close_clause(closing)
|
20
|
+
self.after("#{Renshi::Parser::STRING_END} #{closing}; #{Renshi::Parser::STRING_START}")
|
21
|
+
end
|
22
|
+
|
23
|
+
#just a hack on next, as Nokogiri returns a new line as the next sibling.
|
24
|
+
def next_real
|
25
|
+
sibling = self.next
|
26
|
+
|
27
|
+
while sibling and sibling.text.strip == ""
|
28
|
+
sibling = sibling.next
|
29
|
+
end
|
30
|
+
|
31
|
+
return sibling
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'renshi/statement'
|
2
|
+
|
3
|
+
module Renshi
|
4
|
+
# The Renshi parser tries to respect speed without complexity. It builds a set of instructions within
|
5
|
+
# the document, which are finally compiled into Ruby.
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
STRING_END = "R_END" #maybe replace this with a funky unicode char
|
9
|
+
STRING_START = "R_START" #maybe replace this with a funky unicode char
|
10
|
+
BUFFER_CONCAT_OPEN = "@output_buffer.concat(\""
|
11
|
+
BUFFER_CONCAT_CLOSE = "\");"
|
12
|
+
NEW_LINE = "@output_buffer.concat('\n');"
|
13
|
+
|
14
|
+
#these symbols cannot be normally escaped, as we need to differentiate between < as an
|
15
|
+
#escaped string, to be left in the document, and < as a boolean operator
|
16
|
+
XML_LT = "R_LT"
|
17
|
+
XML_GT = "R_GT"
|
18
|
+
XML_AMP = "R_AMP"
|
19
|
+
|
20
|
+
def self.parse(xhtml)
|
21
|
+
doc = Nokogiri::HTML.fragment(xhtml)
|
22
|
+
|
23
|
+
doc.children.each do |node|
|
24
|
+
transform_node(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
inner_html = doc.inner_html
|
28
|
+
compiled = compile_to_buffer(inner_html) if inner_html
|
29
|
+
# puts "\n", compiled, "\n"
|
30
|
+
return compiled
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.transform_node(node)
|
34
|
+
if node.attributes
|
35
|
+
expressions = node.commands
|
36
|
+
for expression in expressions
|
37
|
+
perform_expression(node, expression)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#compile text in attribute values, e.g. <div id="foo$i>"
|
42
|
+
if node.attributes()
|
43
|
+
for attribute in node.attributes()
|
44
|
+
compiled = compile(attribute[1].to_s)
|
45
|
+
if compiled
|
46
|
+
node[attribute[0]]= (compiled)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#compile text in nodes, e.g. <p>*</p>
|
52
|
+
if node.text?
|
53
|
+
compiled = compile(node.text)
|
54
|
+
node.content = compiled if compiled
|
55
|
+
end
|
56
|
+
|
57
|
+
node.children.each {|child| transform_node(child)}
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.perform_expression(node, command)
|
61
|
+
expression = command[0][2..-1]
|
62
|
+
|
63
|
+
obj = nil
|
64
|
+
begin
|
65
|
+
obj = eval "Renshi::AttributeExpressions::#{expression.capitalize}.new"
|
66
|
+
rescue StandardError
|
67
|
+
raise Renshi::SyntaxError, "Could not find attribute expression called #{expression}.rb", caller
|
68
|
+
end
|
69
|
+
|
70
|
+
obj.evaluate(command[1].to_s, node)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.compile(text)
|
74
|
+
idx = text.index("$")
|
75
|
+
return text if idx.nil?
|
76
|
+
|
77
|
+
bits = []
|
78
|
+
bits << text[0..(idx -1)] if idx != 0
|
79
|
+
while idx != nil do
|
80
|
+
if text[(idx + 1)..(idx + 1)] == "("
|
81
|
+
#this may be jquery, etc. $(...)
|
82
|
+
return text
|
83
|
+
elsif text[(idx + 1)..(idx + 1)] == "{"
|
84
|
+
begin
|
85
|
+
closing_brace_idx = text.rindex("}")
|
86
|
+
statement_str = text[(idx + 2)..(closing_brace_idx -1)]
|
87
|
+
statement = Renshi::Statement.new(statement_str)
|
88
|
+
bits << statement.compile_to_print!
|
89
|
+
end_statement_idx = closing_brace_idx + 1
|
90
|
+
rescue Renshi::StandardError
|
91
|
+
raise SyntaxError, "No closing brace: #{text[(idx +1)..-1]}", caller
|
92
|
+
end
|
93
|
+
elsif text[(idx + 1)..(idx + 1)] == "["
|
94
|
+
begin
|
95
|
+
closing_brace_idx = text.rindex("]")
|
96
|
+
statement_str = text[(idx + 2)..(closing_brace_idx -1)]
|
97
|
+
statement = Renshi::Statement.new(statement_str)
|
98
|
+
bits << statement.compile_to_expression!
|
99
|
+
end_statement_idx = closing_brace_idx + 1
|
100
|
+
rescue Renshi::StandardError
|
101
|
+
raise SyntaxError, "No closing bracket: #{text[(idx +1)..-1]}", caller
|
102
|
+
end
|
103
|
+
else #$foo
|
104
|
+
words = text[(idx +1)..-1].split(/\s/)
|
105
|
+
words[0] = "'$'" if words[0] == "$"
|
106
|
+
statement_str = words.first
|
107
|
+
statement = Statement.new(statement_str)
|
108
|
+
bits << statement.compile_to_print!
|
109
|
+
end_statement_idx = (words.first.length) + 1 + idx
|
110
|
+
end
|
111
|
+
|
112
|
+
next_statement_idx = text.index("$", end_statement_idx)
|
113
|
+
|
114
|
+
if next_statement_idx
|
115
|
+
gap = text[end_statement_idx..(next_statement_idx -1)]
|
116
|
+
bits << gap
|
117
|
+
else
|
118
|
+
bits << text[end_statement_idx..-1]
|
119
|
+
end
|
120
|
+
idx = next_statement_idx
|
121
|
+
end
|
122
|
+
|
123
|
+
return bits.join
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.compile_to_buffer(str)
|
127
|
+
compiled = "@output_buffer ='';" << BUFFER_CONCAT_OPEN
|
128
|
+
str = compile_print_flags(str)
|
129
|
+
compiled = "#{compiled}#{str}" << BUFFER_CONCAT_CLOSE
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.compile_print_flags(str)
|
133
|
+
#now we parse for RENSHI::STRING_END and RENSHI::STRING_START
|
134
|
+
#and ensure natural strings are buffered
|
135
|
+
str.gsub!("\"", "\\\"")
|
136
|
+
str.gsub!(STRING_END, BUFFER_CONCAT_CLOSE)
|
137
|
+
str.gsub!(STRING_START, BUFFER_CONCAT_OPEN)
|
138
|
+
str.gsub!(XML_GT, ">")
|
139
|
+
str.gsub!(XML_LT, "<")
|
140
|
+
str.gsub!(XML_AMP, "&")
|
141
|
+
|
142
|
+
return str
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Renshi
|
2
|
+
class Statement
|
3
|
+
attr_accessor :stmt
|
4
|
+
|
5
|
+
def initialize(str)
|
6
|
+
@stmt = str
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile_to_expression!
|
10
|
+
str = "#{Renshi::Parser::STRING_END}#{self.stmt};#{Renshi::Parser::STRING_START}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def compile_to_print!
|
14
|
+
str = "#{Renshi::Parser::STRING_END}@output_buffer.concat((#{self.stmt}).to_s);#{Renshi::Parser::STRING_START}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/renshi.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
# Renshi's aim is to be a lightweight library with dependencies solely on nokogiri.
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'renshi/parser'
|
7
|
+
require 'renshi/node'
|
8
|
+
require 'renshi/attribute_expressions'
|
9
|
+
require 'renshi/frameworks'
|
10
|
+
|
11
|
+
module Renshi
|
12
|
+
VERSION="0.0.4"
|
13
|
+
|
14
|
+
class SyntaxError < StandardError; end
|
15
|
+
end
|
16
|
+
|
17
|
+
Nokogiri::XML::Node.send(:include, Renshi::Node)
|
18
|
+
Renshi::Frameworks.notice
|
data/spec/data/for.ren
ADDED
data/spec/data/if_1.ren
ADDED
data/spec/data/if_2.ren
ADDED
data/spec/data/if_3.ren
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
2
|
+
<body>
|
3
|
+
<div id="content" r:if="foo">
|
4
|
+
hello
|
5
|
+
<div id="inner_content" r:if="true">
|
6
|
+
world
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<div id="content" r:elsif="bar">
|
10
|
+
neither
|
11
|
+
</div>
|
12
|
+
<div id="content" r:else>
|
13
|
+
goodbye
|
14
|
+
</div>
|
15
|
+
</body>
|
16
|
+
</html>
|
data/spec/data/while.ren
ADDED
data/spec/else_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:else" do
|
6
|
+
foo = true
|
7
|
+
out = interpret("data/ifelse.ren", binding)
|
8
|
+
doc = N(out)
|
9
|
+
(doc/"div[@id='content']").text.strip.should =~ /hello/
|
10
|
+
(doc/"div[@id='inner_content']").text.strip.should eql "world"
|
11
|
+
|
12
|
+
foo = false
|
13
|
+
|
14
|
+
out = interpret("data/ifelse.ren", binding)
|
15
|
+
doc = N(out)
|
16
|
+
(doc/"div[@id='content']").text.strip.should =~ /goodbye/
|
17
|
+
end
|
18
|
+
end
|
data/spec/elsif_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:elsif" do
|
6
|
+
foo = true
|
7
|
+
bar = false
|
8
|
+
out = interpret("data/ifelsifelse.ren", binding)
|
9
|
+
doc = N(out)
|
10
|
+
(doc/"div[@id='content']").text.strip.should =~ /hello/
|
11
|
+
(doc/"div[@id='inner_content']").text.strip.should eql "world"
|
12
|
+
|
13
|
+
foo = false
|
14
|
+
bar = true
|
15
|
+
out = interpret("data/ifelsifelse.ren", binding)
|
16
|
+
doc = N(out)
|
17
|
+
(doc/"div[@id='content']").text.strip.should =~ /neither/
|
18
|
+
end
|
19
|
+
end
|
data/spec/for_spec.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:for" do
|
6
|
+
foos = [0,1,2]
|
7
|
+
compiled = compile_file("data/for.ren")
|
8
|
+
out = eval compiled
|
9
|
+
doc = N(out)
|
10
|
+
(doc/"div[@id='content0']").text.strip.should =~ /hello0/
|
11
|
+
(doc/"div[@id='content1']").text.strip.should =~ /hello1/
|
12
|
+
(doc/"div[@id='content2']").text.strip.should =~ /hello2/
|
13
|
+
end
|
14
|
+
end
|
data/spec/if_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:ifs" do
|
6
|
+
foo = true
|
7
|
+
out = interpret("data/if_1.ren", binding)
|
8
|
+
doc = N(out)
|
9
|
+
(doc/"div[@id='content']").text.strip.should eql "hello"
|
10
|
+
|
11
|
+
foo = false
|
12
|
+
out = interpret("data/if_1.ren", binding)
|
13
|
+
doc = N(out)
|
14
|
+
(doc/"div[@id='content']").text.strip.should eql ""
|
15
|
+
end
|
16
|
+
|
17
|
+
it "another r:if test" do
|
18
|
+
foo = true
|
19
|
+
out = interpret("data/if_2.ren", binding)
|
20
|
+
doc = N(out)
|
21
|
+
(doc/"div[@id='content']").text.strip.should eql "hello"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should evaluate nested r:ifs" do
|
25
|
+
foo = true
|
26
|
+
out = interpret("data/if_3.ren", binding)
|
27
|
+
doc = N(out)
|
28
|
+
(doc/"div[@id='content']").text.strip.should =~ /hello/
|
29
|
+
(doc/"div[@id='inner_content']").text.strip.should eql "world"
|
30
|
+
end
|
31
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Node do
|
5
|
+
|
6
|
+
it "should return the commands in an XML element's attributes" do
|
7
|
+
doc = Nokogiri::HTML("<span r:if='true' r:while='true'/>")
|
8
|
+
|
9
|
+
body = doc.root.children.first
|
10
|
+
node = body.children.first
|
11
|
+
node.attributes.size.should eql 2
|
12
|
+
node.commands.size.should eql 2
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should interpret $foo" do
|
16
|
+
foo = "hello world"
|
17
|
+
doc = Nokogiri::HTML("$foo")
|
18
|
+
|
19
|
+
body = doc.root.children.first
|
20
|
+
node = body.children.first
|
21
|
+
eval(deliver_compiled(node), binding).should eql foo
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should interpret $foo $bar" do
|
25
|
+
foo = "hello"
|
26
|
+
bar = "world"
|
27
|
+
doc = Nokogiri::HTML("$foo $bar")
|
28
|
+
|
29
|
+
body = doc.root.children.first
|
30
|
+
node = body.children.first
|
31
|
+
eval(deliver_compiled(node), binding).should eql "#{foo} #{bar}"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should interpret ${1 + 1}" do
|
35
|
+
doc = Nokogiri::HTML("${1 + 1}")
|
36
|
+
body = doc.root.children.first
|
37
|
+
node = body.children.first
|
38
|
+
eval(deliver_compiled(node), binding).should eql "2"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should interpret ${1 + 1} $foo" do
|
42
|
+
foo = "is a number"
|
43
|
+
doc = Nokogiri::HTML("${1 + 1} $foo")
|
44
|
+
body = doc.root.children.first
|
45
|
+
node = body.children.first
|
46
|
+
eval(deliver_compiled(node), binding).should eql "2 is a number"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should interpret ${[0,1,2,3,4].each {|i| print i}}" do
|
50
|
+
foo = "is a number"
|
51
|
+
doc = Nokogiri::HTML("${[0,1,2,3,4].each {|i| print i}}")
|
52
|
+
body = doc.root.children.first
|
53
|
+
node = body.children.first
|
54
|
+
eval(deliver_compiled(node), binding).should eql "01234"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should update the binding after each execution" do
|
58
|
+
doc = Nokogiri::HTML("$[@foo = 'hello'] $@foo")
|
59
|
+
body = doc.root.children.first
|
60
|
+
node = body.children.first
|
61
|
+
eval(deliver_compiled(node), binding).should eql " hello"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should output $ when given two $$" do
|
65
|
+
doc = Nokogiri::HTML("$$")
|
66
|
+
body = doc.root.children.first
|
67
|
+
node = body.children.first
|
68
|
+
eval(deliver_compiled(node), binding).should eql "$"
|
69
|
+
end
|
70
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
def N(str)
|
5
|
+
Nokogiri::XML(str)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Renshi::Parser do
|
9
|
+
it "should parse a $foo var in elements" do
|
10
|
+
title = "hello world"
|
11
|
+
out = interpret("data/hello_world1.ren", binding)
|
12
|
+
|
13
|
+
doc = Nokogiri::XML(out)
|
14
|
+
(doc/"title").text.should eql "hello world"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should interpret vars surrounded by whitespace" do
|
18
|
+
foo = "in space no one can hear you scream"
|
19
|
+
out = interpret("data/white_space.ren", binding)
|
20
|
+
doc = N(out)
|
21
|
+
(doc/"div[@id='content']").text.strip.should eql "in space no one can hear you scream"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should ignore $(..)" do
|
25
|
+
doc = Nokogiri::HTML.fragment("$(foo)")
|
26
|
+
node = doc.children.first
|
27
|
+
eval(deliver_compiled(node), binding).should eql "$(foo)"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should parse attribute values - e.g. <div id='content$i'>" do
|
31
|
+
i = 1
|
32
|
+
html = interpret("data/attribute_values_parsed.ren", binding)
|
33
|
+
html = N(html)
|
34
|
+
(html/"div[@id='content1']").text.strip.should =~ /hello/
|
35
|
+
end
|
36
|
+
end
|
data/spec/renshi_spec.rb
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'ruby-debug'
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/renshi")
|
5
|
+
|
6
|
+
|
7
|
+
def read_file(file_name)
|
8
|
+
file = File.read(File.expand_path(File.dirname(__FILE__) + "/#{file_name}"))
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile_file(file)
|
12
|
+
compiled = Renshi::Parser.parse(read_file(file))
|
13
|
+
end
|
14
|
+
|
15
|
+
def interpret(file, context)
|
16
|
+
eval(compile_file(file), context)
|
17
|
+
end
|
18
|
+
|
19
|
+
def deliver_compiled(node)
|
20
|
+
raw = Renshi::Parser.compile(node.text)
|
21
|
+
raw = Renshi::Parser.compile_to_buffer(raw)
|
22
|
+
end
|
23
|
+
|
24
|
+
def N(str)
|
25
|
+
Nokogiri::XML(str)
|
26
|
+
end
|
data/spec/while_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:while" do
|
6
|
+
foo = 0
|
7
|
+
compiled = compile_file("data/while.ren")
|
8
|
+
out = eval compiled
|
9
|
+
doc = N(out)
|
10
|
+
(doc/"div[@id='content0']").text.strip.should =~ /hello0/
|
11
|
+
(doc/"div[@id='content1']").text.strip.should =~ /hello1/
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Renshi::Parser do
|
5
|
+
it "should evaluate r:while(foo < 2)" do
|
6
|
+
foo = 0
|
7
|
+
compiled = compile_file("data/while_lt.ren")
|
8
|
+
out = eval compiled
|
9
|
+
doc = N(out)
|
10
|
+
(doc/"div[@id='content0']").text.strip.should =~ /hello0/
|
11
|
+
(doc/"div[@id='content1']").text.strip.should =~ /hello1/
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: renshi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicholas Faiz
|
8
|
+
autorequire: renshi
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-10 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nokogiri
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.3
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: nicholas.faiz@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
files:
|
34
|
+
- lib/renshi
|
35
|
+
- lib/renshi/attribute_expressions
|
36
|
+
- lib/renshi/attribute_expressions/else.rb
|
37
|
+
- lib/renshi/attribute_expressions/elsif.rb
|
38
|
+
- lib/renshi/attribute_expressions/for.rb
|
39
|
+
- lib/renshi/attribute_expressions/if.rb
|
40
|
+
- lib/renshi/attribute_expressions/while.rb
|
41
|
+
- lib/renshi/attribute_expressions.rb
|
42
|
+
- lib/renshi/frameworks
|
43
|
+
- lib/renshi/frameworks/rails.rb
|
44
|
+
- lib/renshi/frameworks.rb
|
45
|
+
- lib/renshi/node.rb
|
46
|
+
- lib/renshi/parser.rb
|
47
|
+
- lib/renshi/statement.rb
|
48
|
+
- lib/renshi.rb
|
49
|
+
- README
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://treefallinginthewoods.com
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.3.1
|
73
|
+
signing_key:
|
74
|
+
specification_version: 2
|
75
|
+
summary: Renshi is a lightweight XHTML template language, inspired by Python's Genshi and build on Nokogiri - http://nokogiri.rubyforge.org
|
76
|
+
test_files:
|
77
|
+
- spec/data
|
78
|
+
- spec/data/attribute_values_parsed.ren
|
79
|
+
- spec/data/for.ren
|
80
|
+
- spec/data/hello_world1.ren
|
81
|
+
- spec/data/if_1.ren
|
82
|
+
- spec/data/if_2.ren
|
83
|
+
- spec/data/if_3.ren
|
84
|
+
- spec/data/ifelse.ren
|
85
|
+
- spec/data/ifelsifelse.ren
|
86
|
+
- spec/data/while.ren
|
87
|
+
- spec/data/while_lt.ren
|
88
|
+
- spec/data/white_space.ren
|
89
|
+
- spec/else_spec.rb
|
90
|
+
- spec/elsif_spec.rb
|
91
|
+
- spec/for_spec.rb
|
92
|
+
- spec/if_spec.rb
|
93
|
+
- spec/node_spec.rb
|
94
|
+
- spec/parser_spec.rb
|
95
|
+
- spec/renshi_spec.rb
|
96
|
+
- spec/spec.opts
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- spec/while_spec.rb
|
99
|
+
- spec/xml_entities_in_expressions_spec.rb
|