hotcell 0.0.1

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.
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