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 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