hotcell 0.0.1 → 0.1.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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -1
  5. data/.travis.yml +7 -0
  6. data/Gemfile +4 -1
  7. data/README.md +361 -2
  8. data/Rakefile +28 -6
  9. data/ext/lexerc/extconf.rb +3 -0
  10. data/ext/lexerc/lexerc.c +618 -0
  11. data/ext/lexerc/lexerc.h +20 -0
  12. data/ext/lexerc/lexerc.rl +167 -0
  13. data/hotcell.gemspec +8 -7
  14. data/lib/hotcell/commands/case.rb +59 -0
  15. data/lib/hotcell/commands/cycle.rb +38 -0
  16. data/lib/hotcell/commands/for.rb +70 -0
  17. data/lib/hotcell/commands/if.rb +51 -0
  18. data/lib/hotcell/commands/include.rb +21 -0
  19. data/lib/hotcell/commands/scope.rb +13 -0
  20. data/lib/hotcell/commands/unless.rb +23 -0
  21. data/lib/hotcell/commands.rb +13 -0
  22. data/lib/hotcell/config.rb +33 -6
  23. data/lib/hotcell/context.rb +40 -7
  24. data/lib/hotcell/errors.rb +37 -28
  25. data/lib/hotcell/extensions.rb +4 -0
  26. data/lib/hotcell/lexer.rb +19 -635
  27. data/lib/hotcell/lexerr.rb +572 -0
  28. data/lib/hotcell/lexerr.rl +137 -0
  29. data/lib/hotcell/node/assigner.rb +1 -5
  30. data/lib/hotcell/node/block.rb +17 -40
  31. data/lib/hotcell/node/command.rb +29 -22
  32. data/lib/hotcell/node/hasher.rb +1 -1
  33. data/lib/hotcell/node/summoner.rb +2 -6
  34. data/lib/hotcell/node/tag.rb +10 -7
  35. data/lib/hotcell/node.rb +12 -1
  36. data/lib/hotcell/parser.rb +474 -408
  37. data/lib/hotcell/parser.y +175 -117
  38. data/lib/hotcell/resolver.rb +44 -0
  39. data/lib/hotcell/source.rb +35 -0
  40. data/lib/hotcell/template.rb +15 -6
  41. data/lib/hotcell/version.rb +1 -1
  42. data/lib/hotcell.rb +15 -10
  43. data/spec/data/templates/simple.hc +1 -0
  44. data/spec/lib/hotcell/commands/case_spec.rb +39 -0
  45. data/spec/lib/hotcell/commands/cycle_spec.rb +29 -0
  46. data/spec/lib/hotcell/commands/for_spec.rb +65 -0
  47. data/spec/lib/hotcell/commands/if_spec.rb +35 -0
  48. data/spec/lib/hotcell/commands/include_spec.rb +39 -0
  49. data/spec/lib/hotcell/commands/scope_spec.rb +16 -0
  50. data/spec/lib/hotcell/commands/unless_spec.rb +23 -0
  51. data/spec/lib/hotcell/config_spec.rb +35 -10
  52. data/spec/lib/hotcell/context_spec.rb +58 -18
  53. data/spec/lib/hotcell/lexer_spec.rb +37 -28
  54. data/spec/lib/hotcell/node/block_spec.rb +28 -56
  55. data/spec/lib/hotcell/node/command_spec.rb +7 -31
  56. data/spec/lib/hotcell/node/tag_spec.rb +16 -0
  57. data/spec/lib/hotcell/parser_spec.rb +152 -123
  58. data/spec/lib/hotcell/resolver_spec.rb +28 -0
  59. data/spec/lib/hotcell/source_spec.rb +41 -0
  60. data/spec/lib/hotcell/template_spec.rb +47 -4
  61. data/spec/lib/hotcell_spec.rb +2 -1
  62. data/spec/spec_helper.rb +6 -2
  63. metadata +54 -24
  64. data/lib/hotcell/.DS_Store +0 -0
  65. data/lib/hotcell/lexer.rl +0 -299
  66. data/misc/rage.rl +0 -1999
  67. data/misc/unicode2ragel.rb +0 -305
@@ -32,18 +32,18 @@ describe Hotcell::Parser do
32
32
  specify { parse('{{ }}hello').should be_equal_node_to JOINER(TAG(mode: :normal), 'hello') }
33
33
  specify { parse('hello{{ }} hello').should be_equal_node_to JOINER('hello', TAG(mode: :normal), ' hello') }
