flavour_saver 0.3.3

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