hotcell 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +15 -0
  5. data/Guardfile +24 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +17 -0
  9. data/hotcell.gemspec +22 -0
  10. data/lib/hotcell/.DS_Store +0 -0
  11. data/lib/hotcell/config.rb +31 -0
  12. data/lib/hotcell/context.rb +36 -0
  13. data/lib/hotcell/errors.rb +43 -0
  14. data/lib/hotcell/extensions.rb +42 -0
  15. data/lib/hotcell/lexer.rb +783 -0
  16. data/lib/hotcell/lexer.rl +299 -0
  17. data/lib/hotcell/manipulator.rb +31 -0
  18. data/lib/hotcell/node/arrayer.rb +7 -0
  19. data/lib/hotcell/node/assigner.rb +11 -0
  20. data/lib/hotcell/node/block.rb +58 -0
  21. data/lib/hotcell/node/calculator.rb +35 -0
  22. data/lib/hotcell/node/command.rb +41 -0
  23. data/lib/hotcell/node/hasher.rb +7 -0
  24. data/lib/hotcell/node/joiner.rb +7 -0
  25. data/lib/hotcell/node/sequencer.rb +7 -0
  26. data/lib/hotcell/node/summoner.rb +11 -0
  27. data/lib/hotcell/node/tag.rb +26 -0
  28. data/lib/hotcell/node.rb +55 -0
  29. data/lib/hotcell/parser.rb +1186 -0
  30. data/lib/hotcell/parser.y +231 -0
  31. data/lib/hotcell/scope.rb +57 -0
  32. data/lib/hotcell/template.rb +29 -0
  33. data/lib/hotcell/version.rb +3 -0
  34. data/lib/hotcell.rb +19 -0
  35. data/misc/rage.rl +1999 -0
  36. data/misc/unicode2ragel.rb +305 -0
  37. data/spec/data/dstrings +8 -0
  38. data/spec/data/sstrings +6 -0
  39. data/spec/lib/hotcell/config_spec.rb +57 -0
  40. data/spec/lib/hotcell/context_spec.rb +53 -0
  41. data/spec/lib/hotcell/lexer_spec.rb +340 -0
  42. data/spec/lib/hotcell/manipulator_spec.rb +64 -0
  43. data/spec/lib/hotcell/node/block_spec.rb +188 -0
  44. data/spec/lib/hotcell/node/command_spec.rb +71 -0
  45. data/spec/lib/hotcell/parser_spec.rb +382 -0
  46. data/spec/lib/hotcell/scope_spec.rb +160 -0
  47. data/spec/lib/hotcell/template_spec.rb +41 -0
  48. data/spec/lib/hotcell_spec.rb +8 -0
  49. data/spec/spec_helper.rb +44 -0
  50. metadata +139 -0