34
34
  specify { parse('hello {{ hello(\'world\') }} hello').should be_equal_node_to JOINER(
35
- 'hello ', TAG(METHOD(nil, 'hello', 'world'), mode: :normal), ' hello'
35
+ 'hello ', TAG(METHOD('hello', nil, 'world'), mode: :normal), ' hello'
36
36
  ) }
37
37
  specify { parse('hello {{ hello(\'world\') }} hello {{! a = 5; }} {}').should be_equal_node_to JOINER(
38
- 'hello ', TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
38
+ 'hello ', TAG(METHOD('hello', nil, 'world'), mode: :normal),
39
39
  ' hello ', TAG(ASSIGN('a', 5), mode: :silence), ' {}'
40
40
  ) }
41
41
  specify { parse('{{ hello(\'world\') }} hello {{! a = 5; }} {}').should be_equal_node_to JOINER(
42
- TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
42
+ TAG(METHOD('hello', nil, 'world'), mode: :normal),
43
43
  ' hello ', TAG(ASSIGN('a', 5), mode: :silence), ' {}'
44
44
  ) }
45
45
  specify { parse('{{ hello(\'world\') }} hello {{! a = 5; }}').should be_equal_node_to JOINER(
46
- TAG(METHOD(nil, 'hello', 'world'), mode: :normal),
46
+ TAG(METHOD('hello', nil, 'world'), mode: :normal),
47
47
  ' hello ', TAG(ASSIGN('a', 5), mode: :silence)
48
48
  ) }
49
49
  end
@@ -66,7 +66,7 @@ describe Hotcell::Parser do
66
66
  }}
67
67
  TPL
68
68
  ).should be_equal_node_to JOINER(' ', TAG(
69
- METHOD(nil, 'hello'),
69
+ METHOD('hello'),
70
70
  42,
71
71
  ARRAY(1, 2, 3),
72
72
  'hello',
@@ -115,17 +115,17 @@ describe Hotcell::Parser do
115
115
  }}
116
116
  TPL
117
117
  ).should be_equal_node_to JOINER(' ', TAG(
118
- METHOD(nil, 'foo'),
119
- METHOD(nil, 'hello', 7, 8),
118
+ METHOD('foo'),
119
+ METHOD('hello', nil, 7, 8),
120
120
  false,
121
121
  ARRAY(),
122
- PLUS(2, METHOD(nil, 'foo')),
123
- MINUS(46, METHOD(nil, 'moo')),
122
+ PLUS(2, METHOD('foo')),
123
+ MINUS(46, METHOD('moo')),
124
124
  46,
125
- UMINUS(METHOD(nil, 'moo')),
126
- POWER(2, METHOD(nil, 'bar')),
125
+ UMINUS(METHOD('moo')),
126
+ POWER(2, METHOD('bar')),
127
127
  nil,
128
- GTE(METHOD(nil, 'baz'), 5),
128
+ GTE(METHOD('baz'), 5),
129
129
  ASSIGN('foo', 10),
130
130
  HASH(),
131
131
  ARRAY(42, 43, 44),
@@ -134,69 +134,79 @@ describe Hotcell::Parser do
134
134
  end
135
135
 
136
136
  context 'expressions' do
137
- specify { parse('{{ 2 + hello }}').should be_equal_node_to JOINER(TAG(PLUS(2, METHOD(nil, 'hello')), mode: :normal)) }
137
+ specify { parse('{{ 2 + hello }}').should be_equal_node_to JOINER(TAG(PLUS(2, METHOD('hello')), mode: :normal)) }
138
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)) }
139
+ specify { parse('{{ --hello }}').should be_equal_node_to JOINER(TAG(UMINUS(UMINUS(METHOD('hello'))), mode: :normal)) }
140
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)) }
141
+ specify { parse('{{ 2 - hello }}').should be_equal_node_to JOINER(TAG(MINUS(2, METHOD('hello')), mode: :normal)) }
142
+ specify { parse('{{ 2 * hello }}').should be_equal_node_to JOINER(TAG(MULTIPLY(2, METHOD('hello')), mode: :normal)) }
143
+ specify { parse('{{ 2 / hello }}').should be_equal_node_to JOINER(TAG(DIVIDE(2, METHOD('hello')), mode: :normal)) }
144
+ specify { parse('{{ 2 % hello }}').should be_equal_node_to JOINER(TAG(MODULO(2, METHOD('hello')), mode: :normal)) }
145
+ specify { parse('{{ 2 ** hello }}').should be_equal_node_to JOINER(TAG(POWER(2, METHOD('hello')), mode: :normal)) }
146
+ specify { parse('{{ -hello }}').should be_equal_node_to JOINER(TAG(UMINUS(METHOD('hello')), mode: :normal)) }
147
+ specify { parse('{{ +hello }}').should be_equal_node_to JOINER(TAG(UPLUS(METHOD('hello')), mode: :normal)) }
148
148
  specify { parse('{{ -2 }}').should be_equal_node_to JOINER(TAG(-2, mode: :normal)) }
