renshi 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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,11 @@
1
+ module Renshi
2
+ module AttributeExpressions
3
+ class Else
4
+ def evaluate(expression, node)
5
+ node.open_clause("else")
6
+ node.close_clause("end")
7
+ node.remove_attribute("r:else")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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,11 @@
1
+ module Renshi
2
+ module AttributeExpressions
3
+ class For
4
+ def evaluate(expression, node)
5
+ node.open_clause("for #{expression}")
6
+ node.close_clause("end")
7
+ node.remove_attribute("r:for")
8
+ end
9
+ end
10
+ end
11
+ 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
+
@@ -0,0 +1,9 @@
1
+ module Renshi
2
+ module Frameworks
3
+ #framework integrations can be loaded within notice by checking the environment
4
+ def self.notice
5
+ require 'renshi/frameworks/rails' if defined? ENV['RAILS_ENV']
6
+ end
7
+ end
8
+ end
9
+
@@ -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 &lt; 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
@@ -0,0 +1,4 @@
1
+ <body>
2
+ $[$i = 1]
3
+ <div id='content$i'>hello</div>
4
+ <body>
data/spec/data/for.ren ADDED
@@ -0,0 +1,5 @@
1
+ <div id="outer">
2
+ <div id="content$foo" r:for="foo in foos">
3
+ hello$foo
4
+ </div>
5
+ </div>
@@ -0,0 +1,16 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml">
2
+ <head>
3
+ <title>$title</title>
4
+ </head>
5
+ <body class="index">
6
+ <div id="header">
7
+ <h1>$title</h1>
8
+ </div>
9
+
10
+ <p>Hello</p>
11
+
12
+ <div id="footer">
13
+ <hr />
14
+ </div>
15
+ </body>
16
+ </html>
@@ -0,0 +1,7 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml">
2
+ <body>
3
+ <div id="content" r:if="foo">
4
+ hello
5
+ </div>
6
+ </body>
7
+ </html>
@@ -0,0 +1,7 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml">
2
+ <body>
3
+ <div id="content" r:if="1 + 1 == 2">
4
+ hello
5
+ </div>
6
+ </body>
7
+ </html>
@@ -0,0 +1,10 @@
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
+ </body>
10
+ </html>
@@ -0,0 +1,13 @@
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:else>
10
+ goodbye
11
+ </div>
12
+ </body>
13
+ </html>
@@ -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>
@@ -0,0 +1,6 @@
1
+ <div id="outer">
2
+ <div r:while="foo != 2" id="content$foo">
3
+ hello$foo
4
+ $[foo = foo + 1]
5
+ </div>
6
+ </div>
@@ -0,0 +1,6 @@
1
+ <div id="outer">
2
+ <div id="content$foo" r:while="foo < 2">
3
+ hello$foo
4
+ $[foo = foo + 1]
5
+ </div>
6
+ </div>
@@ -0,0 +1,5 @@
1
+ <body>
2
+ <div id="content">
3
+ $foo
4
+ </div>
5
+ </body>
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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'nokogiri'
3
+
4
+ describe Renshi do
5
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -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
@@ -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