renshi 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|