149
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)) }
150
+ specify { parse('{{ 2 + lol * 2 }}').should be_equal_node_to JOINER(TAG(PLUS(2, MULTIPLY(METHOD('lol'), 2)), mode: :normal)) }
151
+ specify { parse('{{ 2 + lol - 2 }}').should be_equal_node_to JOINER(TAG(MINUS(PLUS(2, METHOD('lol')), 2), mode: :normal)) }
152
+ specify { parse('{{ 2 ** foo * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(POWER(2, METHOD('foo')), 2), mode: :normal)) }
153
+ specify { parse('{{ 1 ** foo ** 3 }}').should be_equal_node_to JOINER(TAG(POWER(1, POWER(METHOD('foo'), 3)), mode: :normal)) }
154
+ specify { parse('{{ (2 + foo) * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(PLUS(2, METHOD('foo')), 2), mode: :normal)) }
155
155
  specify { parse('{{ (nil) }}').should be_equal_node_to JOINER(TAG(nil, mode: :normal)) }
156
156
  specify { parse('{{ (3) }}').should be_equal_node_to JOINER(TAG(3, mode: :normal)) }
157
157
  specify { parse('{{ (\'hello\') }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
158
158
  specify { parse('{{ () }}').should be_equal_node_to JOINER(TAG(nil, mode: :normal)) }
159
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)) }
160
+ specify { parse('{{ bar > 2 }}').should be_equal_node_to JOINER(TAG(GT(METHOD('bar'), 2), mode: :normal)) }
161
+ specify { parse('{{ 2 < bar }}').should be_equal_node_to JOINER(TAG(LT(2, METHOD('bar')), mode: :normal)) }
162
+ specify { parse('{{ 2 >= tru }}').should be_equal_node_to JOINER(TAG(GTE(2, METHOD('tru')), mode: :normal)) }
163
+ specify { parse('{{ some <= 2 }}').should be_equal_node_to JOINER(TAG(LTE(METHOD('some'), 2), mode: :normal)) }
164
164
  specify { parse('{{ 2 && false }}').should be_equal_node_to JOINER(TAG(false, mode: :normal)) }
165
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)) }
166
+ specify { parse('{{ 2 > bar < 2 }}').should be_equal_node_to JOINER(TAG(LT(GT(2, METHOD('bar')), 2), mode: :normal)) }
167
+ specify { parse('{{ 2 || bar && 2 }}').should be_equal_node_to JOINER(TAG(OR(2, AND(METHOD('bar'), 2)), mode: :normal)) }
168
+ specify { parse('{{ 2 && foo || 2 }}').should be_equal_node_to JOINER(TAG(OR(AND(2, METHOD('foo')), 2), mode: :normal)) }
169
+ specify { parse('{{ !2 && moo }}').should be_equal_node_to JOINER(TAG(AND(false, METHOD('moo')), mode: :normal)) }
170
+ specify { parse('{{ !(2 && moo) }}').should be_equal_node_to JOINER(TAG(NOT(AND(2, METHOD('moo'))), mode: :normal)) }
171
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)) }
172
+ specify { parse('{{ hello = bzz + 2 }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', PLUS(METHOD('bzz'), 2)), mode: :normal)) }
173
+ specify { parse('{{ hello = 2 ** bar }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', POWER(2, METHOD('bar'))), mode: :normal)) }
174
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)) }
175
+ specify { parse('{{ hello = 2 && var }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', AND(2, METHOD('var'))), mode: :normal)) }
176
+ specify { parse('{{ hello = world() }}').should be_equal_node_to JOINER(TAG(ASSIGN('hello', METHOD('world')), mode: :normal)) }
177
177
  specify { parse('{{ !hello = 2 >= 2 }}').should be_equal_node_to JOINER(TAG(NOT(ASSIGN('hello', true)), mode: :normal)) }
