hotcell 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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