curlybars 1.3.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/curlybars.rb +8 -1
  3. data/lib/curlybars/configuration.rb +1 -9
  4. data/lib/curlybars/error/base.rb +2 -0
  5. data/lib/curlybars/generic.rb +36 -0
  6. data/lib/curlybars/lexer.rb +3 -0
  7. data/lib/curlybars/method_whitelist.rb +25 -3
  8. data/lib/curlybars/node/block_helper_else.rb +1 -0
  9. data/lib/curlybars/node/each_else.rb +6 -2
  10. data/lib/curlybars/node/if_else.rb +1 -1
  11. data/lib/curlybars/node/path.rb +58 -11
  12. data/lib/curlybars/node/sub_expression.rb +108 -0
  13. data/lib/curlybars/node/unless_else.rb +1 -1
  14. data/lib/curlybars/node/with_else.rb +6 -2
  15. data/lib/curlybars/parser.rb +39 -0
  16. data/lib/curlybars/processor/tilde.rb +3 -0
  17. data/lib/curlybars/rendering_support.rb +9 -1
  18. data/lib/curlybars/template_handler.rb +18 -6
  19. data/lib/curlybars/version.rb +1 -1
  20. data/lib/curlybars/visitor.rb +6 -0
  21. data/spec/acceptance/application_layout_spec.rb +2 -2
  22. data/spec/acceptance/collection_blocks_spec.rb +1 -1
  23. data/spec/acceptance/global_helper_spec.rb +1 -1
  24. data/spec/curlybars/lexer_spec.rb +25 -2
  25. data/spec/curlybars/method_whitelist_spec.rb +8 -4
  26. data/spec/curlybars/rendering_support_spec.rb +4 -9
  27. data/spec/curlybars/template_handler_spec.rb +33 -30
  28. data/spec/integration/cache_spec.rb +20 -18
  29. data/spec/integration/node/block_helper_else_spec.rb +0 -2
  30. data/spec/integration/node/each_else_spec.rb +208 -4
  31. data/spec/integration/node/each_spec.rb +0 -2
  32. data/spec/integration/node/helper_spec.rb +12 -2
  33. data/spec/integration/node/if_else_spec.rb +0 -2
  34. data/spec/integration/node/if_spec.rb +2 -4
  35. data/spec/integration/node/output_spec.rb +0 -2
  36. data/spec/integration/node/partial_spec.rb +0 -2
  37. data/spec/integration/node/path_spec.rb +0 -2
  38. data/spec/integration/node/root_spec.rb +0 -2
  39. data/spec/integration/node/sub_expression_spec.rb +426 -0
  40. data/spec/integration/node/template_spec.rb +0 -2
  41. data/spec/integration/node/unless_else_spec.rb +2 -4
  42. data/spec/integration/node/unless_spec.rb +0 -2
  43. data/spec/integration/node/with_spec.rb +66 -4
  44. data/spec/integration/processor/tilde_spec.rb +1 -1
  45. data/spec/integration/processors_spec.rb +4 -5
  46. data/spec/integration/visitor_spec.rb +13 -5
  47. metadata +49 -15
@@ -1,29 +1,31 @@
1
1
  describe "caching" do
2
- class DummyCache
3
- attr_reader :reads, :hits
4
-
5
- def initialize
6
- @store = {}
7
- @reads = 0
8
- @hits = 0
9
- end
2
+ let(:dummy_cache) do
3
+ Class.new do
4
+ attr_reader :reads, :hits
5
+
6
+ def initialize
7
+ @store = {}
8
+ @reads = 0
9
+ @hits = 0
10
+ end
10
11
 
11
- def fetch(key)
12
- @reads += 1
13
- if @store.key?(key)
14
- @hits += 1
15
- @store[key]
16
- else
17
- value = yield
18
- @store[key] = value
19
- value
12
+ def fetch(key)
13
+ @reads += 1
14
+ if @store.key?(key)
15
+ @hits += 1
16
+ @store[key]
17
+ else
18
+ value = yield
19
+ @store[key] = value
20
+ value
21
+ end
20
22
  end