178
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)) }
179
+ specify { parse('{{ !foo ** 2 + 3 }}').should be_equal_node_to JOINER(TAG(PLUS(POWER(NOT(METHOD('foo')), 2), 3), mode: :normal)) }
180
+ specify { parse('{{ -bla ** 2 }}').should be_equal_node_to JOINER(TAG(UMINUS(POWER(METHOD('bla'), 2)), mode: :normal)) }
181
+ specify { parse('{{ -2 % bla }}').should be_equal_node_to JOINER(TAG(MODULO(-2, METHOD('bla')), mode: :normal)) }
182
+ specify { parse('{{ -hello ** 2 }}').should be_equal_node_to JOINER(TAG(UMINUS(POWER(METHOD('hello'), 2)), mode: :normal)) }
183
+ specify { parse('{{ -hello * 2 }}').should be_equal_node_to JOINER(TAG(MULTIPLY(UMINUS(METHOD('hello')), 2), mode: :normal)) }
184
+ specify { parse('{{ haha + 2 == 2 * 2 }}').should be_equal_node_to JOINER(TAG(EQUAL(PLUS(METHOD('haha'), 2), 4), mode: :normal)) }
185
+ specify { parse('{{ 2 * foo != 2 && bar }}').should be_equal_node_to JOINER(TAG(AND(INEQUAL(MULTIPLY(2, METHOD('foo')), 2), METHOD('bar')), mode: :normal)) }
186
186
 
187
187
  context 'method call' do
188
188
  specify { parse('{{ foo.bar.baz }}').should be_equal_node_to JOINER(TAG(
189
- METHOD(METHOD(METHOD(nil, 'foo'), 'bar'), 'baz'),
189
+ METHOD('baz', METHOD('bar', METHOD('foo'))),
190
+ mode: :normal)) }
191
+ specify { parse('{{ -bar.baz }}').should be_equal_node_to JOINER(TAG(
192
+ UMINUS(METHOD('baz', METHOD('bar'))),
193
+ mode: :normal)) }
194
+ specify { parse('{{ -42.baz }}').should be_equal_node_to JOINER(TAG(
195
+ METHOD('baz', -42),
196
+ mode: :normal)) }
197
+ specify { parse('{{ - 42.baz }}').should be_equal_node_to JOINER(TAG(
198
+ UMINUS(METHOD('baz', 42)),
199
+ mode: :normal)) }
200
+ specify { parse('{{ -42.42.baz }}').should be_equal_node_to JOINER(TAG(
201
+ METHOD('baz', -42.42),
190
202
  mode: :normal)) }
191
203
  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
204
+ METHOD('baz',
205
+ METHOD('manipulator_brackets',
206
+ METHOD('bar',
207
+ METHOD('foo', nil, 'hello')
208
+ ), 2
209
+ ), -42
200
210
  ),
201
211
  mode: :normal)) }
202
212
  end
@@ -206,8 +216,8 @@ describe Hotcell::Parser do
206
216
  specify { parse('{{ [] }}').should be_equal_node_to JOINER(TAG(ARRAY(), mode: :normal)) }
207
217
  specify { parse('{{ [ 2 ] }}').should be_equal_node_to JOINER(TAG(ARRAY(2), mode: :normal)) }
208
218
  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)) }
219
+ specify { parse('{{ [2, 3][42] }}').should be_equal_node_to JOINER(TAG(METHOD('manipulator_brackets', ARRAY(2, 3), 42), mode: :normal)) }
220
+ specify { parse('{{ [2 + foo, (2 * bar)] }}').should be_equal_node_to JOINER(TAG(ARRAY(PLUS(2, METHOD('foo')), MULTIPLY(2, METHOD('bar'))), mode: :normal)) }
211
221
  specify { parse('{{ [[2, 3], 42] }}').should be_equal_node_to JOINER(TAG(ARRAY(ARRAY(2, 3), 42), mode: :normal)) }
212
222
  end
213
223
 
@@ -217,48 +227,48 @@ describe Hotcell::Parser do
217
227
  JOINER(TAG(HASH(PAIR('hello', 'world')), mode: :normal))
218
228
  ) }
219
229
  specify { parse('{{ {hello: \'world\'}[\'hello\'] }}').should be_equal_node_to(
220
- JOINER(TAG(METHOD(HASH(PAIR('hello', 'world')), '[]', 'hello'), mode: :normal))
230
+ JOINER(TAG(METHOD('manipulator_brackets', HASH(PAIR('hello', 'world')), 'hello'), mode: :normal))
221
231
  ) }
222
232
  specify { parse('{{ { hello: 3, world: 6 * foo } }}').should be_equal_node_to(
223
233
  JOINER(TAG(HASH(
224
234
  PAIR('hello', 3),
225
- PAIR('world', MULTIPLY(6, METHOD(nil, 'foo')))
235
+ PAIR('world', MULTIPLY(6, METHOD('foo')))
226
236
  ), mode: :normal))
227
237
  ) }
228
238
  end
229
239
 
230
240
  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)) }
