curlybars 0.9.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/lib/curlybars.rb +108 -0
  3. data/lib/curlybars/configuration.rb +41 -0
  4. data/lib/curlybars/dependency_tracker.rb +8 -0
  5. data/lib/curlybars/error/base.rb +18 -0
  6. data/lib/curlybars/error/compile.rb +11 -0
  7. data/lib/curlybars/error/lex.rb +22 -0
  8. data/lib/curlybars/error/parse.rb +41 -0
  9. data/lib/curlybars/error/presenter/not_found.rb +23 -0
  10. data/lib/curlybars/error/render.rb +11 -0
  11. data/lib/curlybars/error/validate.rb +18 -0
  12. data/lib/curlybars/lexer.rb +60 -0
  13. data/lib/curlybars/method_whitelist.rb +69 -0
  14. data/lib/curlybars/node/block_helper_else.rb +108 -0
  15. data/lib/curlybars/node/boolean.rb +24 -0
  16. data/lib/curlybars/node/each_else.rb +69 -0
  17. data/lib/curlybars/node/if_else.rb +33 -0
  18. data/lib/curlybars/node/item.rb +31 -0
  19. data/lib/curlybars/node/literal.rb +28 -0
  20. data/lib/curlybars/node/option.rb +25 -0
  21. data/lib/curlybars/node/output.rb +24 -0
  22. data/lib/curlybars/node/partial.rb +24 -0
  23. data/lib/curlybars/node/path.rb +137 -0
  24. data/lib/curlybars/node/root.rb +29 -0
  25. data/lib/curlybars/node/string.rb +24 -0
  26. data/lib/curlybars/node/template.rb +32 -0
  27. data/lib/curlybars/node/text.rb +24 -0
  28. data/lib/curlybars/node/unless_else.rb +33 -0
  29. data/lib/curlybars/node/variable.rb +34 -0
  30. data/lib/curlybars/node/with_else.rb +54 -0
  31. data/lib/curlybars/parser.rb +183 -0
  32. data/lib/curlybars/position.rb +7 -0
  33. data/lib/curlybars/presenter.rb +288 -0
  34. data/lib/curlybars/processor/tilde.rb +31 -0
  35. data/lib/curlybars/processor/token_factory.rb +9 -0
  36. data/lib/curlybars/railtie.rb +18 -0
  37. data/lib/curlybars/rendering_support.rb +222 -0
  38. data/lib/curlybars/safe_buffer.rb +11 -0
  39. data/lib/curlybars/template_handler.rb +93 -0
  40. data/lib/curlybars/version.rb +3 -0
  41. data/spec/acceptance/application_layout_spec.rb +60 -0
  42. data/spec/acceptance/collection_blocks_spec.rb +28 -0
  43. data/spec/acceptance/global_helper_spec.rb +25 -0
  44. data/spec/curlybars/configuration_spec.rb +57 -0
  45. data/spec/curlybars/error/base_spec.rb +41 -0
  46. data/spec/curlybars/error/compile_spec.rb +19 -0
  47. data/spec/curlybars/error/lex_spec.rb +25 -0
  48. data/spec/curlybars/error/parse_spec.rb +74 -0
  49. data/spec/curlybars/error/render_spec.rb +19 -0
  50. data/spec/curlybars/error/validate_spec.rb +19 -0
  51. data/spec/curlybars/lexer_spec.rb +466 -0
  52. data/spec/curlybars/method_whitelist_spec.rb +168 -0
  53. data/spec/curlybars/processor/tilde_spec.rb +60 -0
  54. data/spec/curlybars/rendering_support_spec.rb +426 -0
  55. data/spec/curlybars/safe_buffer_spec.rb +46 -0
  56. data/spec/curlybars/template_handler_spec.rb +222 -0
  57. data/spec/integration/cache_spec.rb +124 -0
  58. data/spec/integration/comment_spec.rb +60 -0
  59. data/spec/integration/exception_spec.rb +31 -0
  60. data/spec/integration/node/block_helper_else_spec.rb +422 -0
  61. data/spec/integration/node/each_else_spec.rb +204 -0
  62. data/spec/integration/node/each_spec.rb +291 -0
  63. data/spec/integration/node/escape_spec.rb +27 -0
  64. data/spec/integration/node/helper_spec.rb +176 -0
  65. data/spec/integration/node/if_else_spec.rb +129 -0
  66. data/spec/integration/node/if_spec.rb +143 -0
  67. data/spec/integration/node/output_spec.rb +68 -0
  68. data/spec/integration/node/partial_spec.rb +66 -0
  69. data/spec/integration/node/path_spec.rb +286 -0
  70. data/spec/integration/node/root_spec.rb +15 -0
  71. data/spec/integration/node/template_spec.rb +86 -0
  72. data/spec/integration/node/unless_else_spec.rb +129 -0
  73. data/spec/integration/node/unless_spec.rb +130 -0
  74. data/spec/integration/node/with_spec.rb +116 -0
  75. data/spec/integration/processor/tilde_spec.rb +38 -0
  76. data/spec/integration/processors_spec.rb +30 -0
  77. metadata +358 -0
