mvz-ruby-handlebars 0.0.8 → 0.0.9
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/CHANGELOG.md +10 -0
- data/README.md +59 -17
- data/lib/ruby-handlebars.rb +21 -38
- data/lib/ruby-handlebars/context.rb +30 -4
- data/lib/ruby-handlebars/escapers/dummy_escaper.rb +9 -0
- data/lib/ruby-handlebars/escapers/html_escaper.rb +9 -0
- data/lib/ruby-handlebars/helper.rb +23 -19
- data/lib/ruby-handlebars/helpers/default_helper.rb +28 -0
- data/lib/ruby-handlebars/helpers/each_helper.rb +31 -0
- data/lib/ruby-handlebars/helpers/helper_missing_helper.rb +18 -0
- data/lib/ruby-handlebars/helpers/if_helper.rb +23 -0
- data/lib/ruby-handlebars/helpers/register_default_helpers.rb +15 -0
- data/lib/ruby-handlebars/helpers/unless_helper.rb +21 -0
- data/lib/ruby-handlebars/parser.rb +53 -7
- data/lib/ruby-handlebars/tree.rb +99 -11
- data/lib/ruby-handlebars/version.rb +1 -1
- data/spec/handlebars_spec.rb +143 -193
- data/spec/parser_spec.rb +162 -21
- data/spec/ruby-handlebars/context_spec.rb +145 -0
- data/spec/ruby-handlebars/helpers/each_helper_spec.rb +331 -0
- data/spec/ruby-handlebars/helpers/helper_missing_helper_spec.rb +45 -0
- data/spec/ruby-handlebars/helpers/if_helper_spec.rb +90 -0
- data/spec/ruby-handlebars/helpers/register_default_helpers_spec.rb +34 -0
- data/spec/ruby-handlebars/helpers/shared.rb +55 -0
- data/spec/ruby-handlebars/helpers/unless_helper_spec.rb +65 -0
- data/spec/spec_helper.rb +5 -0
- metadata +36 -6
@@ -0,0 +1,331 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative './shared'
|
3
|
+
|
4
|
+
require_relative '../../../lib/ruby-handlebars'
|
5
|
+
require_relative '../../../lib/ruby-handlebars/tree'
|
6
|
+
require_relative '../../../lib/ruby-handlebars/helpers/each_helper'
|
7
|
+
|
8
|
+
|
9
|
+
describe Handlebars::Helpers::EachHelper do
|
10
|
+
let(:subject) { Handlebars::Helpers::EachHelper }
|
11
|
+
let(:hbs) {Handlebars::Handlebars.new}
|
12
|
+
let(:ctx) {Handlebars::Context.new(hbs, {})}
|
13
|
+
|
14
|
+
it_behaves_like "a registerable helper", "each"
|
15
|
+
|
16
|
+
context '.apply' do
|
17
|
+
include_context "shared apply helper"
|
18
|
+
|
19
|
+
let(:values) { [Handlebars::Tree::String.new('a'), Handlebars::Tree::String.new('b'), Handlebars::Tree::String.new('c') ]}
|
20
|
+
|
21
|
+
it 'applies the block on all values' do
|
22
|
+
subject.apply(ctx, values, block, else_block)
|
23
|
+
|
24
|
+
expect(block).to have_received(:fn).exactly(3).times
|
25
|
+
expect(else_block).not_to have_received(:fn)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when values is nil' do
|
29
|
+
let(:values) { nil }
|
30
|
+
|
31
|
+
it 'uses the else_block if provided' do
|
32
|
+
subject.apply(ctx, values, block, else_block)
|
33
|
+
|
34
|
+
expect(block).not_to have_received(:fn)
|
35
|
+
expect(else_block).to have_received(:fn).once
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns nil if no else_block is provided' do
|
39
|
+
expect(subject.apply(ctx, values, block, nil)).to be nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when values is empty' do
|
44
|
+
let(:values) { [] }
|
45
|
+
|
46
|
+
it 'uses the else_block if provided' do
|
47
|
+
subject.apply(ctx, values, block, else_block)
|
48
|
+
|
49
|
+
expect(block).not_to have_received(:fn)
|
50
|
+
expect(else_block).to have_received(:fn).once
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns nil if no else_block is provided' do
|
54
|
+
expect(subject.apply(ctx, values, block, nil)).to be nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'integration' do
|
60
|
+
include_context "shared helpers integration tests"
|
61
|
+
|
62
|
+
let(:ducks) {[{name: 'Huey'}, {name: 'Dewey'}, {name: 'Louis'}]}
|
63
|
+
|
64
|
+
it 'simple case' do
|
65
|
+
template = [
|
66
|
+
"<ul>",
|
67
|
+
"{{#each items}} <li>{{this.name}}</li>",
|
68
|
+
"{{/each}}</ul>"
|
69
|
+
].join("\n")
|
70
|
+
|
71
|
+
data = {items: ducks}
|
72
|
+
expect(evaluate(template, data)).to eq([
|
73
|
+
"<ul>",
|
74
|
+
" <li>Huey</li>",
|
75
|
+
" <li>Dewey</li>",
|
76
|
+
" <li>Louis</li>",
|
77
|
+
"</ul>"
|
78
|
+
].join("\n"))
|
79
|
+
|
80
|
+
data = {items: []}
|
81
|
+
expect(evaluate(template, data)).to eq([
|
82
|
+
"<ul>",
|
83
|
+
"</ul>"
|
84
|
+
].join("\n"))
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'considers not found items as an empty list and does not raise an error' do
|
88
|
+
template = [
|
89
|
+
"<ul>",
|
90
|
+
"{{#each stuff}} <li>{{this.name}}</li>",
|
91
|
+
"{{/each}}</ul>"
|
92
|
+
].join("\n")
|
93
|
+
|
94
|
+
expect(evaluate(template, {})).to eq([
|
95
|
+
"<ul>",
|
96
|
+
"</ul>"
|
97
|
+
].join("\n"))
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'considers not found items as an empty list and uses else block if provided' do
|
101
|
+
template = [
|
102
|
+
"<ul>",
|
103
|
+
"{{#each stuff}} <li>{{this.name}}</li>",
|
104
|
+
"{{else}} <li>No stuff found....</li>",
|
105
|
+
"{{/each}}</ul>"
|
106
|
+
].join("\n")
|
107
|
+
|
108
|
+
expect(evaluate(template, {})).to eq([
|
109
|
+
"<ul>",
|
110
|
+
" <li>No stuff found....</li>",
|
111
|
+
"</ul>"
|
112
|
+
].join("\n"))
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'works with non-hash data' do
|
116
|
+
template = [
|
117
|
+
"<ul>",
|
118
|
+
"{{#each items}} <li>{{this.name}}</li>",
|
119
|
+
"{{/each}}</ul>"
|
120
|
+
].join("\n")
|
121
|
+
|
122
|
+
data = double(items: ducks)
|
123
|
+
expect(evaluate(template, data)).to eq([
|
124
|
+
"<ul>",
|
125
|
+
" <li>Huey</li>",
|
126
|
+
" <li>Dewey</li>",
|
127
|
+
" <li>Louis</li>",
|
128
|
+
"</ul>"
|
129
|
+
].join("\n"))
|
130
|
+
|
131
|
+
data = {items: []}
|
132
|
+
expect(evaluate(template, data)).to eq([
|
133
|
+
"<ul>",
|
134
|
+
"</ul>"
|
135
|
+
].join("\n"))
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'using an else statement' do
|
139
|
+
template = [
|
140
|
+
"<ul>",
|
141
|
+
"{{#each items}} <li>{{this.name}}</li>",
|
142
|
+
"{{else}} <li>No ducks to display</li>",
|
143
|
+
"{{/each}}</ul>"
|
144
|
+
].join("\n")
|
145
|
+
|
146
|
+
data = {items: ducks}
|
147
|
+
expect(evaluate(template, data)).to eq([
|
148
|
+
"<ul>",
|
149
|
+
" <li>Huey</li>",
|
150
|
+
" <li>Dewey</li>",
|
151
|
+
" <li>Louis</li>",
|
152
|
+
"</ul>"
|
153
|
+
].join("\n"))
|
154
|
+
|
155
|
+
data = {items: []}
|
156
|
+
expect(evaluate(template, data)).to eq([
|
157
|
+
"<ul>",
|
158
|
+
" <li>No ducks to display</li>",
|
159
|
+
"</ul>"
|
160
|
+
].join("\n"))
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'imbricated' do
|
164
|
+
data = {people: [
|
165
|
+
{
|
166
|
+
name: 'Huey',
|
167
|
+
email: 'huey@junior-woodchucks.example.com',
|
168
|
+
phones: ['1234', '5678'],
|
169
|
+
},
|
170
|
+
{
|
171
|
+
name: 'Dewey',
|
172
|
+
email: 'dewey@junior-woodchucks.example.com',
|
173
|
+
phones: ['4321'],
|
174
|
+
}
|
175
|
+
]}
|
176
|
+
|
177
|
+
template = [
|
178
|
+
"People:",
|
179
|
+
"<ul>",
|
180
|
+
" {{#each people}}",
|
181
|
+
" <li>",
|
182
|
+
" <ul>",
|
183
|
+
" <li>Name: {{this.name}}</li>",
|
184
|
+
" <li>Phones: {{#each this.phones}} {{this}} {{/each}}</li>",
|
185
|
+
" <li>email: {{this.email}}</li>",
|
186
|
+
" </ul>",
|
187
|
+
" </li>",
|
188
|
+
" {{else}}",
|
189
|
+
" <li>No one to display</li>",
|
190
|
+
" {{/each}}",
|
191
|
+
"</ul>"
|
192
|
+
].join("\n")
|
193
|
+
|
194
|
+
expect(evaluate(template, data)).to eq([
|
195
|
+
"People:",
|
196
|
+
"<ul>",
|
197
|
+
" ",
|
198
|
+
" <li>",
|
199
|
+
" <ul>",
|
200
|
+
" <li>Name: Huey</li>",
|
201
|
+
" <li>Phones: 1234 5678 </li>",
|
202
|
+
" <li>email: huey@junior-woodchucks.example.com</li>",
|
203
|
+
" </ul>",
|
204
|
+
" </li>",
|
205
|
+
" ",
|
206
|
+
" <li>",
|
207
|
+
" <ul>",
|
208
|
+
" <li>Name: Dewey</li>",
|
209
|
+
" <li>Phones: 4321 </li>",
|
210
|
+
" <li>email: dewey@junior-woodchucks.example.com</li>",
|
211
|
+
" </ul>",
|
212
|
+
" </li>",
|
213
|
+
" ",
|
214
|
+
"</ul>"
|
215
|
+
].join("\n"))
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'special variables' do
|
219
|
+
it '@first' do
|
220
|
+
template = [
|
221
|
+
"{{#each items}}",
|
222
|
+
"{{this}}",
|
223
|
+
"{{#if @first}}",
|
224
|
+
" first",
|
225
|
+
"{{/if}}\n",
|
226
|
+
"{{/each}}"
|
227
|
+
].join
|
228
|
+
expect(evaluate(template, {items: %w(a b c)})).to eq("a first\nb\nc\n")
|
229
|
+
end
|
230
|
+
|
231
|
+
it '@last' do
|
232
|
+
template = [
|
233
|
+
"{{#each items}}",
|
234
|
+
"{{this}}",
|
235
|
+
"{{#if @last}}",
|
236
|
+
" last",
|
237
|
+
"{{/if}}\n",
|
238
|
+
"{{/each}}"
|
239
|
+
].join
|
240
|
+
expect(evaluate(template, {items: %w(a b c)})).to eq("a\nb\nc last\n")
|
241
|
+
end
|
242
|
+
|
243
|
+
it '@index' do
|
244
|
+
template = [
|
245
|
+
"{{#each items}}",
|
246
|
+
"{{this}} {{@index}}\n",
|
247
|
+
"{{/each}}"
|
248
|
+
].join
|
249
|
+
expect(evaluate(template, {items: %w(a b c)})).to eq("a 0\nb 1\nc 2\n")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'integration with "as |value|" notation' do
|
255
|
+
include_context "shared helpers integration tests"
|
256
|
+
|
257
|
+
let(:ducks) {[{name: 'Huey'}, {name: 'Dewey'}, {name: 'Louis'}]}
|
258
|
+
|
259
|
+
it 'simple case' do
|
260
|
+
template = [
|
261
|
+
"<ul>",
|
262
|
+
"{{#each items as |item|}} <li>{{item.name}}</li>",
|
263
|
+
"{{/each}}</ul>"
|
264
|
+
].join("\n")
|
265
|
+
|
266
|
+
data = {items: ducks}
|
267
|
+
expect(evaluate(template, data)).to eq([
|
268
|
+
"<ul>",
|
269
|
+
" <li>Huey</li>",
|
270
|
+
" <li>Dewey</li>",
|
271
|
+
" <li>Louis</li>",
|
272
|
+
"</ul>"
|
273
|
+
].join("\n"))
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'imbricated' do
|
277
|
+
data = {people: [
|
278
|
+
{
|
279
|
+
name: 'Huey',
|
280
|
+
email: 'huey@junior-woodchucks.example.com',
|
281
|
+
phones: ['1234', '5678'],
|
282
|
+
},
|
283
|
+
{
|
284
|
+
name: 'Dewey',
|
285
|
+
email: 'dewey@junior-woodchucks.example.com',
|
286
|
+
phones: ['4321'],
|
287
|
+
}
|
288
|
+
]}
|
289
|
+
|
290
|
+
template = [
|
291
|
+
"People:",
|
292
|
+
"<ul>",
|
293
|
+
" {{#each people as |person| }}",
|
294
|
+
" <li>",
|
295
|
+
" <ul>",
|
296
|
+
" <li>Name: {{person.name}}</li>",
|
297
|
+
" <li>Phones: {{#each person.phones as |phone|}} {{phone}} {{/each}}</li>",
|
298
|
+
" <li>email: {{person.email}}</li>",
|
299
|
+
" </ul>",
|
300
|
+
" </li>",
|
301
|
+
" {{else}}",
|
302
|
+
" <li>No one to display</li>",
|
303
|
+
" {{/each}}",
|
304
|
+
"</ul>"
|
305
|
+
].join("\n")
|
306
|
+
|
307
|
+
expect(evaluate(template, data)).to eq([
|
308
|
+
"People:",
|
309
|
+
"<ul>",
|
310
|
+
" ",
|
311
|
+
" <li>",
|
312
|
+
" <ul>",
|
313
|
+
" <li>Name: Huey</li>",
|
314
|
+
" <li>Phones: 1234 5678 </li>",
|
315
|
+
" <li>email: huey@junior-woodchucks.example.com</li>",
|
316
|
+
" </ul>",
|
317
|
+
" </li>",
|
318
|
+
" ",
|
319
|
+
" <li>",
|
320
|
+
" <ul>",
|
321
|
+
" <li>Name: Dewey</li>",
|
322
|
+
" <li>Phones: 4321 </li>",
|
323
|
+
" <li>email: dewey@junior-woodchucks.example.com</li>",
|
324
|
+
" </ul>",
|
325
|
+
" </li>",
|
326
|
+
" ",
|
327
|
+
"</ul>"
|
328
|
+
].join("\n"))
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative './shared'
|
3
|
+
|
4
|
+
require_relative '../../../lib/ruby-handlebars'
|
5
|
+
require_relative '../../../lib/ruby-handlebars/tree'
|
6
|
+
require_relative '../../../lib/ruby-handlebars/helpers/helper_missing_helper'
|
7
|
+
|
8
|
+
|
9
|
+
describe Handlebars::Helpers::HelperMissingHelper do
|
10
|
+
let(:subject) { Handlebars::Helpers::HelperMissingHelper }
|
11
|
+
let(:hbs) { Handlebars::Handlebars.new }
|
12
|
+
let(:ctx) {Handlebars::Context.new(hbs, {})}
|
13
|
+
|
14
|
+
it_behaves_like "a registerable helper", "helperMissing"
|
15
|
+
|
16
|
+
context '.apply' do
|
17
|
+
let(:name) { "missing_helper" }
|
18
|
+
|
19
|
+
it 'raises a Handlebars::UnknownHelper exception with the name given as a parameter' do
|
20
|
+
expect { subject.apply(ctx, name, nil, nil) }.to raise_exception(Handlebars::UnknownHelper, "Helper \"#{name}\" does not exist")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'integration' do
|
25
|
+
include_context "shared helpers integration tests"
|
26
|
+
|
27
|
+
context 'is called when an unknown helper is called in a template' do
|
28
|
+
it 'should provide a useful error message with inline helpers' do
|
29
|
+
expect { evaluate('{{unknown "This will hardly work" }}') }.to raise_exception(Handlebars::UnknownHelper, 'Helper "unknown" does not exist')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should provide a useful error message with block helpers' do
|
33
|
+
expect { evaluate('{{#unknown}}This will hardly work{{/unknown}}') }.to raise_exception(Handlebars::UnknownHelper, 'Helper "unknown" does not exist')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can be overriden easily' do
|
38
|
+
hbs.register_helper('helperMissing') do |context, name|
|
39
|
+
# Do nothing
|
40
|
+
end
|
41
|
+
|
42
|
+
expect { evaluate('{{unknown "This will hardly work" }}') }.not_to raise_exception
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative './shared'
|
3
|
+
|
4
|
+
require_relative '../../../lib/ruby-handlebars'
|
5
|
+
require_relative '../../../lib/ruby-handlebars/helpers/if_helper'
|
6
|
+
|
7
|
+
|
8
|
+
describe Handlebars::Helpers::IfHelper do
|
9
|
+
let(:subject) { Handlebars::Helpers::IfHelper }
|
10
|
+
let(:hbs) {Handlebars::Handlebars.new}
|
11
|
+
let(:ctx) {Handlebars::Context.new(hbs, {})}
|
12
|
+
|
13
|
+
it_behaves_like "a registerable helper", "if"
|
14
|
+
|
15
|
+
context '.apply' do
|
16
|
+
it_behaves_like "a helper running the main block", 'true', true
|
17
|
+
it_behaves_like "a helper running the main block", 'a non-empty string', 'something'
|
18
|
+
it_behaves_like "a helper running the main block", 'a non-empty list', ['a']
|
19
|
+
it_behaves_like "a helper running the main block", 'a non-empty hash', {a: 'b'}
|
20
|
+
|
21
|
+
it_behaves_like "a helper running the else block", "false", false
|
22
|
+
it_behaves_like "a helper running the else block", "an empty string", ""
|
23
|
+
it_behaves_like "a helper running the else block", "an empty list", []
|
24
|
+
it_behaves_like "a helper running the else block", "an empty hash", {}
|
25
|
+
|
26
|
+
context 'when else_block is not present' do
|
27
|
+
include_context "shared apply helper"
|
28
|
+
let(:params) { false }
|
29
|
+
let(:else_block) { nil }
|
30
|
+
|
31
|
+
it 'returns an empty-string' do
|
32
|
+
expect(subject.apply(ctx, params, block, else_block)).to eq("")
|
33
|
+
|
34
|
+
expect(block).not_to have_received(:fn)
|
35
|
+
expect(else_block).not_to have_received(:fn)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'integration' do
|
41
|
+
include_context "shared helpers integration tests"
|
42
|
+
|
43
|
+
context 'if' do
|
44
|
+
it 'without else' do
|
45
|
+
template = [
|
46
|
+
"{{#if condition}}",
|
47
|
+
" Show something",
|
48
|
+
"{{/if}}"
|
49
|
+
].join("\n")
|
50
|
+
expect(evaluate(template, {condition: true})).to eq("\n Show something\n")
|
51
|
+
expect(evaluate(template, {condition: false})).to eq("")
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'with an else' do
|
55
|
+
template = [
|
56
|
+
"{{#if condition}}",
|
57
|
+
" Show something",
|
58
|
+
"{{ else }}",
|
59
|
+
" Do not show something",
|
60
|
+
"{{/if}}"
|
61
|
+
].join("\n")
|
62
|
+
expect(evaluate(template, {condition: true})).to eq("\n Show something\n")
|
63
|
+
expect(evaluate(template, {condition: false})).to eq("\n Do not show something\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'imbricated ifs' do
|
67
|
+
template = [
|
68
|
+
"{{#if first_condition}}",
|
69
|
+
" {{#if second_condition}}",
|
70
|
+
" Case 1",
|
71
|
+
" {{else}}",
|
72
|
+
" Case 2",
|
73
|
+
" {{/if}}",
|
74
|
+
"{{else}}",
|
75
|
+
" {{#if second_condition}}",
|
76
|
+
" Case 3",
|
77
|
+
" {{else}}",
|
78
|
+
" Case 4",
|
79
|
+
" {{/if}}",
|
80
|
+
"{{/if}}"
|
81
|
+
].join("\n")
|
82
|
+
|
83
|
+
expect(evaluate(template, {first_condition: true, second_condition: true}).strip).to eq("Case 1")
|
84
|
+
expect(evaluate(template, {first_condition: true, second_condition: false}).strip).to eq("Case 2")
|
85
|
+
expect(evaluate(template, {first_condition: false, second_condition: true}).strip).to eq("Case 3")
|
86
|
+
expect(evaluate(template, {first_condition: false, second_condition: false}).strip).to eq("Case 4")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|