241
+ specify { parse('{{ hello[3] }}').should be_equal_node_to JOINER(TAG(METHOD('manipulator_brackets', METHOD('hello'), 3), mode: :normal)) }
242
+ specify { parse('{{ \'boom\'[3] }}').should be_equal_node_to JOINER(TAG(METHOD('manipulator_brackets', 'boom', 3), mode: :normal)) }
243
+ specify { parse('{{ 7[3] }}').should be_equal_node_to JOINER(TAG(METHOD('manipulator_brackets', 7, 3), mode: :normal)) }
244
+ specify { parse('{{ 3 + 5[7] }}').should be_equal_node_to JOINER(TAG(PLUS(3, METHOD('manipulator_brackets', 5, 7)), mode: :normal)) }
245
+ specify { parse('{{ (3 + 5)[7] }}').should be_equal_node_to JOINER(TAG(METHOD('manipulator_brackets', 8, 7), mode: :normal)) }
236
246
  end
237
247
 
238
248
  context 'function arguments' do
239
- specify { parse('{{ hello() }}').should be_equal_node_to JOINER(TAG(METHOD(nil, 'hello'), mode: :normal)) }
249
+ specify { parse('{{ hello() }}').should be_equal_node_to JOINER(TAG(METHOD('hello'), mode: :normal)) }
240
250
  specify { parse('{{ hello(2 * foo) }}').should be_equal_node_to(
241
- JOINER(TAG(METHOD(nil, 'hello', MULTIPLY(2, METHOD(nil, 'foo'))), mode: :normal))
251
+ JOINER(TAG(METHOD('hello', nil, MULTIPLY(2, METHOD('foo'))), mode: :normal))
242
252
  ) }
243
253
  specify { parse('{{ hello([2 * car]) }}').should be_equal_node_to(
244
- JOINER(TAG(METHOD(nil, 'hello', ARRAY(MULTIPLY(2, METHOD(nil, 'car')))), mode: :normal))
254
+ JOINER(TAG(METHOD('hello', nil, ARRAY(MULTIPLY(2, METHOD('car')))), mode: :normal))
245
255
  ) }
246
256
  specify { parse('{{ hello({hello: \'world\'}) }}').should be_equal_node_to(
247
- JOINER(TAG(METHOD(nil, 'hello', HASH(PAIR('hello', 'world'))), mode: :normal))
257
+ JOINER(TAG(METHOD('hello', nil, HASH(PAIR('hello', 'world'))), mode: :normal))
248
258
  ) }
249
259
  specify { parse('{{ hello(hello: \'world\') }}').should be_equal_node_to(
250
- JOINER(TAG(METHOD(nil, 'hello', HASH(PAIR('hello', 'world'))), mode: :normal))
260
+ JOINER(TAG(METHOD('hello', nil, HASH(PAIR('hello', 'world'))), mode: :normal))
251
261
  ) }
252
262
  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')),
263
+ JOINER(TAG(METHOD('hello', nil,
264
+ MULTIPLY(2, METHOD('foo')),
255
265
  'bla',
256
266
  HASH(PAIR('hello', 'world'))
257
267
  ), mode: :normal))
258
268
  ) }
259
269
  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),
270
+ JOINER(TAG(METHOD('hello', nil,
271
+ MULTIPLY(METHOD('moo'), 3),
262
272
  'bla',
263
273
  HASH(PAIR('hello', 'world'))
264
274
  ), mode: :normal))
@@ -280,9 +290,9 @@ describe Hotcell::Parser do
280
290
  specify { parse('{{ 42; \'hello\'; 44; }}').should be_equal_node_to JOINER(TAG(42, 'hello', 44, mode: :normal)) }
281
291
  specify { parse('{{ (42; \'hello\'); 44; }}').should be_equal_node_to JOINER(TAG(SEQUENCE(42, 'hello'), 44, mode: :normal)) }
282
292
  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)) }
293
+ specify { parse('{{ hello(42, (43; 44), 45) }}').should be_equal_node_to JOINER(TAG(METHOD('hello', nil, 42, SEQUENCE(43, 44), 45), mode: :normal)) }
294
+ specify { parse('{{ hello(42, ((43; 44)), 45) }}').should be_equal_node_to JOINER(TAG(METHOD('hello', nil, 42, SEQUENCE(43, 44), 45), mode: :normal)) }
295
+ specify { parse('{{ hello((42)) }}').should be_equal_node_to JOINER(TAG(METHOD('hello', nil, 42), mode: :normal)) }
286
296
  end
287
297
 
288
298
  context 'comments' do
@@ -292,91 +302,110 @@ describe Hotcell::Parser do
292
302
  specify { parse('hello {{!# world #}}').should be_equal_node_to JOINER('hello ', TAG(mode: :silence)) }