@@ -0,0 +1,204 @@
1
+ describe "{{#each collection}}...{{else}}...{{/each}}" do
2
+ let(:global_helpers_providers) { [] }
3
+
4
+ describe "#compile" do
5
+ let(:post) { double("post") }
6
+ let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
7
+
8
+ it "uses each_template when collection is not empty" do
9
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
10
+ allow(presenter).to receive(:non_empty_collection) { [presenter] }
11
+
12
+ template = Curlybars.compile(<<-HBS)
13
+ {{#each non_empty_collection}}
14
+ each_template
15
+ {{else}}
16
+ else_template
17
+ {{/each}}
18
+ HBS
19
+
20
+ expect(eval(template)).to resemble(<<-HTML)
21
+ each_template
22
+ HTML
23
+ end
24
+
25
+ it "uses else_template when collection is not empty" do
26
+ allow(presenter).to receive(:allows_method?).with(:empty_collection) { true }
27
+ allow(presenter).to receive(:empty_collection) { [] }
28
+
29
+ template = Curlybars.compile(<<-HBS)
30
+ {{#each empty_collection}}
31
+ each_template
32
+ {{else}}
33
+ else_template
34
+ {{/each}}
35
+ HBS
36
+
37
+ expect(eval(template)).to resemble(<<-HTML)
38
+ else_template
39
+ HTML
40
+ end
41
+
42
+ it "uses each_template when collection is not empty" do
43
+ path_presenter_class = Class.new(Curlybars::Presenter) do
44
+ presents :path
45
+ allow_methods :path
46
+ def path
47
+ @path
48
+ end
49
+ end
50
+
51
+ a_path_presenter = path_presenter_class.new(nil, path: 'a_path')
52
+ another_path_presenter = path_presenter_class.new(nil, path: 'another_path')
53
+
54
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
55
+ allow(presenter).to receive(:non_empty_collection) { [a_path_presenter, another_path_presenter] }
56
+
57
+ template = Curlybars.compile(<<-HBS)
58
+ {{#each non_empty_collection}}
59
+ {{path}}
60
+ {{else}}
61
+ else_template
62
+ {{/each}}
63
+ HBS
64
+
65
+ expect(eval(template)).to resemble(<<-HTML)
66
+ a_path
67
+ another_path
68
+ HTML
69
+ end
70
+
71
+ it "allows empty each_template" do
72
+ allow(presenter).to receive(:allows_method?).with(:empty_collection) { true }
73
+ allow(presenter).to receive(:empty_collection) { [] }
74
+
75
+ template = Curlybars.compile(<<-HBS)
76
+ {{#each empty_collection}}{{else}}
77
+ else_template
78
+ {{/each}}
79
+ HBS
80
+
81
+ expect(eval(template)).to resemble(<<-HTML)
82
+ else_template
83
+ HTML
84
+ end
85
+
86
+ it "allows empty else_template" do
87
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
88
+ allow(presenter).to receive(:non_empty_collection) { [presenter] }
89
+
90
+ template = Curlybars.compile(<<-HBS)
91
+ {{#each non_empty_collection}}
92
+ each_template
93
+ {{else}}{{/each}}
94
+ HBS
95
+
96
+ expect(eval(template)).to resemble(<<-HTML)
97
+ each_template
98
+ HTML
99
+ end
100
+
101
+ it "allows empty each_template and else_template" do
102
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
103
+ allow(presenter).to receive(:non_empty_collection) { [presenter] }
104
+
105
+ template = Curlybars.compile(<<-HBS)
106
+ {{#each non_empty_collection}}{{else}}{{/each}}
107
+ HBS
108
+
109
+ expect(eval(template)).to resemble("")
110
+ end
111
+
112
+ it "renders nothing if the context is nil" do
113
+ template = Curlybars.compile(<<-HBS)
114
+ {{#each return_nil}}
115
+ each_template
116
+ {{else}}
117
+ else_template
118
+ {{/each}}
119
+ HBS
120
+
121
+ expect(eval(template)).to resemble(<<-HTML)
122
+ else_template
123
+ HTML
124
+ end
125
+
126
+ it "raises an error if the context is not an array-like object" do
127
+ allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:not_a_collection) { true }
128
+ allow(presenter).to receive(:not_a_collection) { "string" }
129
+
130
+ template = Curlybars.compile(<<-HBS)
131
+ {{#each not_a_collection}}{{else}}{{/each}}
132
+ HBS
133
+
134
+ expect do
135
+ eval(template)
136
+ end.to raise_error(Curlybars::Error::Render)
137
+ end
138
+
139
+ it "raises an error if the objects inside of the context array are not presenters" do
140
+ allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:not_a_presenter_collection) { true }
141
+ allow(presenter).to receive(:not_a_presenter_collection) { [:an_element] }
142
+
143
+ template = Curlybars.compile(<<-HBS)
144
+ {{#each not_a_presenter_collection}}{{else}}{{/each}}
145
+ HBS
146
+
147
+ expect do
148
+ eval(template)
149
+ end.to raise_error(Curlybars::Error::Render)
150
+ end
151
+ end
152
+
153
+ describe "#validate" do
154
+ let(:presenter_class) { double(:presenter_class) }
155
+
156
+ it "without errors" do
157
+ dependency_tree = { a_presenter_collection: [{}] }
158
+
159
+ source = <<-HBS
160
+ {{#each a_presenter_collection}} {{else}} {{/each}}
161
+ HBS
162
+
163
+ errors = Curlybars.validate(dependency_tree, source)
164
+
165
+ expect(errors).to be_empty
166
+ end
167
+
168
+ it "with errors due to a presenter path" do
169
+ dependency_tree = { a_presenter: {} }
170
+
171
+ source = <<-HBS
172
+ {{#each a_presenter}} {{else}} {{/each}}
173
+ HBS
174
+
175
+ errors = Curlybars.validate(dependency_tree, source)
176
+
177
+ expect(errors).not_to be_empty
178
+ end
179
+
180
+ it "with errors due to a leaf path" do
181
+ dependency_tree = { a_leaf: nil }
182
+
183
+ source = <<-HBS
184
+ {{#each a_leaf}} {{else}} {{/each}}
185
+ HBS
186
+
187
+ errors = Curlybars.validate(dependency_tree, source)
188
+
189
+ expect(errors).not_to be_empty
190
+ end
191
+
192
+ it "with errors due unallowed method" do
193
+ dependency_tree = {}
194
+
195
+ source = <<-HBS
196
+ {{#each unallowed}} {{else}} {{/each}}
197
+ HBS
198
+
199
+ errors = Curlybars.validate(dependency_tree, source)
200
+
201
+ expect(errors).not_to be_empty
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,291 @@
1
+ describe "{{#each collection}}...{{/each}}" do
2
+ let(:global_helpers_providers) { [] }
3
+
4
+ describe "#compile" do
5
+ let(:post) { double("post") }
6
+ let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
7
+
8
+ it "uses each_template when collection is not empty" do
9
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
10
+ allow(presenter).to receive(:non_empty_collection) { [presenter] }
11
+
12
+ template = Curlybars.compile(<<-HBS)
13
+ {{#each non_empty_collection}}
14
+ each_template
15
+ {{/each}}
16
+ HBS
17
+
18
+ expect(eval(template)).to resemble(<<-HTML)
19
+ each_template
20
+ HTML
21
+ end
22
+
23
+ it "doesn't use each_template when collection is empty" do
24
+ allow(presenter).to receive(:allows_method?).with(:empty_collection) { true }
25
+ allow(presenter).to receive(:empty_collection) { [] }
26
+
27
+ template = Curlybars.compile(<<-HBS)
28
+ {{#each empty_collection}}
29
+ each_template
30
+ {{/each}}
31
+ HBS
32
+
33
+ expect(eval(template)).to resemble("")
34
+ end
35
+
36
+ it "allows empty each_template" do
37
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
38
+ allow(presenter).to receive(:non_empty_collection) { [presenter] }
39
+
40
+ template = Curlybars.compile(<<-HBS)
41
+ {{#each non_empty_collection}}{{/each}}
42
+ HBS
43
+
44
+ expect(eval(template)).to resemble("")
45
+ end
46
+
47
+ it "uses each_template when collection is a not empty enumerable" do
48
+ path_presenter_class = Class.new(Curlybars::Presenter) do
49
+ presents :path
50
+ allow_methods :path
51
+ def path
52
+ @path
53
+ end
54
+ end
55
+
56
+ a_path_presenter = path_presenter_class.new(nil, path: 'a_path')
57
+ another_path_presenter = path_presenter_class.new(nil, path: 'another_path')
58
+
59
+ allow(presenter).to receive(:allows_method?).with(:non_empty_collection) { true }
60
+ allow(presenter).to receive(:non_empty_collection) { [a_path_presenter, another_path_presenter] }
61
+
62
+ template = Curlybars.compile(<<-HBS)
63
+ {{#each non_empty_collection}}
64
+ {{path}}
65
+ {{/each}}
66
+ HBS
67
+
68
+ expect(eval(template)).to resemble(<<-HTML)
69
+ a_path
70
+ another_path
71
+ HTML
72
+ end
73
+
74
+ it "uses each_template when collection is a not empty hash" do
75
+ path_presenter_class = Class.new(Curlybars::Presenter) do
76
+ presents :path
77
+ allow_methods :path
78
+ def path
79
+ @path
80
+ end
81
+ end
82
+
83
+ a_path_presenter = path_presenter_class.new(nil, path: 'a_path')
84
+ another_path_presenter = path_presenter_class.new(nil, path: 'another_path')
85
+
86
+ allow(presenter).to receive(:allows_method?).with(:non_empty_hash) { true }
87
+ allow(presenter).to receive(:non_empty_hash) do
88
+ { first: a_path_presenter, second: another_path_presenter }
89
+ end
90
+
91
+ template = Curlybars.compile(<<-HBS)
92
+ {{#each non_empty_hash}}
93
+ {{path}}
94
+ {{/each}}
95
+ HBS
96
+
97
+ expect(eval(template)).to resemble(<<-HTML)
98
+ a_path
99
+ another_path
100
+ HTML
101
+ end
102
+
103
+ it "raises an error if the context is not an array-like object" do
104
+ allow(presenter).to receive(:allows_method?).with(:not_a_collection) { true }
105
+ allow(presenter).to receive(:not_a_collection) { "string" }
106
+
107
+ template = Curlybars.compile(<<-HBS)
108
+ {{#each not_a_collection}}{{/each}}
109
+ HBS
110
+
111
+ expect do
112
+ eval(template)
113
+ end.to raise_error(Curlybars::Error::Render)
114
+ end
115
+
116
+ it "renders nothing if the context is nil" do
117
+ template = Curlybars.compile(<<-HBS)
118
+ {{#each return_nil}}
119
+ text
120
+ {{/each}}
121
+ HBS
122
+
123
+ expect(eval(template)).to resemble("")
124
+ end
125
+
126
+ it "gives access to `@index` variable" do
127
+ template = Curlybars.compile(<<-HBS)
128
+ {{#each two_elements}}
129
+ {{@index}}
130
+ {{/each}}
131
+ HBS
132
+
133
+ expect(eval(template)).to resemble(<<-HTML)
134
+ 0
135
+ 1
136
+ HTML
137
+ end
138
+
139
+ it "gives access to `@first` variable" do
140
+ template = Curlybars.compile(<<-HBS)
141
+ {{#each two_elements}}
142
+ {{@first}}
143
+ {{/each}}
144
+ HBS
145
+
146
+ expect(eval(template)).to resemble(<<-HTML)
147
+ true
148
+ false
149
+ HTML
150
+ end
151
+
152
+ it "gives access to `@last` variable" do
153
+ template = Curlybars.compile(<<-HBS)
154
+ {{#each two_elements}}
155
+ {{@last}}
156
+ {{/each}}
157
+ HBS
158
+
159
+ expect(eval(template)).to resemble(<<-HTML)
160
+ false
161
+ true
162
+ HTML
163
+ end
164
+
165
+ it "gives access to variables from nested {{#with}}" do
166
+ template = Curlybars.compile(<<-HBS)
167
+ {{#each two_elements}}
168
+ {{#with me}}
169
+ {{@index}}
170
+ {{/with}}
171
+ {{/each}}
172
+ HBS
173
+
174
+ expect(eval(template)).to resemble(<<-HTML)
175
+ 0
176
+ 1
177
+ HTML
178
+ end
179
+
180
+ it "gives access to variables from nested {{#each}}" do
181
+ template = Curlybars.compile(<<-HBS)
182
+ {{#each two_elements}}
183
+ {{#each ../two_elements}}
184
+ {{@../index}}
185
+ {{/each}}
186
+ {{/each}}
187
+ HBS
188
+
189
+ expect(eval(template)).to resemble(<<-HTML)
190
+ 0
191
+ 0
192
+ 1
193
+ 1
194
+ HTML
195
+ end
196
+
197
+ it "gives access to variables when collection is a not empty hash" do
198
+ path_presenter_class = Class.new(Curlybars::Presenter) do
199
+ presents :path
200
+ allow_methods :path
201
+ def path
202
+ @path
203
+ end
204
+ end
205
+
206
+ a_path_presenter = path_presenter_class.new(nil, path: 'a_path')
207
+ another_path_presenter = path_presenter_class.new(nil, path: 'another_path')
208
+
209
+ allow(presenter).to receive(:allows_method?).with(:non_empty_hash) { true }
210
+ allow(presenter).to receive(:non_empty_hash) do
211
+ { first: a_path_presenter, second: another_path_presenter }
212
+ end
213
+
214
+ template = Curlybars.compile(<<-HBS)
215
+ {{#each non_empty_hash}}
216
+ {{@index}}) {{@key}}: {{path}}
217
+ {{/each}}
218
+ HBS
219
+
220
+ expect(eval(template)).to resemble(<<-HTML)
221
+ 0) first: a_path
222
+ 1) second: another_path
223
+ HTML
224
+ end
225
+
226
+ it "raises an error if the objects inside of the context array are not presenters" do
227
+ allow(presenter).to receive(:allows_method?).with(:not_a_presenter_collection) { true }
228
+ allow(presenter).to receive(:not_a_presenter_collection) { [:an_element] }
229
+
230
+ template = Curlybars.compile(<<-HBS)
231
+ {{#each not_a_presenter_collection}}{{/each}}
232
+ HBS
233
+
234
+ expect do
235
+ eval(template)
236
+ end.to raise_error(Curlybars::Error::Render)
237
+ end
238
+ end
239
+
240
+ describe "#validate" do
241
+ let(:presenter_class) { double(:presenter_class) }
242
+
243
+ it "without errors" do
244
+ dependency_tree = { a_presenter_collection: [{}] }
245
+
246
+ source = <<-HBS
247
+ {{#each a_presenter_collection}}{{/each}}
248
+ HBS
249
+
250
+ errors = Curlybars.validate(dependency_tree, source)
251
+
252
+ expect(errors).to be_empty
253
+ end
254
+
255
+ it "with errors due to a presenter path" do
256
+ dependency_tree = { a_presenter: {} }
257
+
258
+ source = <<-HBS
259
+ {{#each a_presenter}}{{/each}}
260
+ HBS
261
+
262
+ errors = Curlybars.validate(dependency_tree, source)
263
+
264
+ expect(errors).not_to be_empty
265
+ end
266
+
267
+ it "with errors due to a leaf path" do
268
+ dependency_tree = { a_leaf: nil }
269
+
270
+ source = <<-HBS
271
+ {{#each a_leaf}}{{/each}}
272
+ HBS
273
+
274
+ errors = Curlybars.validate(dependency_tree, source)
275
+
276
+ expect(errors).not_to be_empty
277
+ end
278
+
279
+ it "with errors due unallowed method" do
280
+ dependency_tree = {}
281
+
282
+ source = <<-HBS
283
+ {{#each unallowed}}{{/each}}
284
+ HBS
285
+
286
+ errors = Curlybars.validate(dependency_tree, source)
287
+
288
+ expect(errors).not_to be_empty
289
+ end
290
+ end
291
+ end