flavour_saver 4.0.2 → 5.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +14 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +48 -5
- data/README.md +153 -271
- data/flavour_saver.gemspec +17 -19
- data/lib/flavour_saver/error.rb +5 -0
- data/lib/flavour_saver/helpers.rb +19 -18
- data/lib/flavour_saver/lexer.rb +123 -50
- data/lib/flavour_saver/nodes.rb +107 -19
- data/lib/flavour_saver/parser.rb +339 -161
- data/lib/flavour_saver/partial.rb +4 -6
- data/lib/flavour_saver/rails_partial.rb +10 -4
- data/lib/flavour_saver/runtime.rb +76 -81
- data/lib/flavour_saver/template.rb +3 -5
- data/lib/flavour_saver/version.rb +1 -1
- data/lib/flavour_saver.rb +17 -17
- data/spec/acceptance/backtrack_spec.rb +9 -9
- data/spec/acceptance/comment_spec.rb +7 -7
- data/spec/acceptance/custom_block_helper_spec.rb +12 -12
- data/spec/acceptance/custom_helper_spec.rb +8 -8
- data/spec/acceptance/ensure_no_rce_spec.rb +64 -53
- data/spec/acceptance/handlebars_qunit_spec.rb +295 -297
- data/spec/acceptance/if_else_spec.rb +6 -6
- data/spec/acceptance/multi_level_with_spec.rb +10 -11
- data/spec/acceptance/nesting_depth_spec.rb +34 -0
- data/spec/acceptance/one_character_identifier_spec.rb +8 -8
- data/spec/acceptance/raw_block_spec.rb +7 -7
- data/spec/acceptance/sections_spec.rb +17 -14
- data/spec/acceptance/segment_literals_spec.rb +5 -5
- data/spec/acceptance/simple_expression_spec.rb +9 -9
- data/spec/acceptance/subexpression_spec.rb +18 -17
- data/spec/acceptance/unless_spec.rb +19 -6
- data/spec/lib/flavour_saver/lexer_spec.rb +103 -89
- data/spec/lib/flavour_saver/nodes_spec.rb +85 -0
- data/spec/lib/flavour_saver/parser_spec.rb +155 -95
- data/spec/lib/flavour_saver/runtime_spec.rb +94 -96
- data/spec/lib/flavour_saver/template_spec.rb +1 -2
- metadata +19 -48
|
@@ -1,104 +1,100 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "flavour_saver/parser"
|
|
2
|
+
require "flavour_saver/lexer"
|
|
3
3
|
|
|
4
4
|
describe FlavourSaver::Parser do
|
|
5
|
-
let
|
|
5
|
+
let(:items) { subject.items }
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
describe 'HTML template' do
|
|
12
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('<html><h1>Hello world!</h1></html>')) }
|
|
7
|
+
describe "HTML template" do
|
|
8
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("<html><h1>Hello world!</h1></html>")) }
|
|
13
9
|
|
|
14
|
-
it
|
|
10
|
+
it "is an output node" do
|
|
15
11
|
expect(items.first).to be_a(FlavourSaver::OutputNode)
|
|
16
12
|
end
|
|
17
13
|
|
|
18
|
-
it
|
|
19
|
-
expect(items.first.value).to eq
|
|
14
|
+
it "has the correct contents" do
|
|
15
|
+
expect(items.first.value).to eq "<html><h1>Hello world!</h1></html>"
|
|
20
16
|
end
|
|
21
17
|
end
|
|
22
18
|
|
|
23
|
-
describe
|
|
24
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
19
|
+
describe "HTML template containing a handlebars expression" do
|
|
20
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("<html>{{foo}}</html>")) }
|
|
25
21
|
|
|
26
|
-
it
|
|
22
|
+
it "has template output either side of the expression" do
|
|
27
23
|
expect(items.map(&:class)).to eq [FlavourSaver::OutputNode, FlavourSaver::ExpressionNode, FlavourSaver::OutputNode]
|
|
28
24
|
end
|
|
29
25
|
end
|
|
30
26
|
|
|
31
|
-
describe
|
|
32
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
27
|
+
describe "{{foo}}" do
|
|
28
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo}}")) }
|
|
33
29
|
|
|
34
|
-
it
|
|
30
|
+
it "contains an expression node" do
|
|
35
31
|
expect(items.first).to be_a(FlavourSaver::ExpressionNode)
|
|
36
32
|
end
|
|
37
33
|
|
|
38
34
|
it 'calls the method "foo" with no arguments' do
|
|
39
35
|
expect(items.first.method).to be_one
|
|
40
36
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
41
|
-
expect(items.first.method.first.name).to eq
|
|
37
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
42
38
|
expect(items.first.method.first.arguments).to be_empty
|
|
43
39
|
end
|
|
44
40
|
end
|
|
45
41
|
|
|
46
|
-
describe
|
|
47
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
42
|
+
describe "{{foo.bar}}" do
|
|
43
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo.bar}}")) }
|
|
48
44
|
|
|
49
|
-
it
|
|
45
|
+
it "calls two methods" do
|
|
50
46
|
expect(items.first.method.size).to eq 2
|
|
51
47
|
end
|
|
52
48
|
|
|
53
49
|
it 'calls the method "foo" with no arguments first' do
|
|
54
50
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
55
|
-
expect(items.first.method.first.name).to eq
|
|
51
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
56
52
|
expect(items.first.method.first.arguments).to be_empty
|
|
57
53
|
end
|
|
58
54
|
|
|
59
55
|
it 'calls the method "bar" with no arguments second' do
|
|
60
56
|
expect(items.first.method[1]).to be_a(FlavourSaver::CallNode)
|
|
61
|
-
expect(items.first.method[1].name).to eq
|
|
57
|
+
expect(items.first.method[1].name).to eq "bar"
|
|
62
58
|
expect(items.first.method[1].arguments).to be_empty
|
|
63
59
|
end
|
|
64
60
|
end
|
|
65
61
|
|
|
66
|
-
describe
|
|
67
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
62
|
+
describe "{{foo.[&@^$*].bar}}" do
|
|
63
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo.[&@^$*].bar}}")) }
|
|
68
64
|
|
|
69
|
-
it
|
|
65
|
+
it "calls three methods" do
|
|
70
66
|
expect(items.first.method.size).to eq 3
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
it 'calls the method "foo" with no arguments first' do
|
|
74
70
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
75
|
-
expect(items.first.method.first.name).to eq
|
|
71
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
76
72
|
expect(items.first.method.first.arguments).to be_empty
|
|
77
73
|
end
|
|
78
74
|
|
|
79
75
|
it 'calls the method "&@^$*" with no arguments second' do
|
|
80
76
|
expect(items.first.method[1]).to be_a(FlavourSaver::CallNode)
|
|
81
|
-
expect(items.first.method[1].name).to eq
|
|
77
|
+
expect(items.first.method[1].name).to eq "&@^$*"
|
|
82
78
|
expect(items.first.method[1].arguments).to be_empty
|
|
83
79
|
end
|
|
84
80
|
|
|
85
81
|
it 'calls the method "bar" with no arguments third' do
|
|
86
82
|
expect(items.first.method[2]).to be_a(FlavourSaver::CallNode)
|
|
87
|
-
expect(items.first.method[2].name).to eq
|
|
83
|
+
expect(items.first.method[2].name).to eq "bar"
|
|
88
84
|
expect(items.first.method[2].arguments).to be_empty
|
|
89
85
|
end
|
|
90
86
|
end
|
|
91
87
|
|
|
92
|
-
describe
|
|
93
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
88
|
+
describe "{{foo bar}}" do
|
|
89
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo bar}}")) }
|
|
94
90
|
|
|
95
91
|
it 'calls the method "foo" with a method argument of "bar"' do
|
|
96
92
|
expect(items.first.method).to be_one
|
|
97
93
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
98
|
-
expect(items.first.method.first.name).to eq
|
|
94
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
99
95
|
expect(items.first.method.first.arguments).to be_one
|
|
100
96
|
expect(items.first.method.first.arguments.first.first).to be_a(FlavourSaver::CallNode)
|
|
101
|
-
expect(items.first.method.first.arguments.first.first.name).to eq
|
|
97
|
+
expect(items.first.method.first.arguments.first.first.name).to eq "bar"
|
|
102
98
|
end
|
|
103
99
|
end
|
|
104
100
|
|
|
@@ -108,10 +104,10 @@ describe FlavourSaver::Parser do
|
|
|
108
104
|
it 'calls the method "foo" with a string argument of "bar"' do
|
|
109
105
|
expect(items.first.method).to be_one
|
|
110
106
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
111
|
-
expect(items.first.method.first.name).to eq
|
|
107
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
112
108
|
expect(items.first.method.first.arguments).to be_one
|
|
113
109
|
expect(items.first.method.first.arguments.first).to be_a(FlavourSaver::StringNode)
|
|
114
|
-
expect(items.first.method.first.arguments.first.value).to eq
|
|
110
|
+
expect(items.first.method.first.arguments.first.value).to eq "bar"
|
|
115
111
|
end
|
|
116
112
|
end
|
|
117
113
|
|
|
@@ -121,23 +117,23 @@ describe FlavourSaver::Parser do
|
|
|
121
117
|
it 'calls the method "foo"' do
|
|
122
118
|
expect(items.first.method).to be_one
|
|
123
119
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
124
|
-
expect(items.first.method.first.name).to eq
|
|
120
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
125
121
|
end
|
|
126
122
|
|
|
127
|
-
describe
|
|
123
|
+
describe "with arguments" do
|
|
128
124
|
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar "baz" }}')).items.first.method.first.arguments }
|
|
129
125
|
|
|
130
|
-
describe
|
|
126
|
+
describe "[0]" do
|
|
131
127
|
it 'is the method call "bar" with no arguments' do
|
|
132
128
|
expect(subject.first.first).to be_a(FlavourSaver::CallNode)
|
|
133
|
-
expect(subject.first.first.name).to eq
|
|
129
|
+
expect(subject.first.first.name).to eq "bar"
|
|
134
130
|
end
|
|
135
131
|
end
|
|
136
132
|
|
|
137
|
-
describe
|
|
133
|
+
describe "[1]" do
|
|
138
134
|
it 'is the string "baz"' do
|
|
139
135
|
expect(subject[1]).to be_a(FlavourSaver::StringNode)
|
|
140
|
-
expect(subject[1].value).to eq
|
|
136
|
+
expect(subject[1].value).to eq "baz"
|
|
141
137
|
end
|
|
142
138
|
end
|
|
143
139
|
end
|
|
@@ -149,19 +145,18 @@ describe FlavourSaver::Parser do
|
|
|
149
145
|
it 'calls the method "foo"' do
|
|
150
146
|
expect(items.first.method).to be_one
|
|
151
147
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
152
|
-
expect(items.first.method.first.name).to eq
|
|
148
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
153
149
|
end
|
|
154
150
|
|
|
155
|
-
describe
|
|
151
|
+
describe "with arguments" do
|
|
156
152
|
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo (bar "baz") }}')).items.first.method.first.arguments }
|
|
157
153
|
|
|
158
|
-
describe
|
|
159
|
-
it
|
|
154
|
+
describe "[0]" do
|
|
155
|
+
it "is a subexpression" do
|
|
160
156
|
expect(subject.first.first).to be_a(FlavourSaver::CallNode)
|
|
161
|
-
expect(subject.first.first.name).to eq
|
|
157
|
+
expect(subject.first.first.name).to eq "bar"
|
|
162
158
|
end
|
|
163
159
|
end
|
|
164
|
-
|
|
165
160
|
end
|
|
166
161
|
end
|
|
167
162
|
|
|
@@ -170,9 +165,9 @@ describe FlavourSaver::Parser do
|
|
|
170
165
|
|
|
171
166
|
it 'calls the method "foo" with the hash {:bar => "baz"} as arguments' do
|
|
172
167
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
173
|
-
expect(items.first.method.first.name).to eq
|
|
168
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
174
169
|
expect(items.first.method.first.arguments.first).to be_a(Hash)
|
|
175
|
-
expect(items.first.method.first.arguments.first).to eq({
|
|
170
|
+
expect(items.first.method.first.arguments.first).to eq({bar: FlavourSaver::StringNode.new("baz")})
|
|
176
171
|
end
|
|
177
172
|
end
|
|
178
173
|
|
|
@@ -181,7 +176,7 @@ describe FlavourSaver::Parser do
|
|
|
181
176
|
|
|
182
177
|
it 'calls the method "foo" with the hash {:bar => (baz "qux")} as arguments' do
|
|
183
178
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
184
|
-
expect(items.first.method.first.name).to eq
|
|
179
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
185
180
|
expect(items.first.method.first.arguments.first).to be_a(Hash)
|
|
186
181
|
expect(items.first.method.first.arguments.first[:bar].first).to be_a(FlavourSaver::CallNode)
|
|
187
182
|
end
|
|
@@ -196,9 +191,9 @@ describe FlavourSaver::Parser do
|
|
|
196
191
|
|
|
197
192
|
it 'calls the method "foo" with the hash {:bar => 1} as arguments' do
|
|
198
193
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
199
|
-
expect(items.first.method.first.name).to eq
|
|
194
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
200
195
|
expect(items.first.method.first.arguments.first).to be_a(Hash)
|
|
201
|
-
expect(items.first.method.first.arguments.first).to eq({
|
|
196
|
+
expect(items.first.method.first.arguments.first).to eq({bar: FlavourSaver::NumberNode.new("1")})
|
|
202
197
|
end
|
|
203
198
|
end
|
|
204
199
|
|
|
@@ -207,90 +202,98 @@ describe FlavourSaver::Parser do
|
|
|
207
202
|
|
|
208
203
|
it 'calls the method "foo" with the hash {:bar => "baz", :fred => "wilma"} as arguments' do
|
|
209
204
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
210
|
-
expect(items.first.method.first.name).to eq
|
|
205
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
211
206
|
expect(items.first.method.first.arguments.first).to be_a(Hash)
|
|
212
|
-
expect(items.first.method.first.arguments.first).to eq({
|
|
207
|
+
expect(items.first.method.first.arguments.first).to eq({bar: FlavourSaver::StringNode.new("baz"), fred: FlavourSaver::StringNode.new("wilma")})
|
|
213
208
|
end
|
|
214
209
|
end
|
|
215
210
|
|
|
216
211
|
describe '{{foo bar="baz" fred "wilma"}}' do
|
|
217
212
|
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo bar="baz" fred "wilma"}}')) }
|
|
218
213
|
|
|
219
|
-
it
|
|
220
|
-
expect { subject }.to raise_exception(
|
|
214
|
+
it "raises an exception" do
|
|
215
|
+
expect { subject }.to raise_exception(FlavourSaver::Parser::NotInLanguage)
|
|
221
216
|
end
|
|
222
217
|
end
|
|
223
218
|
|
|
224
|
-
describe
|
|
225
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
219
|
+
describe "{{{foo}}}" do
|
|
220
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{{foo}}}")) }
|
|
226
221
|
|
|
227
|
-
it
|
|
222
|
+
it "returns a safe expression node" do
|
|
228
223
|
expect(items.first).to be_a(FlavourSaver::SafeExpressionNode)
|
|
229
224
|
end
|
|
230
225
|
end
|
|
231
226
|
|
|
232
|
-
describe
|
|
233
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
227
|
+
describe "{{../foo}}" do
|
|
228
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{../foo}}")) }
|
|
234
229
|
|
|
235
|
-
it
|
|
230
|
+
it "returns a parent call node" do
|
|
236
231
|
expect(items.first.method.first).to be_a(FlavourSaver::ParentCallNode)
|
|
237
232
|
end
|
|
238
233
|
end
|
|
239
234
|
|
|
240
|
-
describe
|
|
241
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
235
|
+
describe "{{! comment}}" do
|
|
236
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{! comment}}")) }
|
|
242
237
|
|
|
243
|
-
it
|
|
238
|
+
it "returns a comment node" do
|
|
244
239
|
expect(items.first).to be_a(FlavourSaver::CommentNode)
|
|
245
240
|
end
|
|
246
241
|
end
|
|
247
242
|
|
|
248
|
-
describe
|
|
249
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
243
|
+
describe "{{#foo}}hello{{/foo}}" do
|
|
244
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo}}hello{{/foo}}")) }
|
|
250
245
|
|
|
251
|
-
it
|
|
252
|
-
expect(items.map(&:class)).to eq [
|
|
246
|
+
it "has a block start and end" do
|
|
247
|
+
expect(items.map(&:class)).to eq [FlavourSaver::BlockExpressionNode]
|
|
253
248
|
end
|
|
254
249
|
|
|
255
|
-
describe
|
|
256
|
-
it
|
|
250
|
+
describe "#contents" do
|
|
251
|
+
it "contains a single output node" do
|
|
257
252
|
expect(items.first.contents.items.size).to eq 1
|
|
258
253
|
expect(items.first.contents.items.first).to be_a(FlavourSaver::OutputNode)
|
|
259
|
-
expect(items.first.contents.items.first.value).to eq
|
|
254
|
+
expect(items.first.contents.items.first.value).to eq "hello"
|
|
260
255
|
end
|
|
261
256
|
end
|
|
262
257
|
end
|
|
263
258
|
|
|
264
|
-
describe
|
|
265
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
259
|
+
describe "{{/foo}}" do
|
|
260
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{/foo}}")) }
|
|
261
|
+
|
|
262
|
+
it "raises a syntax error" do
|
|
263
|
+
expect { subject }.to raise_error(FlavourSaver::Parser::NotInLanguage)
|
|
264
|
+
end
|
|
266
265
|
|
|
267
|
-
it
|
|
268
|
-
expect { subject }.to raise_error(
|
|
266
|
+
it "is a FlavourSaver::Error" do
|
|
267
|
+
expect { subject }.to raise_error(FlavourSaver::Error)
|
|
269
268
|
end
|
|
270
269
|
end
|
|
271
270
|
|
|
272
|
-
describe
|
|
273
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
271
|
+
describe "{{#foo}}" do
|
|
272
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo}}")) }
|
|
274
273
|
|
|
275
|
-
it
|
|
276
|
-
expect { subject }.to raise_error(
|
|
274
|
+
it "raises a syntax error" do
|
|
275
|
+
expect { subject }.to raise_error(FlavourSaver::Parser::NotInLanguage)
|
|
277
276
|
end
|
|
278
277
|
end
|
|
279
278
|
|
|
280
279
|
describe "{{foo}}\n" do
|
|
281
280
|
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{foo}}\n")) }
|
|
282
281
|
|
|
283
|
-
it
|
|
284
|
-
expect(items.map(&:class)).to eq [
|
|
282
|
+
it "has a block start and end" do
|
|
283
|
+
expect(items.map(&:class)).to eq [FlavourSaver::ExpressionNode, FlavourSaver::OutputNode]
|
|
285
284
|
end
|
|
286
285
|
end
|
|
287
286
|
|
|
288
|
-
describe
|
|
289
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
287
|
+
describe "{{#foo}}{{#bar}}{{/foo}}" do
|
|
288
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo}}{{#bar}}{{/foo}}")) }
|
|
290
289
|
|
|
291
|
-
it
|
|
290
|
+
it "raises a syntax error" do
|
|
292
291
|
expect { subject }.to raise_error(FlavourSaver::Parser::UnbalancedBlockError)
|
|
293
292
|
end
|
|
293
|
+
|
|
294
|
+
it "is a FlavourSaver::Error" do
|
|
295
|
+
expect { subject }.to raise_error(FlavourSaver::Error)
|
|
296
|
+
end
|
|
294
297
|
end
|
|
295
298
|
|
|
296
299
|
describe "{{#foo}}\n{{/foo}}" do
|
|
@@ -302,12 +305,12 @@ describe FlavourSaver::Parser do
|
|
|
302
305
|
end
|
|
303
306
|
|
|
304
307
|
describe "{{#foo}}{#foo}}{{/foo}}{{/foo}}" do
|
|
305
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
308
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo}}{{#foo}}{{/foo}}{{/foo}}")) }
|
|
306
309
|
|
|
307
|
-
describe
|
|
310
|
+
describe "the outer block" do
|
|
308
311
|
let(:block) { subject.items.first }
|
|
309
312
|
|
|
310
|
-
it
|
|
313
|
+
it "should contain another block" do
|
|
311
314
|
expect(block.contents.items.size).to eq 1
|
|
312
315
|
expect(block.contents.items.first).to be_a(FlavourSaver::BlockExpressionNode)
|
|
313
316
|
end
|
|
@@ -329,10 +332,10 @@ describe FlavourSaver::Parser do
|
|
|
329
332
|
expect { subject }.to_not raise_error
|
|
330
333
|
end
|
|
331
334
|
end
|
|
332
|
-
describe
|
|
333
|
-
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(
|
|
335
|
+
describe "" do
|
|
336
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("")) }
|
|
334
337
|
|
|
335
|
-
it
|
|
338
|
+
it "returns an empty template" do
|
|
336
339
|
expect(items).to be_empty
|
|
337
340
|
end
|
|
338
341
|
end
|
|
@@ -340,11 +343,68 @@ describe FlavourSaver::Parser do
|
|
|
340
343
|
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex('{{foo "bar" fred="wilma"}}')) }
|
|
341
344
|
it 'calls the method "foo" with the "bar" and {:fred => "wilma"} as arguments' do
|
|
342
345
|
expect(items.first.method.first).to be_a(FlavourSaver::CallNode)
|
|
343
|
-
expect(items.first.method.first.name).to eq
|
|
346
|
+
expect(items.first.method.first.name).to eq "foo"
|
|
344
347
|
expect(items.first.method.first.arguments.first).to be_a(FlavourSaver::StringNode)
|
|
345
|
-
expect(items.first.method.first.arguments.first.value).to eq
|
|
348
|
+
expect(items.first.method.first.arguments.first.value).to eq "bar"
|
|
346
349
|
expect(items.first.method.first.arguments.last).to be_a(Hash)
|
|
347
|
-
expect(items.first.method.first.arguments.last).to eq({
|
|
350
|
+
expect(items.first.method.first.arguments.last).to eq({fred: FlavourSaver::StringNode.new("wilma")})
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
describe "{{^foo}}falsy{{else}}truthy{{/foo}}" do
|
|
354
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{^foo}}falsy{{else}}truthy{{/foo}}")) }
|
|
355
|
+
|
|
356
|
+
it "renders the {{else}} section when foo is truthy and the body when it is falsy" do
|
|
357
|
+
expect(items.first).to be_a(FlavourSaver::BlockExpressionNodeWithElse)
|
|
358
|
+
expect(items.first.contents.items.map(&:value)).to eq ["truthy"]
|
|
359
|
+
expect(items.first.alternate.items.map(&:value)).to eq ["falsy"]
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
describe "{{#foo bar}}{{/foo}}" do
|
|
364
|
+
subject { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex("{{#foo bar}}{{/foo}}")) }
|
|
365
|
+
|
|
366
|
+
it "links each call to the node that contains it" do
|
|
367
|
+
expect(items.first.method.first.parent).to eq items.first
|
|
368
|
+
expect(items.first.method.first.arguments.first.first.parent).to be_nil
|
|
369
|
+
expect(items.first.parent).to eq subject
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
describe "nesting depth" do
|
|
374
|
+
let(:max) { FlavourSaver::Parser::MAX_DEPTH }
|
|
375
|
+
let(:parse) { ->(template) { FlavourSaver::Parser.parse(FlavourSaver::Lexer.lex(template)) } }
|
|
376
|
+
|
|
377
|
+
def blocks(depth)
|
|
378
|
+
("{{#foo}}" * depth) + ("{{/foo}}" * depth)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def subexpressions(depth)
|
|
382
|
+
"{{foo #{"(foo " * depth}bar#{")" * depth}}}"
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it "allows blocks nested up to the limit" do
|
|
386
|
+
expect { parse.call(blocks(max)) }.not_to raise_error
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
it "raises NotInLanguage for blocks nested past the limit" do
|
|
390
|
+
expect { parse.call(blocks(max + 1)) }.to raise_error(FlavourSaver::Parser::NotInLanguage, /more than #{max} levels/)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
it "allows subexpressions nested up to the limit" do
|
|
394
|
+
expect { parse.call(subexpressions(max)) }.not_to raise_error
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it "raises NotInLanguage for subexpressions nested past the limit" do
|
|
398
|
+
expect { parse.call(subexpressions(max + 1)) }.to raise_error(FlavourSaver::Parser::NotInLanguage, /more than #{max} levels/)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it "counts blocks and subexpressions together" do
|
|
402
|
+
template = ("{{#foo}}" * max) + "{{bar (baz)}}" + ("{{/foo}}" * max)
|
|
403
|
+
expect { parse.call(template) }.to raise_error(FlavourSaver::Parser::NotInLanguage, /more than #{max} levels/)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "raises NotInLanguage rather than SystemStackError inside a Fiber" do
|
|
407
|
+
expect { Fiber.new { parse.call(blocks(10_000)) }.resume }.to raise_error(FlavourSaver::Parser::NotInLanguage)
|
|
348
408
|
end
|
|
349
409
|
end
|
|
350
410
|
end
|