293
303
  specify { parse('hello {{ # world}}').should be_equal_node_to JOINER('hello ', TAG(mode: :normal)) }
294
304
  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)) }
305
+ specify { parse("hello {{ # world\n foo}}").should be_equal_node_to JOINER('hello ', TAG(METHOD('foo'), mode: :normal)) }
306
+ specify { parse("hello {{ world# foo}}").should be_equal_node_to JOINER('hello ', TAG(METHOD('world'), mode: :normal)) }
296
307
  end
297
308
 
298
309
  context 'commands' do
310
+ let(:include_tag) { Class.new(Hotcell::Command) }
311
+ let(:snippet_tag) { Class.new(Hotcell::Command) }
312
+ let(:commands) do
313
+ {
314
+ include: include_tag,
315
+ snippet: snippet_tag
316
+ }.stringify_keys
317
+ end
318
+
299
319
  specify { parse("{{ include 'some/partial' }}",
300
- commands: [:include, :snippet]).should be_equal_node_to JOINER(
301
- COMMAND('include', 'some/partial', mode: :normal)
320
+ commands: commands).should be_equal_node_to JOINER(
321
+ TAG(include_tag.build('include', 'some/partial'), mode: :normal)
302
322
  ) }
303
323
  specify { parse("{{ include }}",
304
- commands: [:include, :snippet]).should be_equal_node_to JOINER(
305
- COMMAND('include', mode: :normal)
324
+ commands: commands).should be_equal_node_to JOINER(
325
+ TAG(include_tag.build('include'), mode: :normal)
306
326
  ) }
307
327
  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),
328
+ commands: commands).should be_equal_node_to JOINER(
329
+ TAG(include_tag.build('include', 'some/partial'), mode: :silence),
310
330
  "\n",
311
- COMMAND('snippet', 'sidebar', mode: :normal),
331
+ TAG(snippet_tag.build('snippet', 'sidebar'), mode: :normal),
312
332
  ) }
313
333
  specify { parse("{{! variable = include }}",
314
- commands: [:include, :snippet]).should be_equal_node_to JOINER(
315
- COMMAND('include', mode: :silence, assign: 'variable')
334
+ commands: commands).should be_equal_node_to JOINER(
335
+ TAG(ASSIGN('variable', include_tag.build('include')), mode: :silence)
316
336
  ) }
317
337
  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')
338
+ commands: commands).should be_equal_node_to JOINER(
339
+ TAG(ASSIGN('variable', include_tag.build('include', 'some/partial')), mode: :normal)
320
340
  ) }
321
341
  end
322
342
 
323
343
  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
- ) }
344
+ let(:scoped_tag) { Class.new(Hotcell::Block) }
345
+ let(:each_tag) { Class.new(Hotcell::Block) }
346
+ let(:blocks) do
347
+ {
348
+ scoped: scoped_tag,
349
+ each: each_tag
350
+ }.stringify_keys
351
+ end
352
+
328
353
  specify { parse("{{ scoped }}{{ end scoped }}",
329
- blocks: [:scoped, :for]).should be_equal_node_to JOINER(
330
- BLOCK('scoped', mode: :normal)
354
+ blocks: blocks).should be_equal_node_to JOINER(
355
+ TAG(scoped_tag.build('scoped'), mode: :normal)
356
+ ) }
357
+ specify { parse("{{ scoped var: 'hello' }}{{ endscoped }}",
358
+ blocks: blocks).should be_equal_node_to JOINER(
359
+ TAG(scoped_tag.build('scoped', HASH(PAIR('var', 'hello'))), mode: :normal)
331
360
  ) }
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(
361
+ specify { parse("<article>\n{{ each post, in: posts }}\n<h1>{{ post.title }}</h1>\n{{ end each }}\n</article>",
362
+ blocks: blocks).should be_equal_node_to JOINER(
334
363
  "<article>\n",
335
- BLOCK('for',
336
- METHOD(nil, 'post'),
337
- HASH(PAIR('in', METHOD(nil, 'posts'))),
338
- mode: :normal,
364
+ TAG(each_tag.build('each',
365
+ METHOD('post'),
366
+ HASH(PAIR('in', METHOD('posts'))),
339
367
  subnodes: [JOINER(
340
368
  "\n<h1>",
341
- TAG(METHOD(METHOD(nil, 'post'), 'title'), mode: :normal),
369
+ TAG(METHOD('title', METHOD('post')), mode: :normal),
342
370
  "</h1>\n"
343
371
  )]
344
- ),
372
+ ), mode: :normal),
345
373
  "\n</article>"
346
374
  ) }
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',
375
+ specify { parse("{{! iter = each post, in: posts }}\n<h1>{{ post.title }}</h1>\n{{ end each }}",
376
+ blocks: blocks).should be_equal_node_to JOINER(
377
+ TAG(ASSIGN('iter', each_tag.build('each',
378
+ METHOD('post'),
379
+ HASH(PAIR('in', METHOD('posts'))),
354
380
  subnodes: [JOINER(
355
381
  "\n<h1>",
356
- TAG(METHOD(METHOD(nil, 'post'), 'title'), mode: :normal),
382
+ TAG(METHOD('title', METHOD('post')), mode: :normal),
357
383
  "</h1>\n"
358
384
  )]
359
- ),
385
+ )), mode: :silence),
360
386
  ) }
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',
387
+ specify { parse("{{ capture = scoped }} hello {{ each post, in: posts }} {{ loop }} {{ end each }}{{ endscoped }}",
388
+ blocks: blocks).should be_equal_node_to JOINER(
389
+ TAG(ASSIGN('capture', scoped_tag.build('scoped',
366
390
  subnodes: [JOINER(
367
391
  ' hello ',
368
- BLOCK('for',
369
- METHOD(nil, 'post'),
370
- HASH(PAIR('in', METHOD(nil, 'posts'))),
371
- mode: :normal,
392
+ TAG(each_tag.build('each',
393
+ METHOD('post'),
394
+ HASH(PAIR('in', METHOD('posts'))),
372
395
  subnodes: [JOINER(
373
396
  ' ',
374
- TAG(METHOD(nil, 'loop'), mode: :normal),
397
+ TAG(METHOD('loop'), mode: :normal),
375
398
  ' '
376
399
  )]
377
- )
400
+ ), mode: :normal)
378
401
  )]
379
- )
402
+ )), mode: :normal)
380
403
  ) }
