flavour_saver 0.3.3

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +81 -0
  6. data/Guardfile +12 -0
  7. data/LICENSE +22 -0
  8. data/README.md +339 -0
  9. data/Rakefile +2 -0
  10. data/flavour_saver.gemspec +28 -0
  11. data/lib/flavour_saver/helpers.rb +118 -0
  12. data/lib/flavour_saver/lexer.rb +127 -0
  13. data/lib/flavour_saver/nodes.rb +177 -0
  14. data/lib/flavour_saver/parser.rb +183 -0
  15. data/lib/flavour_saver/partial.rb +29 -0
  16. data/lib/flavour_saver/rails_partial.rb +10 -0
  17. data/lib/flavour_saver/runtime.rb +269 -0
  18. data/lib/flavour_saver/template.rb +19 -0
  19. data/lib/flavour_saver/version.rb +3 -0
  20. data/lib/flavour_saver.rb +78 -0
  21. data/spec/acceptance/backtrack_spec.rb +14 -0
  22. data/spec/acceptance/comment_spec.rb +12 -0
  23. data/spec/acceptance/custom_block_helper_spec.rb +35 -0
  24. data/spec/acceptance/custom_helper_spec.rb +15 -0
  25. data/spec/acceptance/ensure_no_rce_spec.rb +26 -0
  26. data/spec/acceptance/handlebars_qunit_spec.rb +911 -0
  27. data/spec/acceptance/if_else_spec.rb +17 -0
  28. data/spec/acceptance/multi_level_with_spec.rb +15 -0
  29. data/spec/acceptance/one_character_identifier_spec.rb +13 -0
  30. data/spec/acceptance/runtime_run_spec.rb +27 -0
  31. data/spec/acceptance/sections_spec.rb +25 -0
  32. data/spec/acceptance/segment_literals_spec.rb +26 -0
  33. data/spec/acceptance/simple_expression_spec.rb +13 -0
  34. data/spec/fixtures/backtrack.hbs +4 -0
  35. data/spec/fixtures/comment.hbs +1 -0
  36. data/spec/fixtures/custom_block_helper.hbs +3 -0
  37. data/spec/fixtures/custom_helper.hbs +1 -0
  38. data/spec/fixtures/if_else.hbs +5 -0
  39. data/spec/fixtures/multi_level_if.hbs +12 -0
  40. data/spec/fixtures/multi_level_with.hbs +11 -0
  41. data/spec/fixtures/one_character_identifier.hbs +1 -0
  42. data/spec/fixtures/sections.hbs +9 -0
  43. data/spec/fixtures/simple_expression.hbs +1 -0
  44. data/spec/lib/flavour_saver/lexer_spec.rb +187 -0
  45. data/spec/lib/flavour_saver/parser_spec.rb +277 -0
  46. data/spec/lib/flavour_saver/runtime_spec.rb +190 -0
  47. data/spec/lib/flavour_saver/template_spec.rb +5 -0
  48. metadata +243 -0