21
23
  end
22
24
  end
23
25
 
24
26
  let(:global_helpers_providers) { [] }
25
27
  let(:presenter) { IntegrationTest::Presenter.new(double("view_context")) }
26
- let(:cache) { DummyCache.new }
28
+ let(:cache) { dummy_cache.new }
27
29
 
28
30
  before do
29
31
  Curlybars.configure do |config|
@@ -230,8 +230,6 @@ describe "{{#helper arg1 arg2 ... key=value ...}}...<{{else}}>...{{/helper}}" do
230
230
  end
231
231
 
232
232
  describe "#validate" do
233
- let(:presenter_class) { double(:presenter_class) }
234
-
235
233
  it "without errors when global helper" do
236
234
  allow(Curlybars.configuration).to receive(:global_helpers_provider_classes).and_return([IntegrationTest::GlobalHelperProvider])
237
235
 
@@ -1,9 +1,10 @@
1
1
  describe "{{#each collection}}...{{else}}...{{/each}}" do
2
- let(:global_helpers_providers) { [] }
2
+ let(:global_helpers_providers) { [IntegrationTest::GlobalHelperProvider.new] }
3
3
 
4
4
  describe "#compile" do
5
5
  let(:post) { double("post") }
6
- let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
6
+ let(:presenter_class) { IntegrationTest::Presenter }
7
+ let(:presenter) { presenter_class.new(double("view_context"), post: post) }
7
8
 
8
9
  it "uses each_template when collection is not empty" do
9
10
  allow(presenter).to receive(:allows_method?).with(:non_empty_collection).and_return(true)
@@ -123,6 +124,41 @@ describe "{{#each collection}}...{{else}}...{{/each}}" do
123
124
  HTML
124
125
  end
125
126
 