381
404
  end
405
+
406
+ context 'errors' do
407
+ let(:error) { Hotcell::UnexpectedLexem }
408
+
409
+ specify { expect { parse("{{ var = 3 * 5; hello(, 3) }}") }.to raise_error error, 'Unexpected COMMA `,` at 1:23' }
410
+ end
382
411
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hotcell::Resolver do
4
+ specify { expect { subject.template 'any/path' }.to raise_error NotImplementedError }
5
+
6
+ describe '#template' do
7
+ let(:dummy) do
8
+ Class.new(described_class) do
9
+ def resolve path, context = nil
10
+ path
11
+ end
12
+ end
13
+ end
14
+ subject { dummy.new.template('template/source') }
15
+
16
+ it { should be_a Hotcell::Template }
17
+ its('source.source') { should == 'template/source' }
18
+ end
19
+ end
20
+
21
+ describe Hotcell::FileSystemResolver do
22
+ subject(:resolver) { described_class.new(data_path('templates')) }
23
+
24
+ describe '#resolve' do
25
+ specify { subject.resolve('simple').should == 'Hello, {{ name }}' }
26
+ specify { expect { subject.resolve('unexisting') }.to raise_error Errno::ENOENT }
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ # coding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Hotcell::Source do
6
+ subject(:source) { described_class.new('hello', 'file/path') }
7
+
8
+ describe '.wrap' do
9
+ specify { described_class.wrap('hello').should be_a described_class }
10
+ specify { described_class.wrap('hello').source.should == 'hello' }
11
+ specify { described_class.wrap(source).should === source }
12
+ end
13
+
14
+ describe '#initialize' do
15
+ its(:source) { should == 'hello' }
16
+ its(:file) { should == 'file/path' }
17
+ specify { described_class.new('hello').source.should == 'hello' }
18
+ specify { described_class.new('hello').file.should be_nil }
19
+ end
20
+
21
+ describe '#data' do
22
+ its(:data) { should == [104, 101, 108, 108, 111] }
23
+ end
24
+
25
+ describe '#info' do
26
+ def info source, position
27
+ described_class.new(source, 'file/path').info(position)
28
+ end
29
+
30
+ specify { info('hello', 0).should == { line: 1, column: 1 } }
31
+ specify { info('hello', 100).should == { line: 1, column: 5 } }
32
+ specify { info('hello', 2).should == { line: 1, column: 3 } }
33
+ specify { info('привет', 0).should == { line: 1, column: 1 } }
34
+ specify { info('привет', 4).should == { line: 1, column: 3 } }
35
+ specify { info('привет', 100).should == { line: 1, column: 6 } }
36
+ specify { info("привет\nhello", 0).should == { line: 1, column: 1 } }
37
+ specify { info("привет\nhello", 100).should == { line: 2, column: 5 } }
38
+ specify { info("привет\nhello", 15).should == { line: 2, column: 3 } }
39
+ specify { info("привет\nhello", 7).should == { line: 1, column: 4 } }
40
+ end
41
+ end
@@ -5,13 +5,10 @@ describe Hotcell::Template do
5
5
  before do
