curlybars 1.3.1 → 1.7.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.
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