@@ -0,0 +1,382 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Hotcell::Parser do
5
+ def method_missing method, *args, &block
6
+ klass = Hotcell::Calculator::HANDLERS[method] ?
7
+ Hotcell::Calculator : Hotcell::Node
8
+
9
+ instance = Hotcell::Assigner.new *args if method == :ASSIGN
10
+ instance = Hotcell::Summoner.new *args if method == :METHOD
11
+ klass = Hotcell::Arrayer if [:ARRAY, :PAIR].include?(method)
12
+ klass = Hotcell::Hasher if method == :HASH
13
+ klass = Hotcell::Sequencer if method == :SEQUENCE
14
+
15
+ klass = Hotcell::Tag if method == :TAG
16
+ instance = Hotcell::Command.new *args if method == :COMMAND
17
+ instance = Hotcell::Block.new *args if method == :BLOCK
18
+ klass = Hotcell::Joiner if method == :JOINER
19
+
20
+ instance || klass.new(method, *args)
21
+ end
22
+
23
+ def parse *args
24
+ described_class.new(*args).parse
25
+ end
26
+
27
+ context 'template' do
28
+ specify { parse('').should be_equal_node_to JOINER() }
29
+ specify { parse(' ').should be_equal_node_to JOINER(' ') }
30
+ specify { parse('{{ }}').should be_equal_node_to JOINER(TAG(mode: :normal)) }
31
+ specify { parse('hello {{ }}').should be_equal_node_to JOINER('hello ', TAG(mode: :normal)) }
32
+ specify { parse('{{ }}hello').should be_equal_node_to JOINER(TAG(mode: :normal), 'hello') }
33
+ specify { parse('hello{{ }} hello').should be_equal_node_to JOINER('hello', TAG(mode: :normal), ' hello') }
34
+ specify { parse('hello {{ hello(\'world\') }} hello').should be_equal_node_to JOINER(
35
+ 'hello ', TAG(METHOD(nil, 'hello', 'world'), mode: :normal), ' hello'
36
+ ) }
37
+ specify { parse('hello {{ hello(\'world\') }} hello {{! a = 5; }} {}').should be_equal_node_to JOINER(
38
+ 'hello ', TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
39
+ ' hello ', TAG(ASSIGN('a', 5), mode: :silence), ' {}'
40
+ ) }
41
+ specify { parse('{{ hello(\'world\') }} hello {{! a = 5; }} {}').should be_equal_node_to JOINER(
42
+ TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
43
+ ' hello ', TAG(ASSIGN('a', 5), mode: :silence), ' {}'
44
+ ) }
45
+ specify { parse('{{ hello(\'world\') }} hello {{! a = 5; }}').should be_equal_node_to JOINER(
46
+ TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
47
+ ' hello ', TAG(ASSIGN('a', 5), mode: :silence)
48
+ ) }
49
+ end
50
+
51
+ context 'multiline' do
52
+ specify { parse(<<-TPL
53
+ {{
54
+ hello;
55
+ 42
56
+ [1, 2, 3];
57
+
58
+ ;
59
+
60
+
61
+ "hello"; {}
62
+ (/regexp/; 43)
63
+
64
+ foo = (3; 5)
65
+ bar = 3; 5
66
+ }}
67
+ TPL
68
+ ).should be_equal_node_to JOINER(' ', TAG(
69
+ METHOD(nil, 'hello'),
70
+ 42,
71
+ ARRAY(1, 2, 3),
72
+ 'hello',
73
+ HASH(),
74
+ SEQUENCE(/regexp/, 43),
75
+ ASSIGN('foo', SEQUENCE(3, 5)),
76
+ ASSIGN('bar', 3),
77
+ 5,
78
+ mode: :normal), "\n") }
79
+ specify { parse(<<-TPL
80
+ {{
81
+ foo(
82
+ )
83
+ hello(
84
+ 7,
85
+ 8
86
+ )
87
+ !
88
+ true
89
+ [
90
+ ]
91
+ 2 +
92
+ foo
93
+ 46 -
94
+ moo
95
+ 46
96
+ - moo
97
+ 2 **
98
+ bar
99
+ false ||
100
+ nil
101
+ baz >=
102
+ 5; foo =
103
+ 10
104
+ {
105
+ }
106
+ [
107
+ 42, 43,
108
+ 44
109
+ ]
110
+ {
111
+ foo: 'hello',
112
+ bar:
113
+ 'world'
114
+ }
115
+ }}
116
+ TPL
117
+ ).should be_equal_node_to JOINER(' ', TAG(
118
+ METHOD(nil, 'foo'),
119
+ METHOD(nil, 'hello', 7, 8),
120
+ false,
121
+ ARRAY(),
122
+ PLUS(2, METHOD(nil, 'foo')),
123
+ MINUS(46, METHOD(nil, 'moo')),
124
+ 46,
125
+ UMINUS(METHOD(nil, 'moo')),
126
+ POWER(2, METHOD(nil, 'bar')),
127
+ nil,
128
+ GTE(METHOD(nil, 'baz'), 5),
129
+ ASSIGN('foo', 10),
130
+ HASH(),
131
+ ARRAY(42, 43, 44),
132
+ HASH(PAIR('foo', 'hello'), PAIR('bar', 'world')),
133
+ mode: :normal), "\n") }
134
+ end
135
+
136
+ context 'expressions' do
137
+ specify { parse('{{ 2 + hello }}').should be_equal_node_to JOINER(TAG(PLUS(2, METHOD(nil, 'hello')), mode: :normal)) }
138
+ specify { parse('{{ --2 }}').should be_equal_node_to JOINER(TAG(2, mode: :normal)) }
139
+ specify { parse('{{ --hello }}').should be_equal_node_to JOINER(TAG(UMINUS(UMINUS(METHOD(nil, 'hello'))), mode: :normal)) }
140
+ specify { parse('{{ \'hello\' + \'world\' }}').should be_equal_node_to JOINER(TAG('helloworld', mode: :normal)) }
141
+ specify { parse('{{ 2 - hello }}').should be_equal_node_to JOINER(TAG(MINUS(2, METHOD(nil, 'hello')), mode: :normal)) }
142
+ specify { parse('{{ 2 * hello }}').should be_equal_node_to JOINER(TAG(MULTIPLY(2, METHOD(nil, 'hello')), mode: :normal)) }
143
+ specify { parse('{{ 2 / hello }}').should be_equal_node_to JOINER(TAG(DIVIDE(2, METHOD(nil, 'hello')), mode: :normal)) }
144
+ specify { parse('{{ 2 % hello }}').should be_equal_node_to JOINER(TAG(MODULO(2, METHOD(nil, 'hello')), mode: :normal)) }
145
+ specify { parse('{{ 2 ** hello }}').should be_equal_node_to JOINER(TAG(POWER(2, METHOD(nil, 'hello')), mode: :normal)) }
146
+ specify { parse('{{ -hello }}').should be_equal_node_to JOINER(TAG(UMINUS(METHOD(nil, 'hello')), mode: :normal)) }
147
+ specify { parse('{{ +hello }}').should be_equal_node_to JOINER(TAG(UPLUS(METHOD(nil, 'hello')), mode: :normal)) }
148
+ specify { parse('{{ -2 }}').should be_equal_node_to JOINER(TAG(-2, mode: :normal)) }
149
+ specify { parse('{{ +2 }}').should be_equal_node_to JOINER(TAG(2, mode: :normal)) }
150
+ specify { parse('{{ 2 + lol * 2 }}').should be_equal_node_to JOINER(TAG(PLUS(2, MULTIPLY(METHOD(nil, 'lol'), 2)), mode: :normal)) }
151
+ specify { parse('{{ 2 + lol - 2 }}').should be_equal_node_to JOINER(TAG(MINUS(PLUS(2, METHOD(nil, 'lol')), 2), mode: :normal)) }
152
+ specify { parse('{{ 2 ** foo * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(POWER(2, METHOD(nil, 'foo')), 2), mode: :normal)) }
153
+ specify { parse('{{ 1 ** foo ** 3 }}').should be_equal_node_to JOINER(TAG(POWER(1, POWER(METHOD(nil, 'foo'), 3)), mode: :normal)) }
154
+ specify { parse('{{ (2 + foo) * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(PLUS(2, METHOD(nil, 'foo')), 2), mode: :normal)) }
155
+ specify { parse('{{ (nil) }}').should be_equal_node_to JOINER(TAG(nil, mode: :normal)) }
156
+ specify { parse('{{ (3) }}').should be_equal_node_to JOINER(TAG(3, mode: :normal)) }
157
+ specify { parse('{{ (\'hello\') }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
158
+ specify { parse('{{ () }}').should be_equal_node_to JOINER(TAG(nil, mode: :normal)) }
159
+
160
+ specify { parse('{{ bar > 2 }}').should be_equal_node_to JOINER(TAG(GT(METHOD(nil, 'bar'), 2), mode: :normal)) }
161
+ specify { parse('{{ 2 < bar }}').should be_equal_node_to JOINER(TAG(LT(2, METHOD(nil, 'bar')), mode: :normal)) }
162
+ specify { parse('{{ 2 >= tru }}').should be_equal_node_to JOINER(TAG(GTE(2, METHOD(nil, 'tru')), mode: :normal)) }
163
+ specify { parse('{{ some <= 2 }}').should be_equal_node_to JOINER(TAG(LTE(METHOD(nil, 'some'), 2), mode: :normal)) }
164
+ specify { parse('{{ 2 && false }}').should be_equal_node_to JOINER(TAG(false, mode: :normal)) }
165
+ specify { parse('{{ null || 2 }}').should be_equal_node_to JOINER(TAG(2, mode: :normal)) }
166
+ specify { parse('{{ 2 > bar < 2 }}').should be_equal_node_to JOINER(TAG(LT(GT(2, METHOD(nil, 'bar')), 2), mode: :normal)) }
167
+ specify { parse('{{ 2 || bar && 2 }}').should be_equal_node_to JOINER(TAG(OR(2, AND(METHOD(nil, 'bar'), 2)), mode: :normal)) }
168
+ specify { parse('{{ 2 && foo || 2 }}').should be_equal_node_to JOINER(TAG(OR(AND(2, METHOD(nil, 'foo')), 2), mode: :normal)) }
169
+ specify { parse('{{ !2 && moo }}').should be_equal_node_to JOINER(TAG(AND(false, METHOD(nil, 'moo')), mode: :normal)) }
170
+ specify { parse('{{ !(2 && moo) }}').should be_equal_node_to JOINER(TAG(NOT(AND(2, METHOD(nil, 'moo'))), mode: :normal)) }
171
+
172
+ specify { parse('{{ hello = bzz + 2 }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', PLUS(METHOD(nil, 'bzz'), 2)), mode: :normal)) }
173
+ specify { parse('{{ hello = 2 ** bar }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', POWER(2, METHOD(nil, 'bar'))), mode: :normal)) }
174
+ specify { parse('{{ hello = 2 == 2 }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', true), mode: :normal)) }
175
+ specify { parse('{{ hello = 2 && var }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', AND(2, METHOD(nil, 'var'))), mode: :normal)) }
176
+ specify { parse('{{ hello = world() }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', METHOD(nil, 'world')), mode: :normal)) }
177
+ specify { parse('{{ !hello = 2 >= 2 }}').should be_equal_node_to JOINER(TAG(NOT(ASSIGN('hello', true)), mode: :normal)) }
178
+
179
+ specify { parse('{{ !foo ** 2 + 3 }}').should be_equal_node_to JOINER(TAG(PLUS(POWER(NOT(METHOD(nil, 'foo')), 2), 3), mode: :normal)) }
180
+ specify { parse('{{ -bla ** 2 }}').should be_equal_node_to JOINER(TAG(UMINUS(POWER(METHOD(nil, 'bla'), 2)), mode: :normal)) }
181
+ specify { parse('{{ -2 % bla }}').should be_equal_node_to JOINER(TAG(MODULO(-2, METHOD(nil, 'bla')), mode: :normal)) }
182
+ specify { parse('{{ -hello ** 2 }}').should be_equal_node_to JOINER(TAG(UMINUS(POWER(METHOD(nil, 'hello'), 2)), mode: :normal)) }
183
+ specify { parse('{{ -hello * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(UMINUS(METHOD(nil, 'hello')), 2), mode: :normal)) }
184
+ specify { parse('{{ haha + 2 == 2 * 2 }}').should be_equal_node_to JOINER(TAG(EQUAL(PLUS(METHOD(nil, 'haha'), 2), 4), mode: :normal)) }
185
+ specify { parse('{{ 2 * foo != 2 && bar }}').should be_equal_node_to JOINER(TAG(AND(INEQUAL(MULTIPLY(2, METHOD(nil, 'foo')), 2), METHOD(nil, 'bar')), mode: :normal)) }
186
+
187
+ context 'method call' do
188
+ specify { parse('{{ foo.bar.baz }}').should be_equal_node_to JOINER(TAG(
189
+ METHOD(METHOD(METHOD(nil, 'foo'), 'bar'), 'baz'),
190
+ mode: :normal)) }
191
+ specify { parse('{{ foo(\'hello\').bar[2].baz(-42) }}').should be_equal_node_to JOINER(TAG(
192
+ METHOD(
193
+ METHOD(
194
+ METHOD(
195
+ METHOD(
196
+ nil, 'foo', 'hello'
197
+ ), 'bar'
198
+ ), '[]', 2
199
+ ), 'baz', -42
200
+ ),
201
+ mode: :normal)) }
202
+ end
203
+ end
204
+
205
+ context 'arrays' do
206
+ specify { parse('{{ [] }}').should be_equal_node_to JOINER(TAG(ARRAY(), mode: :normal)) }
207
+ specify { parse('{{ [ 2 ] }}').should be_equal_node_to JOINER(TAG(ARRAY(2), mode: :normal)) }
208
+ specify { parse('{{ [ 2, 3 ] }}').should be_equal_node_to JOINER(TAG(ARRAY(2, 3), mode: :normal)) }
209
+ specify { parse('{{ [2, 3][42] }}').should be_equal_node_to JOINER(TAG(METHOD(ARRAY(2, 3), '[]', 42), mode: :normal)) }
210
+ specify { parse('{{ [2 + foo, (2 * bar)] }}').should be_equal_node_to JOINER(TAG(ARRAY(PLUS(2, METHOD(nil, 'foo')), MULTIPLY(2, METHOD(nil, 'bar'))), mode: :normal)) }
211
+ specify { parse('{{ [[2, 3], 42] }}').should be_equal_node_to JOINER(TAG(ARRAY(ARRAY(2, 3), 42), mode: :normal)) }
212
+ end
213
+
214
+ context 'hashes' do
215
+ specify { parse('{{ {} }}').should be_equal_node_to JOINER(TAG(HASH(), mode: :normal)) }
216
+ specify { parse('{{ { hello: \'world\' } }}').should be_equal_node_to(
217
+ JOINER(TAG(HASH(PAIR('hello', 'world')), mode: :normal))
218
+ ) }
219
+ specify { parse('{{ {hello: \'world\'}[\'hello\'] }}').should be_equal_node_to(
220
+ JOINER(TAG(METHOD(HASH(PAIR('hello', 'world')), '[]', 'hello'), mode: :normal))
221
+ ) }
222
+ specify { parse('{{ { hello: 3, world: 6 * foo } }}').should be_equal_node_to(
223
+ JOINER(TAG(HASH(
224
+ PAIR('hello', 3),
225
+ PAIR('world', MULTIPLY(6, METHOD(nil, 'foo')))
226
+ ), mode: :normal))
227
+ ) }
228
+ end
229
+
230
+ context '[]' do
231
+ specify { parse('{{ hello[3] }}').should be_equal_node_to JOINER(TAG(METHOD(METHOD(nil, 'hello'), '[]', 3), mode: :normal)) }
232
+ specify { parse('{{ \'boom\'[3] }}').should be_equal_node_to JOINER(TAG(METHOD('boom', '[]', 3), mode: :normal)) }
233
+ specify { parse('{{ 7[3] }}').should be_equal_node_to JOINER(TAG(METHOD(7, '[]', 3), mode: :normal)) }
234
+ specify { parse('{{ 3 + 5[7] }}').should be_equal_node_to JOINER(TAG(PLUS(3, METHOD(5, '[]', 7)), mode: :normal)) }
235
+ specify { parse('{{ (3 + 5)[7] }}').should be_equal_node_to JOINER(TAG(METHOD(8, '[]', 7), mode: :normal)) }
236
+ end
237
+
238
+ context 'function arguments' do
239
+ specify { parse('{{ hello() }}').should be_equal_node_to JOINER(TAG(METHOD(nil, 'hello'), mode: :normal)) }
240
+ specify { parse('{{ hello(2 * foo) }}').should be_equal_node_to(
241
+ JOINER(TAG(METHOD(nil, 'hello', MULTIPLY(2, METHOD(nil, 'foo'))), mode: :normal))
242
+ ) }
243
+ specify { parse('{{ hello([2 * car]) }}').should be_equal_node_to(
244
+ JOINER(TAG(METHOD(nil, 'hello', ARRAY(MULTIPLY(2, METHOD(nil, 'car')))), mode: :normal))
245
+ ) }
246
+ specify { parse('{{ hello({hello: \'world\'}) }}').should be_equal_node_to(
247
+ JOINER(TAG(METHOD(nil, 'hello', HASH(PAIR('hello', 'world'))), mode: :normal))
248
+ ) }
249
+ specify { parse('{{ hello(hello: \'world\') }}').should be_equal_node_to(
250
+ JOINER(TAG(METHOD(nil, 'hello', HASH(PAIR('hello', 'world'))), mode: :normal))
251
+ ) }
252
+ specify { parse('{{ hello(2 * foo, \'bla\', {hello: \'world\'}) }}').should be_equal_node_to(
253
+ JOINER(TAG(METHOD(nil, 'hello',
254
+ MULTIPLY(2, METHOD(nil, 'foo')),
255
+ 'bla',
256
+ HASH(PAIR('hello', 'world'))
257
+ ), mode: :normal))
258
+ ) }
259
+ specify { parse('{{ hello(moo * 3, \'bla\', hello: \'world\') }}').should be_equal_node_to(
260
+ JOINER(TAG(METHOD(nil, 'hello',
261
+ MULTIPLY(METHOD(nil, 'moo'), 3),
262
+ 'bla',
263
+ HASH(PAIR('hello', 'world'))
264
+ ), mode: :normal))
265
+ ) }
266
+ end
267
+
268
+ context 'sequences' do
269
+ specify { parse('{{ 42; }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
270
+ specify { parse('{{ (42) }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
271
+ specify { parse('{{ ((42)) }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
272
+ specify { parse('{{ ;;;; }}').should be_equal_node_to JOINER(TAG(mode: :normal)) }
273
+ specify { parse('{{ ;;;;;;; 42 }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
274
+ specify { parse('{{ ;;;111;;;; 42 }}').should be_equal_node_to JOINER(TAG(111, 42, mode: :normal)) }
275
+ specify { parse("{{ 42 ;;;;\n;;; }}").should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
276
+ specify { parse('{{ 42 ;;;;;;1 }}').should be_equal_node_to JOINER(TAG(42, 1, mode: :normal)) }
277
+ specify { parse('{{ 42; 43 }}').should be_equal_node_to JOINER(TAG(42, 43, mode: :normal)) }
278
+ specify { parse('{{ ; 42; 43 }}').should be_equal_node_to JOINER(TAG(42, 43, mode: :normal)) }
279
+ specify { parse('{{ 42; 43; 44 }}').should be_equal_node_to JOINER(TAG(42, 43, 44, mode: :normal)) }
280
+ specify { parse('{{ 42; \'hello\'; 44; }}').should be_equal_node_to JOINER(TAG(42, 'hello', 44, mode: :normal)) }
281
+ specify { parse('{{ (42; \'hello\'); 44; }}').should be_equal_node_to JOINER(TAG(SEQUENCE(42, 'hello'), 44, mode: :normal)) }
282
+ specify { parse('{{ 42; (\'hello\'; 44;) }}').should be_equal_node_to JOINER(TAG(42, SEQUENCE('hello', 44), mode: :normal)) }
283
+ specify { parse('{{ hello(42, (43; 44), 45) }}').should be_equal_node_to JOINER(TAG(METHOD(nil, 'hello', 42, SEQUENCE(43, 44), 45), mode: :normal)) }
284
+ specify { parse('{{ hello(42, ((43; 44)), 45) }}').should be_equal_node_to JOINER(TAG(METHOD(nil, 'hello', 42, SEQUENCE(43, 44), 45), mode: :normal)) }
285
+ specify { parse('{{ hello((42)) }}').should be_equal_node_to JOINER(TAG(METHOD(nil, 'hello', 42), mode: :normal)) }
286
+ end
287
+
288
+ context 'comments' do
289
+ specify { parse('hello # world').should be_equal_node_to JOINER('hello # world') }
290
+ specify { parse('hello {{# world').should be_equal_node_to JOINER('hello ') }
291
+ specify { parse('hello {{# world #}} friend').should be_equal_node_to JOINER('hello ', ' friend') }
292
+ specify { parse('hello {{!# world #}}').should be_equal_node_to JOINER('hello ', TAG(mode: :silence)) }
293
+ specify { parse('hello {{ # world}}').should be_equal_node_to JOINER('hello ', TAG(mode: :normal)) }
294
+ specify { parse('hello {{ # world; foo}}').should be_equal_node_to JOINER('hello ', TAG(mode: :normal)) }
295
+ specify { parse("hello {{ # world\n foo}}").should be_equal_node_to JOINER('hello ', TAG(METHOD(nil, 'foo'), mode: :normal)) }
296
+ end
297
+
298
+ context 'commands' do
299
+ specify { parse("{{ include 'some/partial' }}",
300
+ commands: [:include, :snippet]).should be_equal_node_to JOINER(
301
+ COMMAND('include', 'some/partial', mode: :normal)
302
+ ) }
303
+ specify { parse("{{ include }}",
304
+ commands: [:include, :snippet]).should be_equal_node_to JOINER(
305
+ COMMAND('include', mode: :normal)
306
+ ) }
307
+ specify { parse("{{! include 'some/partial' }}\n{{ snippet 'sidebar' }}",
308
+ commands: [:include, :snippet]).should be_equal_node_to JOINER(
309
+ COMMAND('include', 'some/partial', mode: :silence),
310
+ "\n",
311
+ COMMAND('snippet', 'sidebar', mode: :normal),
312
+ ) }
313
+ specify { parse("{{! variable = include }}",
314
+ commands: [:include, :snippet]).should be_equal_node_to JOINER(
315
+ COMMAND('include', mode: :silence, assign: 'variable')
316
+ ) }
317
+ specify { parse("{{ variable = include 'some/partial' }}",
318
+ commands: [:include, :snippet]).should be_equal_node_to JOINER(
319
+ COMMAND('include', 'some/partial', mode: :normal, assign: 'variable')
320
+ ) }
321
+ end
322
+
323
+ context 'blocks' do
324
+ specify { parse("{{ scoped var: 'hello' }}{{ endscoped }}",
325
+ blocks: [:scoped, :for]).should be_equal_node_to JOINER(
326
+ BLOCK('scoped', HASH(PAIR('var', 'hello')), mode: :normal)
327
+ ) }
328
+ specify { parse("{{ scoped }}{{ end scoped }}",
329
+ blocks: [:scoped, :for]).should be_equal_node_to JOINER(
330
+ BLOCK('scoped', mode: :normal)
331
+ ) }
332
+ specify { parse("<article>\n{{ for post, in: posts }}\n<h1>{{ post.title }}</h1>\n{{ end for }}\n</article>",
333
+ blocks: [:scoped, :for]).should be_equal_node_to JOINER(
334
+ "<article>\n",
335
+ BLOCK('for',
336
+ METHOD(nil, 'post'),
337
+ HASH(PAIR('in', METHOD(nil, 'posts'))),
338
+ mode: :normal,
339
+ subnodes: [JOINER(
340
+ "\n<h1>",
341
+ TAG(METHOD(METHOD(nil, 'post'), 'title'), mode: :normal),
342
+ "</h1>\n"
343
+ )]
344
+ ),
345
+ "\n</article>"
346
+ ) }
347
+ specify { parse("{{! posts = for post, in: posts }}\n<h1>{{ post.title }}</h1>\n{{ end for }}",
348
+ blocks: [:scoped, :for]).should be_equal_node_to JOINER(
349
+ BLOCK('for',
350
+ METHOD(nil, 'post'),
351
+ HASH(PAIR('in', METHOD(nil, 'posts'))),
352
+ mode: :silence,
353
+ assign: 'posts',
354
+ subnodes: [JOINER(
355
+ "\n<h1>",
356
+ TAG(METHOD(METHOD(nil, 'post'), 'title'), mode: :normal),
357
+ "</h1>\n"
358
+ )]
359
+ ),
360
+ ) }
361
+ specify { parse("{{ capture = scoped }} hello {{ for post, in: posts }} {{ loop }} {{ end for }}{{ endscoped }}",
362
+ blocks: [:scoped, :for]).should be_equal_node_to JOINER(
363
+ BLOCK('scoped',
364
+ mode: :normal,
365
+ assign: 'capture',
366
+ subnodes: [JOINER(
367
+ ' hello ',
368
+ BLOCK('for',
369
+ METHOD(nil, 'post'),
370
+ HASH(PAIR('in', METHOD(nil, 'posts'))),
371
+ mode: :normal,
372
+ subnodes: [JOINER(
373
+ ' ',
374
+ TAG(METHOD(nil, 'loop'), mode: :normal),
375
+ ' '
376
+ )]
377
+ )
378
+ )]
379
+ )
380
+ ) }
381
+ end
382
+ end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hotcell::Scope do
4
+ subject { described_class.new }
5
+ describe '#initialize' do
6
+ its(:scope) { should == [{}] }
7
+ specify { described_class.new(foo: 1, 'bar' => 2).send(:scope).should == [{foo: 1, 'bar' => 2}] }
8
+ end
9
+
10
+ describe '#push' do
11
+ context do
12
+ before { subject.push(hello: 'world') }
13
+ its(:scope) { should == [{}, {hello: 'world'}] }
14
+ end
15
+
16
+ context do
17
+ before do
18
+ subject.push(hello: 'world')
19
+ subject.push(hello: 'hell', foo: 42)
20
+ end
21
+ its(:scope) { should == [{}, {hello: 'world'}, {hello: 'hell', foo: 42}] }
22
+ end
23
+ end
24
+
25
+ describe '#pop' do
26
+ subject { described_class.new bar: 'baz' }
27
+ before do
28
+ subject.send(:cache)
29
+ subject.push(hello: 'world')
30
+ subject.push(hello: 'hell', foo: 42)
31
+ subject.send(:cache)
32
+ end
33
+
34
+ context do
35
+ specify { subject[:bar].should == 'baz' }
36
+ specify { subject[:hello].should == 'hell' }
37
+ specify { subject[:foo].should == 42 }
38
+ end
39
+
40
+ context do
41
+ before { subject.pop }
42
+ its(:scope) { should == [{bar: 'baz'}, {hello: 'world'}] }
43
+ specify { subject[:bar].should == 'baz' }
44
+ specify { subject[:hello].should == 'world' }
45
+ specify { subject[:foo].should be_nil }
46
+ end
47
+
48
+ context do
49
+ before { 2.times { subject.pop } }
50
+ its(:scope) { should == [{bar: 'baz'}] }
51
+ specify { subject[:bar].should == 'baz' }
52
+ specify { subject[:hello].should be_nil }
53
+ specify { subject[:foo].should be_nil }
54
+ end
55
+
56
+ context do
57
+ before { 4.times { subject.pop } }
58
+ its(:scope) { should == [{bar: 'baz'}] }
59
+ specify { subject[:bar].should == 'baz' }
60
+ specify { subject[:hello].should be_nil }
61
+ specify { subject[:foo].should be_nil }
62
+ end
63
+ end
64
+
65
+ describe '#scoped' do
66
+ specify do
67
+ subject.send(:scope).should == [{}]
68
+ subject.scoped(foo: 'bar') do
69
+ subject.send(:scope).should == [{}, {foo: 'bar'}]
70
+ subject.scoped(moo: 'baz', hello: 42) do
71
+ subject.send(:scope).should == [{}, {foo: 'bar'}, {moo: 'baz', hello: 42}]
72
+ end
73
+ subject.send(:scope).should == [{}, {foo: 'bar'}]
74
+ end
75
+ subject.send(:scope).should == [{}]
76
+ end
77
+
78
+ specify 'exception safety' do
79
+ begin
80
+ subject.send(:scope).should == [{}]
81
+ subject.scoped(foo: 'bar') do
82
+ subject.send(:scope).should == [{}, {foo: 'bar'}]
83
+ raise
84
+ end
85
+ rescue
86
+ subject.send(:scope).should == [{}]
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#[]' do
92
+ subject { described_class.new bar: 'baz' }
93
+
94
+ specify { subject[:foo].should be_nil }
95
+ specify { subject['bar'].should be_nil }
96
+ specify { subject[:bar].should == 'baz' }
97
+
98
+ context do
99
+ before do
100
+ subject.push(hello: 42)
101
+ subject.push(bar: 'world')
102
+ end
103
+
104
+ specify { subject[:hello].should == 42 }
105
+ specify { subject[:bar].should == 'world' }
106
+ end
107
+ end
108
+
109
+ describe '#key?' do
110
+ subject { described_class.new bar: 'baz' }
111
+
112
+ specify { subject.key?(:foo).should be_false }
113
+ specify { subject.key?('bar').should be_false }
114
+ specify { subject.key?(:bar).should be_true }
115
+
116
+ context do
117
+ before do
118
+ subject.push(hello: 42)
119
+ subject.push(bar: 'world')
120
+ end
121
+
122
+ specify { subject.key?(:hello).should be_true }
123
+ specify { subject.key?(:bar).should be_true }
124
+ end
125
+ end
126
+
127
+ describe '#[]=' do
128
+ subject { described_class.new bar: 'baz' }
129
+
130
+ context do
131
+ before do
132
+ subject['foo'] = 1
133
+ subject[:foo] = 2
134
+ end
135
+
136
+ its(:scope) { should == [{bar: "baz", 'foo' => 1, foo: 2}] }
137
+ specify { subject['foo'].should == 1 }
138
+ specify { subject[:foo].should == 2 }
139
+ end
140
+
141
+ context do
142
+ before do
143
+ subject.push(hello: 42)
144
+ subject.push(bar: 'world')
145
+ subject.send(:cache)
146
+ end
147
+
148
+ before do
149
+ subject['foo'] = 1
150
+ subject[:bar] = 2
151
+ subject[:hello] = 3
152
+ end
153
+
154
+ its(:scope) { should == [{bar: 'baz'}, {hello: 3}, {bar: 2, 'foo' => 1}] }
155
+ specify { subject['foo'].should == 1 }
156
+ specify { subject[:bar].should == 2 }
157
+ specify { subject[:hello].should == 3 }
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hotcell::Template do
4
+ describe '.parse' do
5
+ before do
6
+ Hotcell.stub(:commands) { { 'include' => Class.new } }
7
+ Hotcell.stub(:blocks) { { 'for' => Class.new } }
8
+ Hotcell.stub(:subcommands) { { 'else' => Class.new } }
9
+ end
10
+
11
+ specify { described_class.parse('').should be_a described_class }
12
+ specify { described_class.parse('').options.should == {
13
+ commands: ['include'], blocks: ['for'], subcommands: ['else']
14
+ } }
15
+ end
16
+
17
+ describe '#syntax' do
18
+ specify { described_class.new('').syntax.should be_a Hotcell::Joiner }
19
+ specify { described_class.new('hello').syntax.should be_a Hotcell::Joiner }
20
+ specify { described_class.new('hello {{ tag }}').syntax.should be_a Hotcell::Joiner }
21
+ end
22
+
23
+ describe '#render' do
24
+ context do
25
+ subject { described_class.new('Hello, {{ name }}!') }
26
+ specify { subject.render.should == 'Hello, !' }
27
+ specify { subject.render(variables: { name: 'Friend' }).should == 'Hello, Friend!' }
28
+ end
29
+
30
+ context do
31
+ subject { described_class.new('Hello, {{ name * 2 }}!') }
32
+ specify { subject.render.should =~ /Hello.*NoMethodError/ }
33
+ specify { subject.render(variables: { name: 'Friend' }).should == 'Hello, FriendFriend!' }
34
+ end
35
+
36
+ context do
37
+ subject { described_class.new('Hello, {{! name }}!') }
38
+ specify { subject.render(variables: { name: 'Friend' }).should == 'Hello, !' }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hotcell do
4
+ it { should respond_to :commands }
5
+ it { should respond_to :blocks }
6
+ it { should respond_to :subcommands }
7
+ it { should respond_to :register_command }
8
+ end
@@ -0,0 +1,44 @@
1
+ require 'hotcell'
2
+ require 'awesome_print'
3
+
4
+ AwesomePrint::Formatter.class_eval do
5
+ def cast_with_hotcell_node(object, type)
6
+ cast = cast_without_hotcell_node(object, type)
7
+ cast = :hotcell_node if object.is_a?(Hotcell::Node)
8
+ cast
9
+ end
10
+
11
+ def awesome_hotcell_node(object)
12
+ "#{colorize(object.class.name, :class)} #{colorize(object.name.to_s, :method)}" +
13
+ ((awesome_hash(object.options) + "\n") if object.options.present?).to_s +
14
+ awesome_array(object.children)
15
+ end
16
+
17
+ alias_method_chain :cast, :hotcell_node
18
+ end
19
+
20
+ RSpec::Matchers.define :be_equal_node_to do |expected|
21
+ match do |actual|
22
+ actual == expected
23
+ end
24
+
25
+ failure_message_for_should do |actual|
26
+ "expected:\n#{expected.ai(raw: true, indent: 2)}\ngot:\n#{actual.ai(raw: true, indent: 2)}"
27
+ end
28
+ end
29
+
30
+ RSpec.configure do |config|
31
+ def data name
32
+ File.read File.join(File.dirname(__FILE__), 'data', name)
33
+ end
34
+
35
+ config.treat_symbols_as_metadata_keys_with_true_values = true
36
+ config.run_all_when_everything_filtered = true
37
+ config.filter_run :focus
38
+
39
+ # Run specs in random order to surface order dependencies. If you find an
40
+ # order dependency and want to debug it, you can fix the order by providing
41
+ # the seed, which is printed after each run.
42
+ # --seed 1234
43
+ config.order = 'random'
44
+ end