6
6
  Hotcell.stub(:commands) { { 'include' => Class.new } }
7
7
  Hotcell.stub(:blocks) { { 'for' => Class.new } }
8
- Hotcell.stub(:subcommands) { { 'else' => Class.new } }
9
8
  end
10
9
 
11
10
  specify { described_class.parse('').should be_a described_class }
12
- specify { described_class.parse('').options.should == {
13
- commands: ['include'], blocks: ['for'], subcommands: ['else']
14
- } }
11
+ specify { described_class.parse('').options.values.map(&:keys).should == [['include'], ['for']] }
15
12
  end
16
13
 
17
14
  describe '#syntax' do
@@ -37,5 +34,51 @@ describe Hotcell::Template do
37
34
  subject { described_class.new('Hello, {{! name }}!') }
38
35
  specify { subject.render(variables: { name: 'Friend' }).should == 'Hello, !' }
39
36
  end
37
+
38
+ context do
39
+ let(:helper) do
40
+ Module.new do
41
+ def name string
42
+ string.capitalize
43
+ end
44
+ end
45
+ end
46
+ subject { described_class.new("Hello, {{ name('pyra') }}!") }
47
+ specify { subject.render(helpers: helper).should == 'Hello, Pyra!' }
48
+
49
+ context do
50
+ before { Hotcell.stub(:helpers) { [helper] } }
51
+ specify { subject.render.should == 'Hello, Pyra!' }
52
+ specify { subject.render(helpers: []).should == 'Hello, !' }
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'render!' do
58
+ subject { described_class.new('Hello, {{ 2 * foo }}!') }
59
+ specify { expect { subject.render! }.to raise_error TypeError }
60
+ specify { expect { subject.render!(foo: 42) }.not_to raise_error }
61
+ specify { expect { subject.render!(bar: 42) }.to raise_error TypeError }
62
+ specify { expect { subject.render!(Hotcell::Context.new) }.to raise_error TypeError }
63
+ end
64
+
65
+ context 'complex tags test' do
66
+ specify { described_class.parse(<<-SOURCE
67
+ {{ for i, in: [1, 2, 3, 4] }}
68
+ {{ if i % 2 == 1 }}
69
+ {{ i }}
70
+ {{ end if }}
71
+ {{ end for }}
72
+ SOURCE
73
+ ).render.gsub(/[\s\n]+/, ' ').strip.should == '1 3' }
74
+
75
+ specify { described_class.parse(<<-SOURCE
76
+ {{ for i, in: [1, 2, 3, 4] }}
77
+ {{ for j, in: [4, 3, 2, 1] }}
78
+ {{ i * j }}
79
+ {{ end for }}
80
+ {{ end for }}
81
+ SOURCE
82
+ ).render.gsub(/[\s\n]+/, ' ').strip.should == '4 3 2 1 8 6 4 2 12 9 6 3 16 12 8 4' }
40
83
  end
41
84
  end
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  describe Hotcell do
4
4
  it { should respond_to :commands }
5
5
  it { should respond_to :blocks }
6
- it { should respond_to :subcommands }
6
+ it { should respond_to :helpers }
7
7
  it { should respond_to :register_command }
8
+ it { should respond_to :register_helpers }
8
9
  end
data/spec/spec_helper.rb CHANGED
@@ -9,7 +9,7 @@ AwesomePrint::Formatter.class_eval do
9
9
  end
10
10
 
11
11
  def awesome_hotcell_node(object)
12
- "#{colorize(object.class.name, :class)} #{colorize(object.name.to_s, :method)}" +
12
+ "#{colorize(object.class.to_s, :class)} #{colorize(object.name.to_s, :method)}" +
13
13
  ((awesome_hash(object.options) + "\n") if object.options.present?).to_s +
14
14
  awesome_array(object.children)
15
15
  end
@@ -28,8 +28,12 @@ RSpec::Matchers.define :be_equal_node_to do |expected|
28
28
  end
29
29
 
30
30
  RSpec.configure do |config|
31
+ def data_path name
32
+ File.join(File.dirname(__FILE__), 'data', name)
33
+ end
34
+
31
35
  def data name
32
- File.read File.join(File.dirname(__FILE__), 'data', name)
36
+ File.read data_path(name)
33
37
  end
34
38
 
35
39
  config.treat_symbols_as_metadata_keys_with_true_values = true