mvz-ruby-handlebars 0.0.7

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.
@@ -0,0 +1,3 @@
1
+ module Handlebars
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1,327 @@
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/ruby-handlebars'
3
+
4
+ describe Handlebars::Handlebars do
5
+ let(:hbs) {Handlebars::Handlebars.new}
6
+
7
+ def evaluate(template, args = {})
8
+ hbs.compile(template).call(args)
9
+ end
10
+
11
+ context 'evaluating' do
12
+ it 'a dummy template' do
13
+ expect(evaluate('My simple template')).to eq('My simple template')
14
+ end
15
+
16
+ it 'a simple replacement' do
17
+ expect(evaluate('Hello {{name}}', {name: 'world'})).to eq('Hello world')
18
+ end
19
+
20
+ it 'allows values specified by methods' do
21
+ expect(evaluate('Hello {{name}}', double(name: 'world'))).to eq('Hello world')
22
+ end
23
+
24
+ it 'prefers hash value over method value' do
25
+ expect(evaluate('Hello {{name}}', double(name: 'world', '[]': 'dog', has_key?: true))).to eq('Hello dog')
26
+ end
27
+
28
+ it 'handles object that implement #[] but not #has_key?' do
29
+ expect(evaluate('Hello {{name}}', double(name: 'world', '[]': 'dog'))).to eq('Hello world')
30
+ end
31
+
32
+ it 'a replacement with a path' do
33
+ expect(evaluate('My simple template: {{person.name}}', {person: {name: 'Another name'}})).to eq('My simple template: Another name')
34
+ end
35
+
36
+ context 'partials' do
37
+ it 'simple' do
38
+ hbs.register_partial('plic', "Plic")
39
+ expect(evaluate("Hello {{> plic}}")).to eq("Hello Plic")
40
+ end
41
+
42
+ it 'using context' do
43
+ hbs.register_partial('brackets', "[{{name}}]")
44
+ expect(evaluate("Hello {{> brackets}}", {name: 'world'})).to eq("Hello [world]")
45
+ end
46
+ end
47
+
48
+ context 'helpers' do
49
+ it 'without any argument' do
50
+ hbs.register_helper('rainbow') {|context| "-"}
51
+ expect(evaluate("{{rainbow}}")).to eq("-")
52
+ end
53
+
54
+ it 'with a single argument' do
55
+ hbs.register_helper('noah') {|context, value| value.gsub(/a/, '')}
56
+
57
+ expect(evaluate("{{noah country}}", {country: 'Canada'})).to eq("Cnd")
58
+ end
59
+
60
+ it 'with multiple arguments, including strings' do
61
+ hbs.register_helper('add') {|context, left, op, right| "#{left} #{op} #{right}"}
62
+
63
+ expect(evaluate("{{add left '&' right}}", {left: 'Law', right: 'Order'})).to eq("Law & Order")
64
+ end
65
+
66
+ it 'with an empty string argument' do
67
+ hbs.register_helper('noah') {|context, value| value.to_s.gsub(/a/, '')}
68
+
69
+ expect(evaluate("hey{{noah ''}}there", {})).to eq("heythere")
70
+ end
71
+
72
+ it 'block' do
73
+ hbs.register_helper('comment') do |context, commenter, block|
74
+ block.fn(context).split("\n").map do |line|
75
+ "#{commenter} #{line}"
76
+ end.join("\n")
77
+
78
+ expect(evaluate([
79
+ "{{comment '//'}}",
80
+ "Author: {{author.name}}, {{author.company}}",
81
+ "Date: {{commit_date}}",
82
+ "{{/comment}}"
83
+ ].join("\n"), {author: {name: 'Vincent', company: 'Hiptest'}, commit_date: 'today'})).to eq([
84
+ "// Author: Vincent, Hiptest",
85
+ "// Date: today"
86
+ ].join("\n"))
87
+ end
88
+ end
89
+
90
+ it 'block without arguments' do
91
+ template = [
92
+ "<tr>{{#indent}}",
93
+ "{{#each items}}<td>{{{ this }}}</td>",
94
+ "{{/each}}",
95
+ "{{/indent}}",
96
+ "</tr>"
97
+ ].join("\n")
98
+
99
+ hbs.register_helper('indent') do |context, block|
100
+ block.fn(context).split("\n").map do |line|
101
+ " #{line}"
102
+ end.join("\n")
103
+ end
104
+
105
+ expect(evaluate(template, {items: ['a', 'b', 'c']})).to eq([
106
+ "<tr> ",
107
+ " <td>a</td>",
108
+ " <td>b</td>",
109
+ " <td>c</td>",
110
+ "</tr>"
111
+ ].join("\n"))
112
+ end
113
+
114
+ it 'block parameters can be paths' do
115
+ data = {company: {people: ['a', 'b', 'c']}}
116
+ expect(evaluate("{{#each company.people}}{{{this}}}{{/each}}", data)).to eq('abc')
117
+ end
118
+ end
119
+
120
+ context 'default helpers' do
121
+ context 'if' do
122
+ it 'without else' do
123
+ template = [
124
+ "{{#if condition}}",
125
+ " Show something",
126
+ "{{/if}}"
127
+ ].join("\n")
128
+ expect(evaluate(template, {condition: true})).to eq("\n Show something\n")
129
+ expect(evaluate(template, {condition: false})).to eq("")
130
+ end
131
+
132
+ it 'with an else' do
133
+ template = [
134
+ "{{#if condition}}",
135
+ " Show something",
136
+ "{{ else }}",
137
+ " Do not show something",
138
+ "{{/if}}"
139
+ ].join("\n")
140
+ expect(evaluate(template, {condition: true})).to eq("\n Show something\n")
141
+ expect(evaluate(template, {condition: false})).to eq("\n Do not show something\n")
142
+ end
143
+
144
+ it 'imbricated ifs' do
145
+ template = [
146
+ "{{#if first_condition}}",
147
+ " {{#if second_condition}}",
148
+ " Case 1",
149
+ " {{else}}",
150
+ " Case 2",
151
+ " {{/if}}",
152
+ "{{else}}",
153
+ " {{#if second_condition}}",
154
+ " Case 3",
155
+ " {{else}}",
156
+ " Case 4",
157
+ " {{/if}}",
158
+ "{{/if}}"
159
+ ].join("\n")
160
+
161
+ expect(evaluate(template, {first_condition: true, second_condition: true}).strip).to eq("Case 1")
162
+ expect(evaluate(template, {first_condition: true, second_condition: false}).strip).to eq("Case 2")
163
+ expect(evaluate(template, {first_condition: false, second_condition: true}).strip).to eq("Case 3")
164
+ expect(evaluate(template, {first_condition: false, second_condition: false}).strip).to eq("Case 4")
165
+ end
166
+ end
167
+
168
+ context 'each' do
169
+ let(:ducks) {[{name: 'Huey'}, {name: 'Dewey'}, {name: 'Louis'}]}
170
+
171
+ it 'simple case' do
172
+ template = [
173
+ "<ul>",
174
+ "{{#each items}} <li>{{this.name}}</li>",
175
+ "{{/each}}</ul>"
176
+ ].join("\n")
177
+
178
+ data = {items: ducks}
179
+ expect(evaluate(template, data)).to eq([
180
+ "<ul>",
181
+ " <li>Huey</li>",
182
+ " <li>Dewey</li>",
183
+ " <li>Louis</li>",
184
+ "</ul>"
185
+ ].join("\n"))
186
+
187
+ data = {items: []}
188
+ expect(evaluate(template, data)).to eq([
189
+ "<ul>",
190
+ "</ul>"
191
+ ].join("\n"))
192
+ end
193
+
194
+ it 'considers not found items as an empty list and does not raise an error' do
195
+ template = [
196
+ "<ul>",
197
+ "{{#each stuff}} <li>{{this.name}}</li>",
198
+ "{{/each}}</ul>"
199
+ ].join("\n")
200
+
201
+ expect(evaluate(template, {})).to eq([
202
+ "<ul>",
203
+ "</ul>"
204
+ ].join("\n"))
205
+ end
206
+
207
+ it 'considers not found items as an empty list and uses else block if provided' do
208
+ template = [
209
+ "<ul>",
210
+ "{{#each stuff}} <li>{{this.name}}</li>",
211
+ "{{else}} <li>No stuff found....</li>",
212
+ "{{/each}}</ul>"
213
+ ].join("\n")
214
+
215
+ expect(evaluate(template, {})).to eq([
216
+ "<ul>",
217
+ " <li>No stuff found....</li>",
218
+ "</ul>"
219
+ ].join("\n"))
220
+ end
221
+
222
+ it 'works with non-hash data' do
223
+ template = [
224
+ "<ul>",
225
+ "{{#each items}} <li>{{this.name}}</li>",
226
+ "{{/each}}</ul>"
227
+ ].join("\n")
228
+
229
+ data = double(items: ducks)
230
+ expect(evaluate(template, data)).to eq([
231
+ "<ul>",
232
+ " <li>Huey</li>",
233
+ " <li>Dewey</li>",
234
+ " <li>Louis</li>",
235
+ "</ul>"
236
+ ].join("\n"))
237
+
238
+ data = {items: []}
239
+ expect(evaluate(template, data)).to eq([
240
+ "<ul>",
241
+ "</ul>"
242
+ ].join("\n"))
243
+ end
244
+
245
+ it 'using an else statement' do
246
+ template = [
247
+ "<ul>",
248
+ "{{#each items}} <li>{{this.name}}</li>",
249
+ "{{else}} <li>No ducks to display</li>",
250
+ "{{/each}}</ul>"
251
+ ].join("\n")
252
+
253
+ data = {items: ducks}
254
+ expect(evaluate(template, data)).to eq([
255
+ "<ul>",
256
+ " <li>Huey</li>",
257
+ " <li>Dewey</li>",
258
+ " <li>Louis</li>",
259
+ "</ul>"
260
+ ].join("\n"))
261
+
262
+ data = {items: []}
263
+ expect(evaluate(template, data)).to eq([
264
+ "<ul>",
265
+ " <li>No ducks to display</li>",
266
+ "</ul>"
267
+ ].join("\n"))
268
+ end
269
+
270
+ it 'imbricated' do
271
+ data = {people: [
272
+ {
273
+ name: 'Huey',
274
+ email: 'huey@junior-woodchucks.example.com',
275
+ phones: ['1234', '5678'],
276
+ },
277
+ {
278
+ name: 'Dewey',
279
+ email: 'dewey@junior-woodchucks.example.com',
280
+ phones: ['4321'],
281
+ }
282
+ ]}
283
+
284
+ template = [
285
+ "People:",
286
+ "<ul>",
287
+ " {{#each people}}",
288
+ " <li>",
289
+ " <ul>",
290
+ " <li>Name: {{this.name}}</li>",
291
+ " <li>Phones: {{#each this.phones}} {{this}} {{/each}}</li>",
292
+ " <li>email: {{this.email}}</li>",
293
+ " </ul>",
294
+ " </li>",
295
+ " {{else}}",
296
+ " <li>No one to display</li>",
297
+ " {{/each}}",
298
+ "</ul>"
299
+ ].join("\n")
300
+
301
+ expect(evaluate(template, data)).to eq([
302
+ "People:",
303
+ "<ul>",
304
+ " ",
305
+ " <li>",
306
+ " <ul>",
307
+ " <li>Name: Huey</li>",
308
+ " <li>Phones: 1234 5678 </li>",
309
+ " <li>email: huey@junior-woodchucks.example.com</li>",
310
+ " </ul>",
311
+ " </li>",
312
+ " ",
313
+ " <li>",
314
+ " <ul>",
315
+ " <li>Name: Dewey</li>",
316
+ " <li>Phones: 4321 </li>",
317
+ " <li>email: dewey@junior-woodchucks.example.com</li>",
318
+ " </ul>",
319
+ " </li>",
320
+ " ",
321
+ "</ul>"
322
+ ].join("\n"))
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
@@ -0,0 +1,317 @@
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/ruby-handlebars/parser'
3
+
4
+ describe Handlebars::Parser do
5
+ let(:parser) {Handlebars::Parser.new}
6
+
7
+ context 'recognizes' do
8
+ it 'simple templates' do
9
+ expect(parser.parse('Ho hi !')).to eq({
10
+ block_items: [
11
+ {template_content: 'Ho hi !'}
12
+ ]
13
+ })
14
+ end
15
+
16
+ it 'simple replacements' do
17
+ expect(parser.parse('{{plic}}')).to eq({
18
+ block_items: [
19
+ {helper_name: 'plic'}
20
+ ]
21
+ })
22
+ expect(parser.parse('{{ plic}}')).to eq({
23
+ block_items: [
24
+ {helper_name: 'plic'}
25
+ ]
26
+ })
27
+ expect(parser.parse('{{plic }}')).to eq({
28
+ block_items: [
29
+ {helper_name: 'plic'}
30
+ ]
31
+ })
32
+ expect(parser.parse('{{ plic }}')).to eq({
33
+ block_items: [
34
+ {helper_name: 'plic'}
35
+ ]
36
+ })
37
+ end
38
+
39
+ it 'safe strings' do
40
+ expect(parser.parse('{{{plic}}}')).to eq({
41
+ block_items: [
42
+ {helper_name: 'plic'}
43
+ ]
44
+ })
45
+
46
+ expect(parser.parse('{{{ plic}}}')).to eq({
47
+ block_items: [
48
+ {helper_name: 'plic'}
49
+ ]
50
+ })
51
+
52
+ expect(parser.parse('{{{plic }}}')).to eq({
53
+ block_items: [
54
+ {helper_name: 'plic'}
55
+ ]
56
+ })
57
+
58
+ expect(parser.parse('{{{ plic }}}')).to eq({
59
+ block_items: [
60
+ {helper_name: 'plic'}
61
+ ]
62
+ })
63
+
64
+ end
65
+
66
+ context 'helpers' do
67
+ it 'simple' do
68
+ expect(parser.parse('{{ capitalize plic }}')).to eq({
69
+ block_items: [
70
+ {
71
+ helper_name: 'capitalize',
72
+ parameters: {parameter_name: 'plic'}
73
+ }
74
+ ]
75
+ })
76
+ end
77
+
78
+ it 'with single-quoted string parameter' do
79
+ expect(parser.parse("{{ capitalize 'hi'}}")).to eq({
80
+ block_items: [
81
+ {
82
+ helper_name: 'capitalize',
83
+ parameters: {parameter_name: {str_content: 'hi'}},
84
+ }
85
+ ]
86
+ })
87
+ end
88
+
89
+ it 'with single-quoted empty string parameter' do
90
+ expect(parser.parse("{{ capitalize ''}}")).to eq({
91
+ block_items: [
92
+ {
93
+ helper_name: 'capitalize',
94
+ parameters: {parameter_name: {str_content: ''}},
95
+ }
96
+ ]
97
+ })
98
+ end
99
+
100
+ it 'with double-quoted string parameter' do
101
+ expect(parser.parse('{{ capitalize "hi"}}')).to eq({
102
+ block_items: [
103
+ {
104
+ helper_name: 'capitalize',
105
+ parameters: {parameter_name: {str_content: 'hi'}},
106
+ }
107
+ ]
108
+ })
109
+ end
110
+
111
+ it 'with double-quoted empty string parameter' do
112
+ expect(parser.parse('{{ capitalize ""}}')).to eq({
113
+ block_items: [
114
+ {
115
+ helper_name: 'capitalize',
116
+ parameters: {parameter_name: {str_content: ''}},
117
+ }
118
+ ]
119
+ })
120
+ end
121
+
122
+ it 'with multiple parameters' do
123
+ expect(parser.parse('{{ concat plic ploc plouf }}')).to eq({
124
+ block_items: [
125
+ {
126
+ helper_name: 'concat',
127
+ parameters: [
128
+ {parameter_name: 'plic'},
129
+ {parameter_name: 'ploc'},
130
+ {parameter_name: 'plouf'}
131
+ ]
132
+ }
133
+ ]
134
+ })
135
+ end
136
+
137
+ it 'block' do
138
+ expect(parser.parse('{{#capitalize}}plic{{/capitalize}}')).to eq({
139
+ block_items: [
140
+ {
141
+ helper_name: 'capitalize',
142
+ block_items: [
143
+ {template_content: 'plic'}
144
+ ]
145
+ }
146
+ ]
147
+ })
148
+ end
149
+
150
+ it 'block with parameters' do
151
+ expect(parser.parse('{{#comment "#"}}plic{{/comment}}')).to eq({
152
+ block_items: [
153
+ {
154
+ helper_name: 'comment',
155
+ parameters: {parameter_name: {str_content: '#'}},
156
+ block_items: [
157
+ {template_content: 'plic'}
158
+ ]
159
+ }
160
+ ]
161
+ })
162
+ end
163
+
164
+ it 'imbricated blocks' do
165
+ expect(parser.parse('{{#comment "#"}}plic {{#capitalize}}ploc{{/capitalize}} plouc{{/comment}}')).to eq({
166
+ block_items: [
167
+ {
168
+ helper_name: 'comment',
169
+ parameters: {parameter_name: {str_content: '#'}},
170
+ block_items: [
171
+ {template_content: 'plic '},
172
+ {
173
+ helper_name: 'capitalize',
174
+ block_items: [{template_content: 'ploc'}]
175
+ },
176
+ {template_content: ' plouc'},
177
+ ]
178
+ }
179
+ ]
180
+ })
181
+ end
182
+ end
183
+
184
+ context 'if block' do
185
+ it 'simple' do
186
+ expect(parser.parse('{{#if something}}show something else{{/if}}')).to eq({
187
+ block_items: [
188
+ {
189
+ helper_name: 'if',
190
+ parameters: {parameter_name: 'something'},
191
+ block_items: [
192
+ {template_content: 'show something else'}
193
+ ]
194
+ }
195
+ ]
196
+ })
197
+ end
198
+
199
+ it 'with an else statement' do
200
+ expect(parser.parse('{{#if something}}Ok{{else}}not ok{{/if}}')).to eq({
201
+ block_items: [
202
+ {
203
+ helper_name: 'if',
204
+ parameters: {parameter_name: 'something'},
205
+ block_items: [
206
+ {template_content: 'Ok'},
207
+ {helper_name: 'else'},
208
+ {template_content: 'not ok'}
209
+ ]
210
+ }
211
+ ]
212
+ })
213
+ end
214
+
215
+ it 'imbricated' do
216
+ expect(parser.parse('{{#if something}}{{#if another_thing}}Plic{{/if}}ploc{{/if}}')).to eq({
217
+ block_items: [
218
+ {
219
+ helper_name: 'if',
220
+ parameters: {parameter_name: 'something'},
221
+ block_items: [
222
+ {
223
+ helper_name: 'if',
224
+ parameters: {parameter_name: 'another_thing'},
225
+ block_items: [
226
+ {template_content: 'Plic'}
227
+ ]
228
+ },
229
+ {template_content: 'ploc'}
230
+ ]
231
+ }
232
+ ]
233
+ })
234
+ end
235
+ end
236
+
237
+ context 'each block' do
238
+ it 'simple' do
239
+ expect(parser.parse('{{#each people}} {{this.name}} {{/each}}')).to eq({
240
+ block_items: [
241
+ {
242
+ helper_name: 'each',
243
+ parameters: {parameter_name: 'people'},
244
+ block_items: [
245
+ {template_content: ' '},
246
+ {helper_name: 'this.name'},
247
+ {template_content: ' '}
248
+ ]
249
+ }
250
+ ]
251
+ })
252
+ end
253
+
254
+ it 'imbricated' do
255
+ expect(parser.parse('{{#each people}} {{this.name}} <ul> {{#each this.contact}} <li>{{this}}</li> {{/each}}</ul>{{/each}}')).to eq({
256
+ block_items: [
257
+ {
258
+ helper_name: 'each',
259
+ parameters: {parameter_name: 'people'},
260
+ block_items: [
261
+ {template_content: ' '},
262
+ {helper_name: 'this.name'},
263
+ {template_content: ' <ul> '},
264
+ {
265
+ helper_name: 'each',
266
+ parameters: {parameter_name: 'this.contact'},
267
+ block_items: [
268
+ {template_content: ' <li>'},
269
+ {helper_name: 'this'},
270
+ {template_content: '</li> '}
271
+ ]
272
+ },
273
+ {template_content: '</ul>'}
274
+ ]
275
+ }
276
+ ]
277
+ })
278
+ end
279
+ end
280
+
281
+ context 'templates with single curlies' do
282
+ it 'works with loose curlies' do
283
+ expect(parser.parse('} Hi { hey } {')).to eq({
284
+ block_items: [
285
+ {template_content: '} Hi { hey } {'}
286
+ ]
287
+ })
288
+ end
289
+
290
+ it 'works with groups of curlies' do
291
+ expect(parser.parse('{ Hi }{ hey }')).to eq({
292
+ block_items: [
293
+ {template_content: '{ Hi }{ hey }'}
294
+ ]
295
+ })
296
+ end
297
+
298
+ it 'works with closing curly before value' do
299
+ expect(parser.parse('Hi }{{ hey }}')).to eq({
300
+ block_items: [
301
+ {template_content: 'Hi }'},
302
+ {helper_name: 'hey'}
303
+ ]
304
+ })
305
+ end
306
+
307
+ it 'works with closing curly before value at the start' do
308
+ expect(parser.parse('}{{ hey }}')).to eq({
309
+ block_items: [
310
+ {template_content: '}'},
311
+ {helper_name: 'hey'}
312
+ ]
313
+ })
314
+ end
315
+ end
316
+ end
317
+ end