curlybars 1.7.0 → 1.9.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/lib/curlybars/error/lex.rb +1 -1
- data/lib/curlybars/error/parse.rb +1 -1
- data/lib/curlybars/node/path.rb +11 -13
- data/lib/curlybars/presenter.rb +2 -2
- data/lib/curlybars/template_handler.rb +3 -11
- data/lib/curlybars/version.rb +1 -1
- metadata +17 -93
- data/spec/acceptance/application_layout_spec.rb +0 -60
- data/spec/acceptance/collection_blocks_spec.rb +0 -28
- data/spec/acceptance/global_helper_spec.rb +0 -25
- data/spec/curlybars/configuration_spec.rb +0 -57
- data/spec/curlybars/error/base_spec.rb +0 -41
- data/spec/curlybars/error/compile_spec.rb +0 -19
- data/spec/curlybars/error/lex_spec.rb +0 -25
- data/spec/curlybars/error/parse_spec.rb +0 -74
- data/spec/curlybars/error/render_spec.rb +0 -19
- data/spec/curlybars/error/validate_spec.rb +0 -19
- data/spec/curlybars/lexer_spec.rb +0 -490
- data/spec/curlybars/method_whitelist_spec.rb +0 -299
- data/spec/curlybars/processor/tilde_spec.rb +0 -60
- data/spec/curlybars/rendering_support_spec.rb +0 -421
- data/spec/curlybars/safe_buffer_spec.rb +0 -46
- data/spec/curlybars/template_handler_spec.rb +0 -225
- data/spec/integration/cache_spec.rb +0 -126
- data/spec/integration/comment_spec.rb +0 -60
- data/spec/integration/exception_spec.rb +0 -31
- data/spec/integration/node/block_helper_else_spec.rb +0 -420
- data/spec/integration/node/each_else_spec.rb +0 -408
- data/spec/integration/node/each_spec.rb +0 -289
- data/spec/integration/node/escape_spec.rb +0 -27
- data/spec/integration/node/helper_spec.rb +0 -186
- data/spec/integration/node/if_else_spec.rb +0 -170
- data/spec/integration/node/if_spec.rb +0 -153
- data/spec/integration/node/output_spec.rb +0 -66
- data/spec/integration/node/partial_spec.rb +0 -64
- data/spec/integration/node/path_spec.rb +0 -296
- data/spec/integration/node/root_spec.rb +0 -13
- data/spec/integration/node/sub_expression_spec.rb +0 -426
- data/spec/integration/node/template_spec.rb +0 -84
- data/spec/integration/node/unless_else_spec.rb +0 -139
- data/spec/integration/node/unless_spec.rb +0 -128
- data/spec/integration/node/with_spec.rb +0 -178
- data/spec/integration/processor/tilde_spec.rb +0 -38
- data/spec/integration/processors_spec.rb +0 -29
- data/spec/integration/visitor_spec.rb +0 -154
@@ -1,421 +0,0 @@
|
|
1
|
-
# rubocop:disable RSpec/MultipleMemoizedHelpers
|
2
|
-
describe Curlybars::RenderingSupport do
|
3
|
-
let(:file_name) { '/app/views/template.hbs' }
|
4
|
-
let(:presenter) { double(:presenter, allows_method?: true, meth: :value) }
|
5
|
-
let(:contexts) { [presenter] }
|
6
|
-
let(:variables) { [{}] }
|
7
|
-
let(:rendering) { Curlybars::RenderingSupport.new(1.second, contexts, variables, file_name) }
|
8
|
-
let(:position) do
|
9
|
-
double(:position, file_name: 'template.hbs', line_number: 1, line_offset: 0)
|
10
|
-
end
|
11
|
-
|
12
|
-
describe "#check_timeout!" do
|
13
|
-
it "skips checking if timeout is nil" do
|
14
|
-
rendering = Curlybars::RenderingSupport.new(nil, contexts, variables, file_name)
|
15
|
-
|
16
|
-
sleep 0.1.seconds
|
17
|
-
expect { rendering.check_timeout! }.not_to raise_error
|
18
|
-
end
|
19
|
-
|
20
|
-
it "doesn't happen when rendering is < rendering_timeout" do
|
21
|
-
rendering = Curlybars::RenderingSupport.new(10.seconds, contexts, variables, file_name)
|
22
|
-
expect { rendering.check_timeout! }.not_to raise_error
|
23
|
-
end
|
24
|
-
|
25
|
-
it "happens and raises when rendering >= rendering_timeout" do
|
26
|
-
rendering = Curlybars::RenderingSupport.new(0.01.seconds, contexts, variables, file_name)
|
27
|
-
|
28
|
-
sleep 0.1.seconds
|
29
|
-
expect { rendering.check_timeout! }.to raise_error(Curlybars::Error::Render)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe "#to_bool" do
|
34
|
-
describe "returns true" do
|
35
|
-
it "with `true`" do
|
36
|
-
expect(rendering.to_bool(true)).to be_truthy
|
37
|
-
end
|
38
|
-
|
39
|
-
it "with `[:non_empty]`" do
|
40
|
-
expect(rendering.to_bool([:non_empty])).to be_truthy
|
41
|
-
end
|
42
|
-
|
43
|
-
it "with `1`" do
|
44
|
-
expect(rendering.to_bool(1)).to be_truthy
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "returns false" do
|
49
|
-
it "with `false`" do
|
50
|
-
expect(rendering.to_bool(false)).to be_falsey
|
51
|
-
end
|
52
|
-
|
53
|
-
it "with `[]`" do
|
54
|
-
expect(rendering.to_bool([])).to be_falsey
|
55
|
-
end
|
56
|
-
|
57
|
-
it "with `{}`" do
|
58
|
-
expect(rendering.to_bool({})).to be_falsey
|
59
|
-
end
|
60
|
-
|
61
|
-
it "with `0`" do
|
62
|
-
expect(rendering.to_bool(0)).to be_falsey
|
63
|
-
end
|
64
|
-
|
65
|
-
it "with `nil`" do
|
66
|
-
expect(rendering.to_bool(nil)).to be_falsey
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "#path" do
|
72
|
-
it "returns the method in the current context" do
|
73
|
-
allow_all_methods(presenter)
|
74
|
-
allow(presenter).to receive(:method).and_return(:method)
|
75
|
-
|
76
|
-
expect(rendering.path('method', rendering.position(0, 1))).to eq :method
|
77
|
-
end
|
78
|
-
|
79
|
-
it "returns the sub presenter method in the current context" do
|
80
|
-
sub = double(:sub_presenter)
|
81
|
-
allow_all_methods(sub)
|
82
|
-
allow(sub).to receive(:method).and_return(:method)
|
83
|
-
|
84
|
-
allow_all_methods(presenter)
|
85
|
-
allow(presenter).to receive(:sub) { sub }
|
86
|
-
|
87
|
-
expect(rendering.path('sub.method', rendering.position(0, 1))).to eq :method
|
88
|
-
end
|
89
|
-
|
90
|
-
it "returns the length of a collection, when `lenght` is the last step" do
|
91
|
-
allow_all_methods(presenter)
|
92
|
-
single_element_presenter = double(:single_element_presenter)
|
93
|
-
allow_all_methods(single_element_presenter)
|
94
|
-
collection = [single_element_presenter]
|
95
|
-
allow(presenter).to receive(:collection) { collection }
|
96
|
-
|
97
|
-
returned_method = rendering.path('collection.length', rendering.position(0, 1))
|
98
|
-
expect(returned_method.call).to eq collection.length
|
99
|
-
end
|
100
|
-
|
101
|
-
it "raises an exception when the method is not allowed" do
|
102
|
-
disallow_all_methods(presenter)
|
103
|
-
allow(presenter).to receive(:forbidden_method).and_return(:forbidden_method)
|
104
|
-
|
105
|
-
expect do
|
106
|
-
rendering.path('forbidden_method', rendering.position(0, 1))
|
107
|
-
end.to raise_error(Curlybars::Error::Render)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "exposes the unallowed method in the exception payload" do
|
111
|
-
disallow_all_methods(presenter)
|
112
|
-
allow(presenter).to receive(:forbidden_method).and_return(:forbidden_method)
|
113
|
-
|
114
|
-
begin
|
115
|
-
rendering.path('forbidden_method', rendering.position(0, 1))
|
116
|
-
rescue Curlybars::Error::Render => e
|
117
|
-
expect(e.metadata).to eq(meth: :forbidden_method)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
it "raises an exception when the context is not a presenter" do
|
122
|
-
sub = double(:not_presenter)
|
123
|
-
allow(presenter).to receive(:sub) { sub }
|
124
|
-
|
125
|
-
expect do
|
126
|
-
rendering.path('sub.method', rendering.position(0, 1))
|
127
|
-
end.to raise_error(Curlybars::Error::Render)
|
128
|
-
end
|
129
|
-
|
130
|
-
it "refers to the second to last presenter in the stack when using `../`" do
|
131
|
-
sub = double(:sub_presenter)
|
132
|
-
allow_all_methods(sub)
|
133
|
-
allow(sub).to receive(:method).and_return(:sub_method)
|
134
|
-
|
135
|
-
allow_all_methods(presenter)
|
136
|
-
allow(presenter).to receive(:method).and_return(:root_method)
|
137
|
-
|
138
|
-
contexts.push(sub)
|
139
|
-
|
140
|
-
expect(rendering.path('../method', rendering.position(0, 1))).to eq :root_method
|
141
|
-
end
|
142
|
-
|
143
|
-
it "refers to the third to last presenter in the stack when using `../../`" do
|
144
|
-
sub_sub = double(:sub_presenter)
|
145
|
-
allow_all_methods(sub_sub)
|
146
|
-
allow(sub_sub).to receive(:method).and_return(:sub_sub_method)
|
147
|
-
|
148
|
-
sub = double(:sub_presenter)
|
149
|
-
allow_all_methods(sub)
|
150
|
-
allow(sub).to receive(:method).and_return(:sub_method)
|
151
|
-
|
152
|
-
allow_all_methods(presenter)
|
153
|
-
allow(presenter).to receive(:method).and_return(:root_method)
|
154
|
-
|
155
|
-
contexts.push(sub)
|
156
|
-
contexts.push(sub_sub)
|
157
|
-
|
158
|
-
expect(rendering.path('../../method', rendering.position(0, 1))).to eq :root_method
|
159
|
-
end
|
160
|
-
|
161
|
-
it "returns a method that returns nil, if nil is returned from any method in the chain (except the latter)" do
|
162
|
-
allow_all_methods(presenter)
|
163
|
-
allow(presenter).to receive(:returns_nil).and_return(nil)
|
164
|
-
|
165
|
-
outcome = rendering.path('returns_nil.another_method', rendering.position(0, 1)).call
|
166
|
-
expect(outcome).to be_nil
|
167
|
-
end
|
168
|
-
|
169
|
-
it "returns a method that returns nil, if `../`` goes too deep in the stack" do
|
170
|
-
outcome = rendering.path('../too_deep', rendering.position(0, 1)).call
|
171
|
-
expect(outcome).to be_nil
|
172
|
-
end
|
173
|
-
|
174
|
-
it "raises an exception if tha path is too deep (> 10)" do
|
175
|
-
expect do
|
176
|
-
rendering.path('a.b.c.d.e.f.g.h.i.l', rendering.position(0, 1))
|
177
|
-
end.to raise_error(Curlybars::Error::Render)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe "#cached_call" do
|
182
|
-
it "(cache miss) calls the method if not cached already" do
|
183
|
-
meth = presenter.method(:meth)
|
184
|
-
allow(meth).to receive(:call)
|
185
|
-
|
186
|
-
rendering.cached_call(meth)
|
187
|
-
|
188
|
-
expect(meth).to have_received(:call).once
|
189
|
-
end
|
190
|
-
|
191
|
-
it "(cache hit) avoids to call a method for more than one time" do
|
192
|
-
meth = presenter.method(:meth)
|
193
|
-
allow(meth).to receive(:call)
|
194
|
-
|
195
|
-
rendering.cached_call(meth)
|
196
|
-
rendering.cached_call(meth)
|
197
|
-
|
198
|
-
expect(meth).to have_received(:call).once
|
199
|
-
end
|
200
|
-
|
201
|
-
it "the returned cached value is the same as the uncached one" do
|
202
|
-
meth = presenter.method(:meth)
|
203
|
-
|
204
|
-
first_outcome = rendering.cached_call(meth)
|
205
|
-
second_outcome = rendering.cached_call(meth)
|
206
|
-
|
207
|
-
expect(second_outcome).to eq first_outcome
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
describe "#call" do
|
212
|
-
let(:block) { -> {} }
|
213
|
-
|
214
|
-
it "calls with no arguments a method with no parameters" do
|
215
|
-
method = -> { :return }
|
216
|
-
arguments = []
|
217
|
-
|
218
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
219
|
-
expect(output).to eq :return
|
220
|
-
end
|
221
|
-
|
222
|
-
it "calls with one argument a method with no parameters, discarding the parameter" do
|
223
|
-
method = -> { :return }
|
224
|
-
arguments = [:argument]
|
225
|
-
|
226
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
227
|
-
expect(output).to eq :return
|
228
|
-
end
|
229
|
-
|
230
|
-
it "calls a method with only one parameter can only receive the options" do
|
231
|
-
method = ->(parameter) { parameter }
|
232
|
-
arguments = []
|
233
|
-
|
234
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
235
|
-
expect(output).to eq :options
|
236
|
-
end
|
237
|
-
|
238
|
-
it "calls a method with only one parameter can only receive the options, even with some arguments" do
|
239
|
-
method = ->(parameter) { parameter }
|
240
|
-
arguments = [:argument]
|
241
|
-
|
242
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
243
|
-
expect(output).to eq :options
|
244
|
-
end
|
245
|
-
|
246
|
-
it "calls a method with two parameter can receive nil as first argument and the options" do
|
247
|
-
method = ->(parameter, options) { [parameter, options] }
|
248
|
-
arguments = []
|
249
|
-
|
250
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
251
|
-
expect(output).to eq [nil, :options]
|
252
|
-
end
|
253
|
-
|
254
|
-
it "calls a method with two parameter can receive an argument and the options" do
|
255
|
-
method = ->(parameter, options) { [parameter, options] }
|
256
|
-
arguments = [:argument]
|
257
|
-
|
258
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
259
|
-
expect(output).to eq [:argument, :options]
|
260
|
-
end
|
261
|
-
|
262
|
-
it "calls a method with three parameter can receive two arguments and the options" do
|
263
|
-
method = ->(first, second, options) { [first, second, options] }
|
264
|
-
arguments = [:first, :second]
|
265
|
-
|
266
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
267
|
-
expect(output).to eq [:first, :second, :options]
|
268
|
-
end
|
269
|
-
|
270
|
-
it "calls a method with three parameter can receive one argument, nil and the options" do
|
271
|
-
method = ->(first, second, options) { [first, second, options] }
|
272
|
-
arguments = [:first]
|
273
|
-
|
274
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
275
|
-
expect(output).to eq [:first, nil, :options]
|
276
|
-
end
|
277
|
-
|
278
|
-
it "calls a method with three parameter can receive nil, nil and the options" do
|
279
|
-
method = ->(first, second, options) { [first, second, options] }
|
280
|
-
arguments = []
|
281
|
-
|
282
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
283
|
-
expect(output).to eq [nil, nil, :options]
|
284
|
-
end
|
285
|
-
|
286
|
-
it "calls a method passing an array as argument" do
|
287
|
-
method = ->(parameter, _) { parameter }
|
288
|
-
array = [1, 2, 3]
|
289
|
-
arguments = [array]
|
290
|
-
|
291
|
-
output = rendering.call(method, "method", position, arguments, :options, &block)
|
292
|
-
expect(output).to eq arguments.first
|
293
|
-
end
|
294
|
-
|
295
|
-
it "raises Curlybars::Error::Render if the helper has at least an optional parameter" do
|
296
|
-
method = ->(one, two = :optional) {}
|
297
|
-
arguments = [:arg1]
|
298
|
-
options = { key: :value }
|
299
|
-
|
300
|
-
expect do
|
301
|
-
rendering.call(method, "method", position, arguments, options, &block)
|
302
|
-
end.to raise_error(Curlybars::Error::Render)
|
303
|
-
end
|
304
|
-
|
305
|
-
it "raises Curlybars::Error::Render if the helper has at least a keyword parameter" do
|
306
|
-
method = ->(keyword:) {}
|
307
|
-
arguments = [:arg1]
|
308
|
-
options = { key: :value }
|
309
|
-
|
310
|
-
expect do
|
311
|
-
rendering.call(method, "method", position, arguments, options, &block)
|
312
|
-
end.to raise_error(Curlybars::Error::Render)
|
313
|
-
end
|
314
|
-
|
315
|
-
it "raises Curlybars::Error::Render if the helper has at least an optional keyword parameter" do
|
316
|
-
method = ->(keyword: :optional) {}
|
317
|
-
arguments = [:arg1]
|
318
|
-
options = { key: :value }
|
319
|
-
|
320
|
-
expect do
|
321
|
-
rendering.call(method, "meth", position, arguments, options, &block)
|
322
|
-
end.to raise_error(Curlybars::Error::Render)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
describe "#position" do
|
327
|
-
it "returns a position with file_name" do
|
328
|
-
position = rendering.position(0, 0)
|
329
|
-
expect(position.file_name).to eq file_name
|
330
|
-
end
|
331
|
-
|
332
|
-
it "returns a position with line_number" do
|
333
|
-
position = rendering.position(1, 0)
|
334
|
-
expect(position.line_number).to eq 1
|
335
|
-
end
|
336
|
-
|
337
|
-
it "returns a position with line_offset" do
|
338
|
-
position = rendering.position(0, 1)
|
339
|
-
expect(position.line_offset).to eq 1
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
describe "#check_context_is_hash_or_enum_of_presenters" do
|
344
|
-
it "doesn't raise an exception when argument is an empty enumerable" do
|
345
|
-
collection = []
|
346
|
-
rendering.check_context_is_hash_or_enum_of_presenters(collection, 'path', position)
|
347
|
-
end
|
348
|
-
|
349
|
-
it "doesn't raise an exception when argument is an empty hash" do
|
350
|
-
collection = {}
|
351
|
-
rendering.check_context_is_hash_or_enum_of_presenters(collection, nil, position)
|
352
|
-
end
|
353
|
-
|
354
|
-
it "doesn't raise an exception when argument is an enumerable of presenters" do
|
355
|
-
collection = [presenter]
|
356
|
-
rendering.check_context_is_hash_or_enum_of_presenters(collection, 'path', position)
|
357
|
-
end
|
358
|
-
|
359
|
-
it "doesn't raise an exception when argument is a hash of presenters" do
|
360
|
-
collection = { presenter: presenter }
|
361
|
-
rendering.check_context_is_hash_or_enum_of_presenters(collection, 'path', position)
|
362
|
-
end
|
363
|
-
|
364
|
-
it "raises when it is not an hash or an enumerable" do
|
365
|
-
expect do
|
366
|
-
rendering.check_context_is_hash_or_enum_of_presenters(:not_a_presenter, 'path', position)
|
367
|
-
end.to raise_error(Curlybars::Error::Render)
|
368
|
-
end
|
369
|
-
|
370
|
-
it "raises when it is not an hash or an enumerable of presenters" do
|
371
|
-
expect do
|
372
|
-
rendering.check_context_is_hash_or_enum_of_presenters([:not_a_presenter], 'path', position)
|
373
|
-
end.to raise_error(Curlybars::Error::Render)
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
describe "#check_context_is_presenter" do
|
378
|
-
it "doesn't raise an exception when argument is a presenter" do
|
379
|
-
rendering.check_context_is_presenter(presenter, 'path', position)
|
380
|
-
end
|
381
|
-
|
382
|
-
it "raises when it is not a presenter" do
|
383
|
-
expect do
|
384
|
-
rendering.check_context_is_presenter(:not_a_presenter, 'path', position)
|
385
|
-
end.to raise_error(Curlybars::Error::Render)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
describe "#coerce_to_hash!" do
|
390
|
-
let(:a_presenter) { double(:a_presenter, allows_method?: true, meth: :value) }
|
391
|
-
let(:another_presenter) { double(:another_presenter, allows_method?: true, meth: :value) }
|
392
|
-
|
393
|
-
it "leaves hashes intacted" do
|
394
|
-
hash = { first: a_presenter }
|
395
|
-
expect(rendering.coerce_to_hash!(hash, 'path', position)).to be hash
|
396
|
-
end
|
397
|
-
|
398
|
-
it "transform an Array to a Hash" do
|
399
|
-
array = [a_presenter, another_presenter]
|
400
|
-
expected_hash = { 0 => a_presenter, 1 => another_presenter }
|
401
|
-
expect(rendering.coerce_to_hash!(array, 'path', position)).to eq expected_hash
|
402
|
-
end
|
403
|
-
|
404
|
-
it "raises when it is not a hash or an enumerable" do
|
405
|
-
expect do
|
406
|
-
rendering.coerce_to_hash!(:not_a_presenter, 'path', position)
|
407
|
-
end.to raise_error(Curlybars::Error::Render)
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
private
|
412
|
-
|
413
|
-
def allow_all_methods(presenter)
|
414
|
-
allow(presenter).to receive(:allows_method?).and_return(true)
|
415
|
-
end
|
416
|
-
|
417
|
-
def disallow_all_methods(presenter)
|
418
|
-
allow(presenter).to receive(:allows_method?).and_return(false)
|
419
|
-
end
|
420
|
-
end
|
421
|
-
# rubocop:enable RSpec/MultipleMemoizedHelpers
|
@@ -1,46 +0,0 @@
|
|
1
|
-
describe Curlybars::SafeBuffer do
|
2
|
-
let(:configuration) { double(:configuration) }
|
3
|
-
|
4
|
-
before do
|
5
|
-
allow(Curlybars).to receive(:configuration) { configuration }
|
6
|
-
end
|
7
|
-
|
8
|
-
describe '#is_a?' do
|
9
|
-
it "is a String" do
|
10
|
-
expect(Curlybars::SafeBuffer.new).to be_a(String)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "is a ActiveSupport::SafeBuffer" do
|
14
|
-
expect(Curlybars::SafeBuffer.new).to be_a(ActiveSupport::SafeBuffer)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "#concat" do
|
19
|
-
it "accepts when (buffer length + the existing output lenght) <= output_limit" do
|
20
|
-
allow(configuration).to receive(:output_limit).and_return(10)
|
21
|
-
|
22
|
-
buffer = Curlybars::SafeBuffer.new('*' * 5)
|
23
|
-
|
24
|
-
expect do
|
25
|
-
buffer.concat('*' * 5)
|
26
|
-
end.not_to raise_error
|
27
|
-
end
|
28
|
-
|
29
|
-
it "raises when (buffer length + the existing output lenght) > output_limit" do
|
30
|
-
allow(configuration).to receive(:output_limit).and_return(10)
|
31
|
-
buffer = Curlybars::SafeBuffer.new('*' * 10)
|
32
|
-
|
33
|
-
expect do
|
34
|
-
buffer.concat('*')
|
35
|
-
end.to raise_error(Curlybars::Error::Render)
|
36
|
-
end
|
37
|
-
|
38
|
-
it "raises when buffer length > output_limit" do
|
39
|
-
allow(configuration).to receive(:output_limit).and_return(10)
|
40
|
-
|
41
|
-
expect do
|
42
|
-
Curlybars::SafeBuffer.new.concat('*' * 11)
|
43
|
-
end.to raise_error(Curlybars::Error::Render)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,225 +0,0 @@
|
|
1
|
-
describe Curlybars::TemplateHandler do
|
2
|
-
let :presenter_class do
|
3
|
-
Class.new do
|
4
|
-
def initialize(context, options = {})
|
5
|
-
@context = context
|
6
|
-
@cache_key = options.fetch(:cache_key, nil)
|
7
|
-
@cache_duration = options.fetch(:cache_duration, nil)
|
8
|
-
@cache_options = options.fetch(:cache_options, {})
|
9
|
-
end
|
10
|
-
|
11
|
-
def setup!
|
12
|
-
@context.content_for(:foo, "bar")
|
13
|
-
end
|
14
|
-
|
15
|
-
def foo
|
16
|
-
"FOO"
|
17
|
-
end
|
18
|
-
|
19
|
-
def bar
|
20
|
-
@context.bar
|
21
|
-
end
|
22
|
-
|
23
|
-
def cache_key
|
24
|
-
@cache_key
|
25
|
-
end
|
26
|
-
|
27
|
-
def cache_duration
|
28
|
-
@cache_duration
|
29
|
-
end
|
30
|
-
|
31
|
-
def cache_options
|
32
|
-
@cache_options
|
33
|
-
end
|
34
|
-
|
35
|
-
def allows_method?(method)
|
36
|
-
true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
let :context_class do
|
42
|
-
Class.new do
|
43
|
-
attr_reader :output_buffer
|
44
|
-
attr_reader :local_assigns, :assigns
|
45
|
-
|
46
|
-
def initialize
|
47
|
-
@cache = Hash.new
|
48
|
-
@local_assigns = Hash.new
|
49
|
-
@assigns = Hash.new
|
50
|
-
@clock = 0
|
51
|
-
end
|
52
|
-
|
53
|
-
def reset!
|
54
|
-
@output_buffer = ActiveSupport::SafeBuffer.new
|
55
|
-
end
|
56
|
-
|
57
|
-
def advance_clock(duration)
|
58
|
-
@clock += duration
|
59
|
-
end
|
60
|
-
|
61
|
-
def content_for(key, value = nil)
|
62
|
-
@contents ||= {}
|
63
|
-
@contents[key] = value if value.present?
|
64
|
-
@contents[key]
|
65
|
-
end
|
66
|
-
|
67
|
-
def cache(key, options = {})
|
68
|
-
fragment, expired_at = @cache[key]
|
69
|
-
|
70
|
-
if fragment.nil? || @clock >= expired_at
|
71
|
-
old_buffer = @output_buffer
|
72
|
-
@output_buffer = ActiveSupport::SafeBuffer.new
|
73
|
-
|
74
|
-
yield
|
75
|
-
|
76
|
-
fragment = @output_buffer.to_s
|
77
|
-
duration = options[:expires_in] || Float::INFINITY
|
78
|
-
|
79
|
-
@cache[key] = [fragment, @clock + duration]
|
80
|
-
|
81
|
-
@output_buffer = old_buffer
|
82
|
-
end
|
83
|
-
|
84
|
-
safe_concat(fragment)
|
85
|
-
|
86
|
-
nil
|
87
|
-
end
|
88
|
-
|
89
|
-
def safe_concat(str)
|
90
|
-
@output_buffer.safe_concat(str)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
let(:template) { double("template", virtual_path: "test", identifier: "test.hbs") }
|
96
|
-
let(:context) { context_class.new }
|
97
|
-
|
98
|
-
before do
|
99
|
-
stub_const("TestPresenter", presenter_class)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "strips the `# encoding: *` directive away from the template" do
|
103
|
-
output = render(<<~TEMPLATE)
|
104
|
-
# encoding: utf-8"
|
105
|
-
first line
|
106
|
-
TEMPLATE
|
107
|
-
expect(output).to eq(<<~TEMPLATE)
|
108
|
-
|
109
|
-
first line
|
110
|
-
TEMPLATE
|
111
|
-
end
|
112
|
-
|
113
|
-
it "passes in the presenter context to the presenter class" do
|
114
|
-
allow(context).to receive(:bar).and_return("BAR")
|
115
|
-
output = render("{{bar}}")
|
116
|
-
expect(output).to eq("BAR")
|
117
|
-
end
|
118
|
-
|
119
|
-
it "fails if there's no matching presenter class" do
|
120
|
-
allow(template).to receive(:virtual_path).and_return("missing")
|
121
|
-
expect { render(" FOO ") }.to raise_exception(Curlybars::Error::Presenter::NotFound)
|
122
|
-
end
|
123
|
-
|
124
|
-
it "allows calling public methods on the presenter" do
|
125
|
-
output = render("{{foo}}")
|
126
|
-
expect(output).to eq("FOO")
|
127
|
-
end
|
128
|
-
|
129
|
-
it "marks its output as HTML safe" do
|
130
|
-
output = render("{{foo}}")
|
131
|
-
expect(output).to be_html_safe
|
132
|
-
end
|
133
|
-
|
134
|
-
it "calls the #setup! method before rendering the view" do
|
135
|
-
render("{{foo}}")
|
136
|
-
expect(context.content_for(:foo)).to eq("bar")
|
137
|
-
end
|
138
|
-
|
139
|
-
describe "caching" do
|
140
|
-
before do
|
141
|
-
allow(context).to receive(:bar).and_return("BAR")
|
142
|
-
end
|
143
|
-
|
144
|
-
let(:output) { -> { render("{{bar}}") } }
|
145
|
-
|
146
|
-
it "caches the result with the #cache_key from the presenter" do
|
147
|
-
context.assigns[:cache_key] = "x"
|
148
|
-
expect(output.call).to eq("BAR")
|
149
|
-
|
150
|
-
allow(context).to receive(:bar).and_return("BAZ")
|
151
|
-
expect(output.call).to eq("BAR")
|
152
|
-
|
153
|
-
context.assigns[:cache_key] = "y"
|
154
|
-
expect(output.call).to eq("BAZ")
|
155
|
-
end
|
156
|
-
|
157
|
-
it "doesn't cache when the cache key is nil" do
|
158
|
-
context.assigns[:cache_key] = nil
|
159
|
-
expect(output.call).to eq("BAR")
|
160
|
-
|
161
|
-
allow(context).to receive(:bar).and_return("BAZ")
|
162
|
-
expect(output.call).to eq("BAZ")
|
163
|
-
end
|
164
|
-
|
165
|
-
it "adds the presenter class' cache key to the instance's cache key" do
|
166
|
-
# Make sure caching is enabled
|
167
|
-
context.assigns[:cache_key] = "x"
|
168
|
-
|
169
|
-
allow(presenter_class).to receive(:cache_key).and_return("foo")
|
170
|
-
|
171
|
-
expect(output.call).to eq("BAR")
|
172
|
-
|
173
|
-
allow(presenter_class).to receive(:cache_key).and_return("bar")
|
174
|
-
|
175
|
-
allow(context).to receive(:bar).and_return("FOOBAR")
|
176
|
-
expect(output.call).to eq("FOOBAR")
|
177
|
-
end
|
178
|
-
|
179
|
-
it "expires the cache keys after #cache_duration" do
|
180
|
-
context.assigns[:cache_key] = "x"
|
181
|
-
context.assigns[:cache_duration] = 42
|
182
|
-
|
183
|
-
expect(output.call).to eq("BAR")
|
184
|
-
|
185
|
-
allow(context).to receive(:bar).and_return("FOO")
|
186
|
-
|
187
|
-
# Cached fragment has not yet expired.
|
188
|
-
context.advance_clock(41)
|
189
|
-
expect(output.call).to eq("BAR")
|
190
|
-
|
191
|
-
# Now it has! Huzzah!
|
192
|
-
context.advance_clock(1)
|
193
|
-
expect(output.call).to eq("FOO")
|
194
|
-
end
|
195
|
-
|
196
|
-
it "passes #cache_options to the cache backend" do
|
197
|
-
context.assigns[:cache_key] = "x"
|
198
|
-
context.assigns[:cache_options] = { expires_in: 42 }
|
199
|
-
|
200
|
-
expect(output.call).to eq("BAR")
|
201
|
-
|
202
|
-
allow(context).to receive(:bar).and_return("FOO")
|
203
|
-
|
204
|
-
# Cached fragment has not yet expired.
|
205
|
-
context.advance_clock(41)
|
206
|
-
expect(output.call).to eq("BAR")
|
207
|
-
|
208
|
-
# Now it has! Huzzah!
|
209
|
-
context.advance_clock(1)
|
210
|
-
expect(output.call).to eq("FOO")
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
def render(source)
|
215
|
-
if ActionView::VERSION::MAJOR < 6
|
216
|
-
allow(template).to receive(:source).and_return(source)
|
217
|
-
code = Curlybars::TemplateHandler.call(template)
|
218
|
-
else
|
219
|
-
code = Curlybars::TemplateHandler.call(template, source)
|
220
|
-
end
|
221
|
-
|
222
|
-
context.reset!
|
223
|
-
context.instance_eval(code)
|
224
|
-
end
|
225
|
-
end
|