@@ -0,0 +1,17 @@
1
+ require 'tilt'
2
+ require 'flavour_saver'
3
+
4
+ describe 'Fixture: if_else.hbs' do
5
+ subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, ' ').strip }
6
+ let(:context) { Struct.new(:name).new }
7
+ let(:template) { File.expand_path('../../fixtures/if_else.hbs', __FILE__) }
8
+
9
+ it 'renders correctly when given a name' do
10
+ context.name = 'Alan'
11
+ subject.should == "Say hello to Alan."
12
+ end
13
+
14
+ it 'renders correcrtly when not given a name' do
15
+ subject.should == "Nobody to say hi to."
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'tilt'
2
+ require 'flavour_saver'
3
+
4
+ describe 'Fixture: multi_level_with.hbs' do
5
+ subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, ' ').strip }
6
+ let(:template) { File.expand_path('../../fixtures/multi_level_with.hbs', __FILE__) }
7
+ let(:context) { Struct.new(:person,:company).new }
8
+
9
+ it 'renders correctly when person has a name' do
10
+ context.person = Struct.new(:name).new('Alan')
11
+ context.company = Struct.new(:name).new('Rad, Inc.')
12
+ subject.should == 'Alan - Rad, Inc.'
13
+ end
14
+
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'tilt'
2
+ require 'flavour_saver'
3
+
4
+ describe 'Fixture: one_character_identifier.hbs' do
5
+ subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, ' ').strip }
6
+ let(:template) { File.expand_path('../../fixtures/one_character_identifier.hbs', __FILE__) }
7
+ let(:context) { double(:context) }
8
+
9
+ it 'renders correctly' do
10
+ context.should_receive(:a).and_return('foo')
11
+ subject.should == "foo"
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require "flavour_saver"
2
+
3
+ describe FlavourSaver::Runtime do
4
+ describe ".run" do
5
+ subject do
6
+ FlavourSaver::Runtime.run(parsed_template, context, locals, helper_names)
7
+ end
8
+
9
+ let(:parsed_template) { FlavourSaver.parse(FlavourSaver.lex(template)) }
10
+ let(:context) { Object.new }
11
+ let(:locals) { {} }
12
+ let(:helper_names) { [] }
13
+
14
+ describe "with a local helper" do
15
+ let(:template) { "{{n}} is {{#is_even n}}even{{else}}odd{{/is_even}}" }
16
+ let(:context_class) { Struct.new(:n) }
17
+ let(:context) { context_class.new(24) }
18
+
19
+ it "uses the helper" do
20
+ is_even_helper = proc { |n| n % 2 == 0 }
21
+
22
+ locals[:is_even] = is_even_helper
23
+ subject.should == "24 is even"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require 'tilt'
2
+ require 'flavour_saver'
3
+
4
+ describe 'Fixture: sections.hbs' do
5
+ subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, ' ').strip }
6
+ let(:context) { Struct.new(:name, :names, :order).new }
7
+ let(:template) { File.expand_path('../../fixtures/sections.hbs', __FILE__) }
8
+
9
+ it 'renders correctly when given a name' do
10
+ context.name = 'Alan'
11
+ subject.should == "Say hello to Alan."
12
+ end
13
+
14
+ it 'renders correctly when given a list of names' do
15
+ context.names = ['Foo', 'Bar']
16
+ subject.should == "* Foo * Bar"
17
+ end
18
+
19
+ it 'renders correctly when given an order' do
20
+ class Order; def number; 1234; end; end
21
+ context.order = Order.new
22
+ subject.should == 'Number: 1234'
23
+ end
24
+ end
25
+
@@ -0,0 +1,26 @@
1
+ require 'flavour_saver'
2
+
3
+ describe FlavourSaver do
4
+ subject { FS.evaluate(template, context) }
5
+
6
+ let(:context) { double(:context) }
7
+
8
+ after do
9
+ FS.reset_helpers
10
+ FS.reset_partials
11
+ end
12
+
13
+ describe 'segment literal array access' do
14
+ let(:template) { '{{foos.[1].bar}}' }
15
+
16
+ it 'returns "two"' do
17
+ foos = []
18
+ foos << double(:foo)
19
+ foos << double(:foo)
20
+ foos[1].should_receive(:bar).and_return('two')
21
+
22
+ context.stub(:foos).and_return(foos)
23
+ subject.should == 'two'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ require 'tilt'
2
+ require 'flavour_saver'
3
+
4
+ describe 'Fixture: simple_expression.hbs' do
5
+ subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, ' ').strip }
6
+ let(:template) { File.expand_path('../../fixtures/simple_expression.hbs', __FILE__) }
7
+ let(:context) { double(:context) }
8
+
9
+ it 'renders correctly' do
10
+ context.should_receive(:hello).and_return('hello world')
11
+ subject.should == "hello world"
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ {{#with person}}
2
+ {{name}} -
3
+ {{../company.name}}
4
+ {{/with}}
@@ -0,0 +1 @@
1
+ I am {{!not }}a very nice person!
@@ -0,0 +1,3 @@
1
+ {{#three_times}}
2
+ {{this}} time.
3
+ {{/three_times}}
@@ -0,0 +1 @@
1
+ {{say_what_again}}
@@ -0,0 +1,5 @@
1
+ {{#if name}}
2
+ Say hello to {{name}}.
3
+ {{else}}
4
+ Nobody to say hi to.
5
+ {{/if}}
@@ -0,0 +1,12 @@
1
+ {{#if person}}
2
+ {{#if person.name}}
3
+ Hi {{person.name}}.
4
+ {{else}}
5
+ Nobody to say hi to :(
6
+ {{/if}}
7
+ {{/if}}
8
+ {{#if company}}
9
+ - {{company.name}}
10
+ {{else}}
11
+ - No company.
12
+ {{/if}}
@@ -0,0 +1,11 @@
1
+ {{#with person}}
2
+ {{#with name}}
3
+ {{this}}
4
+ {{/with}}
5
+ {{/with}}
6
+ {{#with company}}
7
+ -
8
+ {{#with name}}
9
+ {{this}}
10
+ {{/with}}
11
+ {{/with}}
@@ -0,0 +1 @@
1
+ {{a}}
@@ -0,0 +1,9 @@
1
+ {{#name}}
2
+ Say hello to {{.}}{{nothing}}.
3
+ {{/name}}
4
+ {{#names}}
5
+ * {{.}}
6
+ {{/names}}
7
+ {{#order}}
8
+ Number: {{number}}
9
+ {{/order}}
@@ -0,0 +1 @@
1
+ {{hello}}
@@ -0,0 +1,187 @@
1
+ require 'flavour_saver/lexer'
2
+
3
+ describe FlavourSaver::Lexer do
4
+ it 'is an RLTK lexer' do
5
+ subject.should be_a(RLTK::Lexer)
6
+ end
7
+
8
+ describe 'Tokens' do
9
+ describe 'Expressions' do
10
+ describe '{{foo}}' do
11
+ subject { FlavourSaver::Lexer.lex "{{foo}}" }
12
+
13
+ it 'begins with an EXPRST' do
14
+ subject.first.type.should == :EXPRST
15
+ end
16
+
17
+ it 'ends with an EXPRE' do
18
+ subject[-2].type.should == :EXPRE
19
+ end
20
+
21
+ it 'contains only the identifier "foo"' do
22
+ subject[1..-3].size.should == 1
23
+ subject[1].type.should == :IDENT
24
+ subject[1].value.should == 'foo'
25
+ end
26
+ end
27
+
28
+ describe '{{foo bar}}' do
29
+ subject { FlavourSaver::Lexer.lex "{{foo bar}}" }
30
+
31
+ it 'has tokens in the correct order' do
32
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :WHITE, :IDENT, :EXPRE, :EOS ]
33
+ end
34
+
35
+ it 'has values in the correct order' do
36
+ subject.map(&:value).compact.should == [ 'foo', 'bar' ]
37
+ end
38
+ end
39
+
40
+ describe '{{foo "bar"}}' do
41
+ subject { FlavourSaver::Lexer.lex "{{foo \"bar\"}}" }
42
+
43
+ it 'has tokens in the correct order' do
44
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :WHITE, :STRING, :EXPRE, :EOS ]
45
+ end
46
+
47
+ it 'has values in the correct order' do
48
+ subject.map(&:value).compact.should == [ 'foo', 'bar' ]
49
+ end
50
+ end
51
+
52
+ describe '{{foo bar="baz" hello="goodbye"}}' do
53
+ subject { FlavourSaver::Lexer.lex '{{foo bar="baz" hello="goodbye"}}' }
54
+
55
+ it 'has tokens in the correct order' do
56
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :WHITE, :IDENT, :EQ, :STRING, :WHITE, :IDENT, :EQ, :STRING, :EXPRE, :EOS ]
57
+ end
58
+
59
+ it 'has values in the correct order' do
60
+ subject.map(&:value).compact.should == [ 'foo', 'bar', 'baz', 'hello', 'goodbye' ]
61
+ end
62
+
63
+ end
64
+ end
65
+
66
+ describe '{{else}}' do
67
+ subject { FlavourSaver::Lexer.lex '{{else}}' }
68
+
69
+ it 'has tokens in the correct order' do
70
+ subject.map(&:type).should == [ :EXPRST, :ELSE, :EXPRE, :EOS ]
71
+ end
72
+ end
73
+
74
+ describe 'Object path expressions' do
75
+ describe '{{foo.bar}}' do
76
+ subject { FlavourSaver::Lexer.lex "{{foo.bar}}" }
77
+
78
+ it 'has tokens in the correct order' do
79
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :DOT, :IDENT, :EXPRE, :EOS ]
80
+ end
81
+
82
+ it 'has the correct values' do
83
+ subject.map(&:value).compact.should == ['foo', 'bar']
84
+ end
85
+ end
86
+
87
+ describe '{{foo.[10].bar}}' do
88
+ subject { FlavourSaver::Lexer.lex "{{foo.[10].bar}}" }
89
+
90
+ it 'has tokens in the correct order' do
91
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :DOT, :LITERAL, :DOT, :IDENT, :EXPRE, :EOS ]
92
+ end
93
+
94
+ it 'has the correct values' do
95
+ subject.map(&:value).compact.should == ['foo', '10', 'bar']
96
+ end
97
+ end
98
+
99
+ describe '{{foo.[he!@#$(&@klA)].bar}}' do
100
+ subject { FlavourSaver::Lexer.lex '{{foo.[he!@#$(&@klA)].bar}}' }
101
+
102
+ it 'has tokens in the correct order' do
103
+ subject.map(&:type).should == [ :EXPRST, :IDENT, :DOT, :LITERAL, :DOT, :IDENT, :EXPRE, :EOS ]
104
+ end
105
+
106
+ it 'has the correct values' do
107
+ subject.map(&:value).compact.should == ['foo', 'he!@#$(&@klA)', 'bar']
108
+ end
109
+ end
110
+ end
111
+
112
+ describe 'Triple-stash expressions' do
113
+ subject { FlavourSaver::Lexer.lex "{{{foo}}}" }
114
+
115
+ it 'has tokens in the correct order' do
116
+ subject.map(&:type).should == [ :TEXPRST, :IDENT, :TEXPRE, :EOS ]
117
+ end
118
+ end
119
+
120
+ describe 'Comment expressions' do
121
+ subject { FlavourSaver::Lexer.lex "{{! WAT}}" }
122
+
123
+ it 'has tokens in the correct order' do
124
+ subject.map(&:type).should == [ :EXPRST, :BANG, :COMMENT, :EXPRE, :EOS ]
125
+ end
126
+ end
127
+
128
+ describe 'Backtrack expression' do
129
+ subject { FlavourSaver::Lexer.lex "{{../foo}}" }
130
+
131
+ it 'has tokens in the correct order' do
132
+ subject.map(&:type).should == [:EXPRST, :DOT, :DOT, :FWSL, :IDENT, :EXPRE, :EOS]
133
+ end
134
+ end
135
+
136
+ describe 'Carriage-return and new-lines' do
137
+ subject { FlavourSaver::Lexer.lex "{{foo}}\n{{bar}}\r{{baz}}" }
138
+
139
+ it 'has tokens in the correct order' do
140
+ subject.map(&:type).should == [:EXPRST,:IDENT,:EXPRE,:OUT,:EXPRST,:IDENT,:EXPRE,:OUT,:EXPRST,:IDENT,:EXPRE,:EOS]
141
+ end
142
+ end
143
+
144
+ describe 'Block Expressions' do
145
+ subject { FlavourSaver::Lexer.lex "{{#foo}}{{bar}}{{/foo}}" }
146
+
147
+ describe '{{#foo}}{{bar}}{{/foo}}' do
148
+ it 'has tokens in the correct order' do
149
+ subject.map(&:type).should == [
150
+ :EXPRST, :HASH, :IDENT, :EXPRE,
151
+ :EXPRST, :IDENT, :EXPRE,
152
+ :EXPRST, :FWSL, :IDENT, :EXPRE,
153
+ :EOS
154
+ ]
155
+ end
156
+
157
+ it 'has identifiers in the correct order' do
158
+ subject.map(&:value).compact.should == [ 'foo', 'bar', 'foo' ]
159
+ end
160
+ end
161
+ end
162
+
163
+ describe 'Carriage return' do
164
+ subject { FlavourSaver::Lexer.lex "\r" }
165
+
166
+ it 'has tokens in the correct order' do
167
+ subject.map(&:type).should == [:OUT,:EOS]
168
+ end
169
+ end
170
+
171
+ describe 'New line' do
172
+ subject { FlavourSaver::Lexer.lex "\n" }
173
+
174
+ it 'has tokens in the correct order' do
175
+ subject.map(&:type).should == [:OUT,:EOS]
176
+ end
177
+ end
178
+
179
+ describe 'Single curly bracket' do
180
+ subject { FlavourSaver::Lexer.lex "{" }
181
+
182
+ it 'has tokens in the correct order' do
183
+ subject.map(&:type).should == [:OUT,:EOS]
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,277 @@
1
+ require 'flavour_saver/parser'
2
+ require 'flavour_saver/lexer'
3
+
4
+ describe FlavourSaver::Parser do
5
+ let (:items) { subject.items }
6
+
7
+ it 'is a RLTK::Parser' do
8
+ subject.should be_a(RLTK::Parser)
9
+ end
10
+
11
+ describe 'HTML template' do
12
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('<html><h1>Hello world!</h1></html>')) }
13
+
14
+ it 'is an output node' do
15
+ items.first.should be_a(FlavourSaver::OutputNode)
16
+ end
17
+
18
+ it 'has the correct contents' do
19
+ items.first.value.should == '<html><h1>Hello world!</h1></html>'
20
+ end
21
+ end
22
+
23
+ describe 'HTML template containing a handlebars expression' do
24
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('<html>{{foo}}</html>')) }
25
+
26
+ it 'has template output either side of the expression' do
27
+ items.map(&:class).should == [FlavourSaver::OutputNode, FlavourSaver::ExpressionNode, FlavourSaver::OutputNode]
28
+ end
29
+ end
30
+
31
+ describe '{{foo}}' do
32
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo}}')) }
33
+
34
+ it 'contains an expression node' do
35
+ items.first.should be_an(FlavourSaver::ExpressionNode)
36
+ end
37
+
38
+ it 'calls the method "foo" with no arguments' do
39
+ items.first.method.should be_one
40
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
41
+ items.first.method.first.name.should == 'foo'
42
+ items.first.method.first.arguments.should be_empty
43
+ end
44
+ end
45
+
46
+ describe '{{foo.bar}}' do
47
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo.bar}}')) }
48
+
49
+ it 'calls two methods' do
50
+ items.first.method.size.should == 2
51
+ end
52
+
53
+ it 'calls the method "foo" with no arguments first' do
54
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
55
+ items.first.method.first.name.should == 'foo'
56
+ items.first.method.first.arguments.should be_empty
57
+ end
58
+
59
+ it 'calls the method "bar" with no arguments second' do
60
+ items.first.method[1].should be_a(FlavourSaver::CallNode)
61
+ items.first.method[1].name.should == 'bar'
62
+ items.first.method[1].arguments.should be_empty
63
+ end
64
+ end
65
+
66
+ describe '{{foo.[&@^$*].bar}}' do
67
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo.[&@^$*].bar}}')) }
68
+
69
+ it 'calls three methods' do
70
+ items.first.method.size.should == 3
71
+ end
72
+
73
+ it 'calls the method "foo" with no arguments first' do
74
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
75
+ items.first.method.first.name.should == 'foo'
76
+ items.first.method.first.arguments.should be_empty
77
+ end
78
+
79
+ it 'calls the method "&@^$*" with no arguments second' do
80
+ items.first.method[1].should be_a(FlavourSaver::CallNode)
81
+ items.first.method[1].name.should == '&@^$*'
82
+ items.first.method[1].arguments.should be_empty
83
+ end
84
+
85
+ it 'calls the method "bar" with no arguments third' do
86
+ items.first.method[2].should be_a(FlavourSaver::CallNode)
87
+ items.first.method[2].name.should == 'bar'
88
+ items.first.method[2].arguments.should be_empty
89
+ end
90
+ end
91
+
92
+ describe '{{foo bar}}' do
93
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar}}')) }
94
+
95
+ it 'calls the method "foo" with a method argument of "bar"' do
96
+ items.first.method.should be_one
97
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
98
+ items.first.method.first.name.should == 'foo'
99
+ items.first.method.first.arguments.should be_one
100
+ items.first.method.first.arguments.first.first.should be_a(FlavourSaver::CallNode)
101
+ items.first.method.first.arguments.first.first.name.should == 'bar'
102
+ end
103
+ end
104
+
105
+ describe '{{foo "bar" }}' do
106
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo "bar" }}')) }
107
+
108
+ it 'calls the method "foo" with a string argument of "bar"' do
109
+ items.first.method.should be_one
110
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
111
+ items.first.method.first.name.should == 'foo'
112
+ items.first.method.first.arguments.should be_one
113
+ items.first.method.first.arguments.first.should be_a(FlavourSaver::StringNode)
114
+ items.first.method.first.arguments.first.value.should == 'bar'
115
+ end
116
+ end
117
+
118
+ describe '{{foo bar "baz" }}' do
119
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar "baz" }}')) }
120
+
121
+ it 'calls the method "foo"' do
122
+ items.first.method.should be_one
123
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
124
+ items.first.method.first.name.should == 'foo'
125
+ end
126
+
127
+ describe 'with arguments' do
128
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar "baz" }}')).items.first.method.first.arguments }
129
+
130
+ describe '[0]' do
131
+ it 'is the method call "bar" with no arguments' do
132
+ subject.first.first.should be_a(FlavourSaver::CallNode)
133
+ subject.first.first.name.should == 'bar'
134
+ end
135
+ end
136
+
137
+ describe '[1]' do
138
+ it 'is the string "baz"' do
139
+ subject[1].should be_a(FlavourSaver::StringNode)
140
+ subject[1].value.should == 'baz'
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '{{foo bar="baz"}}' do
147
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar="baz"}}')) }
148
+
149
+ it 'calls the method "foo" with the hash {:bar => "baz"} as arguments' do
150
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
151
+ items.first.method.first.name.should == 'foo'
152
+ items.first.method.first.arguments.first.should be_a(Hash)
153
+ items.first.method.first.arguments.first.should == { :bar => FlavourSaver::StringNode.new('baz') }
154
+ end
155
+ end
156
+
157
+ describe '{{foo bar="baz" fred="wilma"}}' do
158
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar="baz" fred="wilma"}}')) }
159
+
160
+ it 'calls the method "foo" with the hash {:bar => "baz", :fred => "wilma"} as arguments' do
161
+ items.first.method.first.should be_a(FlavourSaver::CallNode)
162
+ items.first.method.first.name.should == 'foo'
163
+ items.first.method.first.arguments.first.should be_a(Hash)
164
+ items.first.method.first.arguments.first.should == { :bar => FlavourSaver::StringNode.new('baz'), :fred => FlavourSaver::StringNode.new('wilma') }
165
+ end
166
+ end
167
+
168
+ describe '{{foo bar="baz" fred "wilma"}}' do
169
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar="baz" fred "wilma"}}')) }
170
+
171
+ it 'raises an exception' do
172
+ -> { subject }.should raise_exception(RLTK::NotInLanguage)
173
+ end
174
+ end
175
+
176
+ describe '{{{foo}}}' do
177
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{{foo}}}')) }
178
+
179
+ it 'returns a safe expression node' do
180
+ items.first.should be_a(FlavourSaver::SafeExpressionNode)
181
+ end
182
+ end
183
+
184
+ describe '{{../foo}}' do
185
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{../foo}}')) }
186
+
187
+ it 'returns a parent call node' do
188
+ items.first.method.first.should be_a(FlavourSaver::ParentCallNode)
189
+ end
190
+ end
191
+
192
+ describe '{{! comment}}' do
193
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{! comment}}')) }
194
+
195
+ it 'returns a comment node' do
196
+ items.first.should be_a(FlavourSaver::CommentNode)
197
+ end
198
+ end
199
+
200
+ describe '{{#foo}}hello{{/foo}}' do
201
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{#foo}}hello{{/foo}}')) }
202
+
203
+ it 'has a block start and end' do
204
+ items.map(&:class).should == [ FlavourSaver::BlockExpressionNode ]
205
+ end
206
+
207
+ describe '#contents' do
208
+ it 'contains a single output node' do
209
+ items.first.contents.items.size.should == 1
210
+ items.first.contents.items.first.should be_a(FlavourSaver::OutputNode)
211
+ items.first.contents.items.first.value.should == 'hello'
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '{{/foo}}' do
217
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{/foo}}')) }
218
+
219
+ it 'raises a syntax error' do
220
+ -> { subject }.should raise_error
221
+ end
222
+ end
223
+
224
+ describe '{{#foo}}' do
225
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{#foo}}')) }
226
+
227
+ it 'raises a syntax error' do
228
+ -> { subject }.should raise_error
229
+ end
230
+ end
231
+
232
+ describe "{{foo}}\n" do
233
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo}}\n")) }
234
+
235
+ it 'has a block start and end' do
236
+ items.map(&:class).should == [ FlavourSaver::ExpressionNode, FlavourSaver::OutputNode ]
237
+ end
238
+ end
239
+
240
+ describe '{{#foo}}{{#bar}}{{/foo}}' do
241
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{#foo}}{{#bar}}{{/foo}}')) }
242
+
243
+ it 'raises a syntax error' do
244
+ -> { subject }.should raise_error
245
+ end
246
+ end
247
+
248
+ describe "{{#foo}}\n{{/foo}}" do
249
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo}}\n{{/foo}}")) }
250
+
251
+ it "doesn't throw a NotInLanguage exception" do
252
+ -> { subject }.should_not raise_error
253
+ end
254
+ end
255
+
256
+ describe "{{#foo}}{#foo}}{{/foo}}{{/foo}}" do
257
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{#foo}}{{#foo}}{{/foo}}{{/foo}}')) }
258
+
259
+ describe 'the outer block' do
260
+ let(:block) { subject.items.first }
261
+
262
+ it 'should contain another block' do
263
+ block.contents.items.size.should == 1
264
+ block.contents.items.first.should be_a(FlavourSaver::BlockExpressionNode)
265
+ end
266
+ end
267
+ end
268
+
269
+ describe '' do
270
+ subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('')) }
271
+
272
+ it 'returns an empty template' do
273
+ items.should be_empty
274
+ end
275
+ end
276
+
277
+ end