127
+ it "allows subexpressions" do
128
+ template = Curlybars.compile(<<-HBS)
129
+ {{#each (articles)}}left{{else}}right{{/each}}
130
+ HBS
131
+
132
+ expected = "left" * presenter.articles.size
133
+ expect(eval(template)).to resemble(expected)
134
+ end
135
+
136
+ it "allows subexpressions with collection helpers" do
137
+ template = Curlybars.compile(<<-HBS)
138
+ {{#each (reverse_articles)}}
139
+ {{title}}
140
+ {{else}}
141
+ right
142
+ {{/each}}
143
+ HBS
144
+
145
+ expected = presenter.reverse_articles.inject("") { |res, a| res + a.title }
146
+ expect(eval(template)).to resemble(expected)
147
+ end
148
+
149
+ it "allows subexpressions with generic collection helpers" do
150
+ template = Curlybars.compile(<<-HBS)
151
+ {{#each (refl articles)}}
152
+ {{title}}
153
+ {{else}}
154
+ right
155
+ {{/each}}
156
+ HBS
157
+
158
+ expected = presenter.articles.inject("") { |res, a| res + a.title }
159
+ expect(eval(template)).to resemble(expected)
160
+ end
161
+
126
162
  it "raises an error if the context is not an array-like object" do
127
163
  allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:not_a_collection).and_return(true)
128
164
  allow(presenter).to receive(:not_a_collection).and_return("string")
@@ -151,8 +187,6 @@ describe "{{#each collection}}...{{else}}...{{/each}}" do
151
187
  end
152
188
 
153
189
  describe "#validate" do
154
- let(:presenter_class) { double(:presenter_class) }
155
-
156
190
  it "without errors" do
157
191
  dependency_tree = { a_presenter_collection: [{}] }
158
192
 
@@ -200,5 +234,175 @@ describe "{{#each collection}}...{{else}}...{{/each}}" do
200
234
 
201
235
  expect(errors).not_to be_empty
202
236
  end
237
+
238
+ describe "with subexpressions" do
239
+ before do
240
+ Curlybars.instance_variable_set(:@global_helpers_dependency_tree, nil)
241
+ end
242
+
243
+ it "without errors when called with a presenter collection" do
244
+ dependency_tree = { a_presenter_collection: [{}] }
245
+
246
+ source = <<-HBS
247
+ {{#each (a_presenter_collection)}} {{else}} {{/each}}
248
+ HBS
249
+
250
+ errors = Curlybars.validate(dependency_tree, source)
251
+
252
+ expect(errors).to be_empty
253
+ end
254
+
255
+ it "without errors when called with a collection helper" do
256
+ dependency_tree = { a_collection_helper: [:helper, [{ url: nil }]] }
257
+
258
+ source = <<-HBS
259
+ {{#each (a_collection_helper)}}
260
+ {{url}}
261
+ {{else}}
262
+ right
263
+ {{/each}}
264
+ HBS
265
+
266
+ errors = Curlybars.validate(dependency_tree, source)
267
+
268
+ expect(errors).to be_empty
269
+ end
270
+
271
+ context "with a generic collection helper" do
272
+ it "without errors when arguments are valid" do
273
+ dependency_tree = {
274
+ refl: [:helper, [{}]],
275
+ articles: [{ url: nil }]
276
+ }
277
+
278
+ source = <<-HBS
279
+ {{#each (refl articles)}}
280
+ {{url}}
281
+ {{else}}
282
+ right
283
+ {{/each}}
284
+ HBS
285
+
286
+ errors = Curlybars.validate(dependency_tree, source)
287
+
288
+ expect(errors).to be_empty
289
+ end
290
+
291
+ it "with errors when the first argument is not a collection" do
292
+ dependency_tree = {
293
+ refl: [:helper, [{}]],
294
+ articles: { url: nil }
295
+ }
296
+
297
+ source = <<-HBS
298
+ {{#each (refl articles)}}
299
+ {{url}}
300
+ {{else}}
301
+ right
302
+ {{/each}}
303
+ HBS
304
+
305
+ errors = Curlybars.validate(dependency_tree, source)
306
+
307
+ expect(errors).not_to be_empty
308
+ end
309
+
310
+ it "with errors when the first argument is not defined" do
311
+ dependency_tree = {
312
+ refl: [:helper, [{}]]
313
+ }
314
+
315
+ source = <<-HBS
316
+ {{#each (refl articles)}}
317
+ {{url}}
318
+ {{else}}
319
+ right
320
+ {{/each}}
321
+ HBS
322
+
323
+ errors = Curlybars.validate(dependency_tree, source)
324
+
325
+ expect(errors).not_to be_empty
326
+ end
327
+
328
+ it "with errors when no argument is given" do
329
+ dependency_tree = {
330
+ refl: [:helper, [{}]]
331
+ }
332
+
333
+ source = <<-HBS
334
+ {{#each (refl)}}
335
+ {{url}}
336
+ {{else}}
337
+ right
338
+ {{/each}}
339
+ HBS
340
+
341
+ errors = Curlybars.validate(dependency_tree, source)
342
+
343
+ expect(errors).not_to be_empty
344
+ end
345
+
346
+ describe "as a global helper" do
347
+ let(:global_helpers_provider_classes) { [IntegrationTest::GlobalHelperProvider] }
348
+
349
+ before do
350
+ allow(Curlybars.configuration).to receive(:global_helpers_provider_classes).and_return(global_helpers_provider_classes)
351
+ end
352
+
353
+ it "without errors when the first argument is a collection" do
354
+ dependency_tree = {
355
+ articles: [{ url: nil }]
356
+ }
357
+
358
+ source = <<-HBS
359
+ {{#each (slice articles 0 4)}}
360
+ {{url}}
361
+ {{else}}
362
+ right
363
+ {{/each}}
364
+ HBS
365
+
366
+ errors = Curlybars.validate(dependency_tree, source)
367
+
368
+ expect(errors).to be_empty
369
+ end
370
+
371
+ it "with errors when the first argument is not a collection" do
372
+ dependency_tree = {
373
+ articles: { url: nil }
374
+ }
375
+
376
+ source = <<-HBS
377
+ {{#each (slice articles 0 4)}}
378
+ {{url}}
379
+ {{else}}
380
+ right
381
+ {{/each}}
382
+ HBS
383
+
384
+ errors = Curlybars.validate(dependency_tree, source)
385
+
386
+ expect(errors).not_to be_empty
387
+ end
388
+
389
+ it "with errors when no argument is given" do
390
+ dependency_tree = {}
391
+
392
+ source = <<-HBS
393
+ {{#each (slice)}}
394
+ {{url}}
395
+ {{else}}
396
+ right
397
+ {{/each}}
398
+ HBS
399
+
400
+ errors = Curlybars.validate(dependency_tree, source)
401
+
402
+ expect(errors).not_to be_empty
403
+ end
404
+ end
405
+ end
406
+ end
203
407
  end
204
408
  end
@@ -238,8 +238,6 @@ describe "{{#each collection}}...{{/each}}" do
238
238
  end
239
239
 
240
240
  describe "#validate" do
241
- let(:presenter_class) { double(:presenter_class) }
242
-
243
241
  it "without errors" do
244
242
  dependency_tree = { a_presenter_collection: [{}] }
245
243
 
@@ -15,6 +15,18 @@ describe "{{helper context key=value}}" do
15
15
  HTML
16
16
  end
17
17
 
18
+ it "calls a helper without arguments in an if statement" do
19
+ template = Curlybars.compile(<<-HBS)
20
+ {{#if print_args_and_options}}
21
+ {{print_args_and_options 'first' 'second'}}
22
+ {{/if}}
23
+ HBS
24
+
25
+ expect(eval(template)).to resemble(<<-HTML)
26
+ first, second, key=
27
+ HTML
28
+ end
29
+
18
30
  it "passes two arguments and options" do
19
31
  template = Curlybars.compile(<<-HBS)
20
32
  {{print_args_and_options 'first' 'second' key='value'}}
@@ -109,8 +121,6 @@ describe "{{helper context key=value}}" do
109
121
  end
110
122
 
111
123
  describe "#validate" do
112
- let(:presenter_class) { double(:presenter_class) }
113
-
114
124
  it "with errors" do
115
125
  dependency_tree = {}
116
126
 
@@ -82,8 +82,6 @@ describe "{{#if}}...{{else}}...{{/if}}" do
82
82
  end
83
83
 
84
84
  describe "#validate" do
85
- let(:presenter_class) { double(:presenter_class) }
86
-
87
85
  it "validates without errors the literal condition" do
88
86
  dependency_tree = {}
89
87
 
@@ -112,8 +112,6 @@ describe "{{#if}}...{{/if}}" do
112
112
  end
113
113
 
114
114
  describe "#validate" do
115
- let(:presenter_class) { double(:presenter_class) }
116
-
117
115
  it "validates with errors the condition" do
118
116
  dependency_tree = {}
119
117
 
@@ -140,7 +138,7 @@ describe "{{#if}}...{{/if}}" do
140
138
  expect(errors).not_to be_empty
141
139
  end
142
140
 
143
- it "validates with errors the helper as condition" do
141
+ it "validates without errors the helper as condition" do
144
142
  dependency_tree = { helper: :helper }
145
143
 
146
144
  source = <<-HBS
@@ -149,7 +147,7 @@ describe "{{#if}}...{{/if}}" do
149
147
 
150
148
  errors = Curlybars.validate(dependency_tree, source)
151
149
 
152
- expect(errors).not_to be_empty
150
+ expect(errors).to be_empty
153
151
  end
154
152
  end
155
153
  end
@@ -51,8 +51,6 @@ describe '{{value}}' do
51
51
  end
52
52
 
53
53
  describe "#validate" do
54
- let(:presenter_class) { double(:presenter_class) }
55
-
56
54
  it "validates the path with errors" do
57
55
  dependency_tree = {}
58
56
 
@@ -25,8 +25,6 @@ describe "{{> partial}}" do
25
25
  end
26
26
 
27
27
  describe "#validate" do
28
- let(:presenter_class) { double(:presenter_class) }
29
-
30
28
  it "validates the path with errors" do
31
29
  dependency_tree = {}
32
30
 
@@ -123,8 +123,6 @@ describe "{{path}}" do
123
123
  end
124
124
 
125
125
  describe "#validate" do
126
- let(:presenter_class) { double(:presenter_class) }
127
-
128
126
  it "without errors" do
129
127
  dependency_tree = { sub_context: {}, outer_field: nil }
130
128
 
@@ -1,7 +1,5 @@
1
1
  describe "root" do
2
2
  describe "#validate" do
3
- let(:presenter_class) { double(:presenter_class) }
4
-
5
3
  it "without errors if template is empty" do
6
4
  dependency_tree = {}
7
5
 
@@ -0,0 +1,426 @@
1
+ describe "{{(helper arg1 arg2 ... key=value ...)}}" do
2
+ let(:global_helpers_providers) { [IntegrationTest::GlobalHelperProvider.new] }
3
+
4
+ describe "#compile" do
5
+ let(:presenter) { IntegrationTest::Presenter.new(double("view_context")) }
6
+
7
+ it "can be an argument to helpers" do
8
+ template = Curlybars.compile(<<-HBS)
9
+ {{global_helper (global_helper 'argument' option='value') option='value'}}
10
+ HBS
11
+
12
+ expect(eval(template)).to resemble(<<-HTML)
13
+ argument - option:value - option:value
14
+ HTML
15
+ end
16
+
17
+ it "can be an argument to itself" do
18
+ template = Curlybars.compile(<<-HBS)
19
+ {{global_helper (global_helper (global_helper 'a' option='b') option='c') option='d'}}
20
+ HBS
21
+
22
+ expect(eval(template)).to resemble(<<-HTML)
23
+ a - option:b - option:c - option:d
24
+ HTML
25
+ end
26
+
27
+ it "can handle data objects as argument" do
28
+ template = Curlybars.compile(<<-HBS)
29
+ {{global_helper (extract user attribute='first_name') option='value'}}
30
+ HBS
31
+
32
+ expect(eval(template)).to resemble(<<-HTML)
33
+ Libo - option:value
34
+ HTML
35
+ end
36
+
37
+ it "can handle calls inside with" do
38
+ template = Curlybars.compile(<<-HBS)
39
+ {{#with article}}
40
+ {{global_helper (extract author attribute='first_name') option='value'}}
41
+ {{/with}}
42
+ HBS
43
+
44
+ expect(eval(template)).to resemble(<<-HTML)
45
+ Nicolò - option:value
46
+ HTML
47
+ end
48
+
49
+ it "does not accept subexpressions in the root" do
50
+ expect do
51
+ Curlybars.compile(<<-HBS)
52
+ {{(join articles attribute='title' separator='-'}}
53
+ HBS
54
+ end.to raise_error(Curlybars::Error::Parse)
55
+ end
56
+
57
+ it "can be called within if expressions" do
58
+ template = Curlybars.compile(<<-HBS)
59
+ {{#if (calc 3 ">" 1)}}
60
+ True
61
+ {{/if}}
62
+ HBS
63
+
64
+ expect(eval(template)).to resemble(<<-HTML)
65
+ True
66
+ HTML
67
+ end
68
+
69
+ # Replication of Handlebars' subexpression specs for feature parity
70
+ # https://github.com/handlebars-lang/handlebars.js/blob/1a08e1d0a7f500f2c1188cbd21750bb9180afcbb/spec/subexpressions.js
71
+
72
+ it "arg-less helper" do
73
+ template = Curlybars.compile(<<-HBS)
74
+ {{foo (bar)}}!
75
+ HBS
76
+
77
+ expect(eval(template)).to resemble(<<-HTML)
78
+ LOLLOL!
79
+ HTML
80
+ end
81
+
82
+ context "with blog presenter" do
83
+ let(:presenter) do
84
+ IntegrationTest::BlogPresenter.new(
85
+ lambda { |*args, options|
86
+ val = args.first
87
+ "val is #{val}"
88
+ }
89
+ )
90
+ end
91
+
92
+ it "helper w args" do
93
+ template = Curlybars.compile(<<-HBS)
94
+ {{blog (equal a b)}}
95
+ HBS
96
+
97
+ expect(eval(template)).to resemble(<<-HTML)
98
+ val is true
99
+ HTML
100
+ end
101
+
102
+ it "supports much nesting" do
103
+ template = Curlybars.compile(<<-HBS)
104
+ {{blog (equal (equal true true) true)}}
105
+ HBS
106
+
107
+ expect(eval(template)).to resemble(<<-HTML)
108
+ val is true
109
+ HTML
110
+ end
111
+
112
+ it "with hashes" do
113
+ template = Curlybars.compile(<<-HBS)
114
+ {{blog (equal (equal true true) true fun='yes')}}
115
+ HBS
116
+
117
+ expect(eval(template)).to resemble(<<-HTML)
118
+ val is true
119
+ HTML
120
+ end
121
+ end
122
+
123
+ context "with a different blog presenter" do
124
+ let(:presenter) do
125
+ IntegrationTest::BlogPresenter.new(
126
+ lambda { |*args, options|
127
+ "val is #{options[:fun]}"
128
+ }
129
+ )
130
+ end
131
+
132
+ it "as hashes" do
133
+ template = Curlybars.compile(<<-HBS)
134
+ {{blog fun=(equal (blog fun=1) 'val is 1')}}
135
+ HBS
136
+
137
+ expect(eval(template)).to resemble(<<-HTML)
138
+ val is true
139
+ HTML
140
+ end
141
+ end
142
+
143
+ context "with yet another blog presenter" do
144
+ let(:presenter) do
145
+ IntegrationTest::BlogPresenter.new(
146
+ lambda { |*args, options|
147
+ first, second, third = args
148
+ "val is #{first}, #{second} and #{third}"
149
+ }
150
+ )
151
+ end
152
+
153
+ it "mixed paths and helpers" do
154
+ template = Curlybars.compile(<<-HBS)
155
+ {{blog baz.bat (equal a b) baz.bar}}
156
+ HBS
157
+
158
+ expect(eval(template)).to resemble(<<-HTML)
159
+ val is bat!, true and bar!
160
+ HTML
161
+ end
162
+ end
163
+
164
+ describe "GH-800 : Complex subexpressions" do
165
+ let(:presenter) do
166
+ IntegrationTest::LetterPresenter.new(
167
+ a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' }
168
+ )
169
+ end
170
+
171
+ it "can handle complex subexpressions" do
172
+ inputs = [
173
+ "{{dash 'abc' (concat a b)}}",
174
+ "{{dash d (concat a b)}}",
175
+ "{{dash c.c (concat a b)}}",
176
+ "{{dash (concat a b) c.c}}",
177
+ "{{dash (concat a e.e) c.c}}"
178
+ ]
179
+
180
+ expected_results = [
181
+ "abc-ab",
182
+ "d-ab",
183
+ "c-ab",
184
+ "ab-c",
185
+ "ae-c"
186
+ ]
187
+
188
+ aggregate_failures do
189
+ inputs.each_with_index do |input, i|
190
+ expect(eval(Curlybars.compile(input))).to resemble(expected_results[i])
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ it "multiple subexpressions in a hash" do
197
+ template = Curlybars.compile(<<-HBS)
198
+ {{input aria-label=(t "Name") placeholder=(t "Example User")}}
199
+ HBS
200
+
201
+ expected_output = '<input aria-label="Name" placeholder="Example User" />'
202
+ .gsub("<", "&lt;")
203
+ .gsub(">", "&gt;")
204
+ .gsub('"', "&quot;")
205
+
206
+ expect(eval(template)).to resemble(expected_output)
207
+ end
208
+
209
+ context "with item show presenter" do
210
+ let(:presenter) do
211
+ IntegrationTest::ItemShowPresenter.new(field: "Name", placeholder: "Example User")
212
+ end
213
+
214
+ it "multiple subexpressions in a hash with context" do
215
+ template = Curlybars.compile(<<-HBS)
216
+ {{input aria-label=(t item.field) placeholder=(t item.placeholder)}}
217
+ HBS
218
+
219
+ expected_output = '<input aria-label="Name" placeholder="Example User" />'
220
+ .gsub("<", "&lt;")
221
+ .gsub(">", "&gt;")
222
+ .gsub('"', "&quot;")
223
+
224
+ expect(eval(template)).to resemble(expected_output)
225
+ end
226
+ end
227
+ end
228
+
229
+ describe "#validate" do
230
+ before do
231
+ allow(Curlybars.configuration).to receive(:global_helpers_provider_classes).and_return([IntegrationTest::GlobalHelperProvider])
232
+ end
233
+
234
+ it "without errors when global helper" do
235
+ dependency_tree = {}
236
+
237
+ source = <<-HBS
238
+ {{#if (global_helper)}} ... {{/if}}
239
+ HBS
240
+
241
+ errors = Curlybars.validate(dependency_tree, source)
242
+
243
+ expect(errors).to be_empty
244
+ end
245
+
246
+ it "with errors when invoking a leaf" do
247
+ dependency_tree = { name: nil }
248
+
249
+ source = <<-HBS
250
+ {{#if (name)}} ... {{/if}}
251
+ HBS
252
+
253
+ errors = Curlybars.validate(dependency_tree, source)
254
+
255
+ expect(errors).not_to be_empty
256
+ end
257
+
258
+ it "without errors if argument is a leaf" do
259
+ dependency_tree = { helper: :helper, argument: nil }
260
+
261
+ source = <<-HBS
262
+ {{#if (helper argument)}} ... {{/if}}
263
+ HBS
264
+
265
+ errors = Curlybars.validate(dependency_tree, source)
266
+
267
+ expect(errors).to be_empty
268
+ end
269
+
270
+ it "without errors if argument is a literal" do
271
+ dependency_tree = { helper: :helper }
272
+
273
+ source = <<-HBS
274
+ {{#if (helper 'argument')}} ... {{/if}}
275
+ HBS
276
+
277
+ errors = Curlybars.validate(dependency_tree, source)
278
+
279
+ expect(errors).to be_empty
280
+ end
281
+
282
+ it "without errors if argument is a variable" do
283
+ dependency_tree = { helper: :helper }
284
+
285
+ source = <<-HBS
286
+ {{#if (helper @var)}} ... {{/if}}
287
+ HBS
288
+
289
+ errors = Curlybars.validate(dependency_tree, source)
290
+
291
+ expect(errors).to be_empty
292
+ end
293
+
294
+ it "without errors if argument is another subexpression" do
295
+ dependency_tree = { helper: :helper }
296
+
297
+ source = <<-HBS
298
+ {{#if (helper (helper option='argument'))}} ... {{/if}}
299
+ HBS
300
+
301
+ errors = Curlybars.validate(dependency_tree, source)
302
+
303
+ expect(errors).to be_empty
304
+ end
305
+
306
+ it "without errors if option is a leaf" do
307
+ dependency_tree = { helper: :helper, argument: nil }
308
+
309
+ source = <<-HBS
310
+ {{#if (helper option=argument)}} ... {{/if}}
311
+ HBS
312
+
313
+ errors = Curlybars.validate(dependency_tree, source)
314
+
315
+ expect(errors).to be_empty
316
+ end
317
+
318
+ it "without errors if option is a literal" do
319
+ dependency_tree = { helper: :helper }
320
+
321
+ source = <<-HBS
322
+ {{#if (helper option='argument')}} ... {{/if}}
323
+ HBS
324
+
325
+ errors = Curlybars.validate(dependency_tree, source)
326
+
327
+ expect(errors).to be_empty
328
+ end
329
+
330
+ it "without errors if option is a variable" do
331
+ dependency_tree = { helper: :helper }
332
+
333
+ source = <<-HBS
334
+ {{#if (helper option=@var)}} ... {{/if}}
335
+ HBS
336
+
337
+ errors = Curlybars.validate(dependency_tree, source)
338
+
339
+ expect(errors).to be_empty
340
+ end
341
+
342
+ it "without errors if option is another subexpression" do
343
+ dependency_tree = { helper: :helper }
344
+
345
+ source = <<-HBS
346
+ {{#if (helper option=(helper))}} ... {{/if}}
347
+ HBS
348
+
349
+ errors = Curlybars.validate(dependency_tree, source)
350
+
351
+ expect(errors).to be_empty
352
+ end
353
+
354
+ it "with errors when helper does not exist" do
355
+ dependency_tree = {}
356
+
357
+ source = <<-HBS
358
+ {{#if (helper)}} ... {{/if}}
359
+ HBS
360
+
361
+ errors = Curlybars.validate(dependency_tree, source)
362
+
363
+ expect(errors).not_to be_empty
364
+ end
365
+
366
+ it "with errors when invoking a leaf with arguments" do
367
+ dependency_tree = { name: nil }
368
+
369
+ source = <<-HBS
370
+ {{#if (name 'argument')}} ... {{/if}}
371
+ HBS
372
+
373
+ errors = Curlybars.validate(dependency_tree, source)
374
+
375
+ expect(errors).not_to be_empty
376
+ end
377
+
378
+ it "with errors when invoking a leaf with options" do
379
+ dependency_tree = { name: nil }
380
+
381
+ source = <<-HBS
382
+ {{#if (name option='value')}} ... {{/if}}
383
+ HBS
384
+
385
+ errors = Curlybars.validate(dependency_tree, source)
386
+
387
+ expect(errors).not_to be_empty
388
+ end
389
+
390
+ it "with errors if argument is not a value" do
391
+ dependency_tree = { helper: :helper }
392
+
393
+ source = <<-HBS
394
+ {{#if (helper not_a_value)}} ... {{/if}}
395
+ HBS
396
+
397
+ errors = Curlybars.validate(dependency_tree, source)
398
+
399
+ expect(errors).not_to be_empty
400
+ end
401
+
402
+ it "with errors if option is not a value" do
403
+ dependency_tree = { helper: :helper }
404
+
405
+ source = <<-HBS
406
+ {{#if (helper option=not_a_value)}} ... {{/if}}
407
+ HBS
408
+
409
+ errors = Curlybars.validate(dependency_tree, source)
410
+
411
+ expect(errors).not_to be_empty
412
+ end
413
+
414
+ it "without errors when invoking a helper with the result of a subexpression" do
415
+ dependency_tree = { join: :helper, uppercase: :helper, article: nil }
416
+
417
+ source = <<-HBS
418
+ {{join (uppercase article) attribute='title' separator='-'}}
419
+ HBS
420
+
421
+ errors = Curlybars.validate(dependency_tree, source)
422
+
423
+ expect(errors).to be_empty
424
+ end
425
+ end
426
+ end