hotcell 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/Rakefile +2 -2
- data/ext/lexerc/extconf.rb +1 -1
- data/ext/lexerc/lexerc.c +771 -348
- data/ext/lexerc/lexerc.h +4 -1
- data/ext/lexerc/lexerc.rl +35 -1
- data/lib/hotcell.rb +1 -1
- data/lib/hotcell/commands/for.rb +1 -1
- data/lib/hotcell/context.rb +4 -4
- data/lib/hotcell/extensions.rb +13 -13
- data/lib/hotcell/lexer.rb +21 -2
- data/lib/hotcell/lexer.rl +27 -8
- data/lib/hotcell/lexerr.rb +579 -262
- data/lib/hotcell/lexerr.rl +33 -1
- data/lib/hotcell/node/expression.rb +21 -19
- data/lib/hotcell/node/summoner.rb +1 -1
- data/lib/hotcell/parser.rb +562 -458
- data/lib/hotcell/parser.y +26 -12
- data/lib/hotcell/tong.rb +54 -0
- data/lib/hotcell/version.rb +1 -1
- data/spec/data/{sstrings → strings} +0 -0
- data/spec/lib/hotcell/commands/include_spec.rb +1 -1
- data/spec/lib/hotcell/context_spec.rb +9 -9
- data/spec/lib/hotcell/lexer_spec.rb +57 -18
- data/spec/lib/hotcell/parser_spec.rb +48 -0
- data/spec/lib/hotcell/template_spec.rb +2 -2
- data/spec/lib/hotcell/tong_spec.rb +83 -0
- metadata +8 -8
- data/lib/hotcell/manipulator.rb +0 -49
- data/spec/lib/hotcell/manipulator_spec.rb +0 -69
data/lib/hotcell/parser.y
CHANGED
@@ -46,15 +46,15 @@ prechigh
|
|
46
46
|
left AND
|
47
47
|
left OR
|
48
48
|
nonassoc RANGE
|
49
|
-
right
|
49
|
+
right QUESTION
|
50
50
|
right ASSIGN
|
51
51
|
nonassoc COMMA COLON
|
52
52
|
left SEMICOLON NEWLINE
|
53
53
|
preclow
|
54
54
|
start document
|
55
55
|
rule
|
56
|
-
document: document document_unit { pospoppush(2); val[0].
|
57
|
-
| document_unit { result =
|
56
|
+
document: document document_unit { pospoppush(2); val[0].push(val[1]) }
|
57
|
+
| document_unit { result = [val[0]] }
|
58
58
|
document_unit: template | tag | block_tag | command_tag
|
59
59
|
|
60
60
|
template: TEMPLATE { result = val[0] }
|
@@ -155,6 +155,7 @@ rule
|
|
155
155
|
| expr EQUAL expr { result = build Expression, :EQUAL, val[0], val[2], position: pospoppush(3) }
|
156
156
|
| expr INEQUAL expr { result = build Expression, :INEQUAL, val[0], val[2], position: pospoppush(3) }
|
157
157
|
| NOT expr { result = build Expression, :NOT, val[1], position: pospoppush(2) }
|
158
|
+
| expr QUESTION expr COLON expr { result = build Expression, :TERNARY, val[0], val[2], val[4], position: pospoppush(5) }
|
158
159
|
| IDENTIFER ASSIGN expr { result = build Assigner, val[0], val[2], position: pospoppush(3) }
|
159
160
|
| expr PERIOD method { pospoppush(3); val[2].children[0] = val[0]; result = val[2] }
|
160
161
|
| expr AOPEN arguments ACLOSE {
|
@@ -162,21 +163,37 @@ rule
|
|
162
163
|
}
|
163
164
|
| POPEN PCLOSE { pospoppush(2); result = nil }
|
164
165
|
| POPEN sequence PCLOSE {
|
165
|
-
position = pospoppush(3)
|
166
166
|
result = case val[1].size
|
167
167
|
when 1
|
168
168
|
val[1][0]
|
169
169
|
else
|
170
|
-
build Sequencer, :SEQUENCE, *val[1].flatten, position:
|
170
|
+
build Sequencer, :SEQUENCE, *val[1].flatten, position: pospoppush(3)
|
171
171
|
end
|
172
172
|
}
|
173
173
|
| value
|
174
174
|
|
175
|
-
value: const | number | string | range | array | hash | method
|
175
|
+
value: const | number | string | dstring | regexp | range | array | hash | method
|
176
176
|
|
177
177
|
const: NIL | TRUE | FALSE
|
178
178
|
number: INTEGER | FLOAT
|
179
|
-
string: STRING
|
179
|
+
string: STRING
|
180
|
+
regexp: REGEXP
|
181
|
+
|
182
|
+
dstring: DOPEN dstring_content DCLOSE { result = build Expression, :DSTRING, *val[1], position: pospoppush(3) }
|
183
|
+
| DOPEN DCLOSE { pospoppush(2); result = '' }
|
184
|
+
dstring_content: STRING { result = [val[0]] }
|
185
|
+
| dstring_interpolation { result = [val[0]] }
|
186
|
+
| dstring_content STRING { pospoppush(2); val[0].push(val[1]); result = val[0] }
|
187
|
+
| dstring_content dstring_interpolation { pospoppush(2); val[0].push(val[1]); result = val[0] }
|
188
|
+
dstring_interpolation: IOPEN sequence ICLOSE {
|
189
|
+
result = case val[1].size
|
190
|
+
when 1
|
191
|
+
val[1][0]
|
192
|
+
else
|
193
|
+
build Sequencer, :SEQUENCE, *val[1].flatten, position: pospoppush(3)
|
194
|
+
end
|
195
|
+
}
|
196
|
+
| IOPEN ICLOSE { pospoppush(2); result = nil }
|
180
197
|
|
181
198
|
range: expr RANGE expr {
|
182
199
|
result = build Expression, val[1] == '..' ? :RANGE : :ERANGE,
|
@@ -260,11 +277,8 @@ rule
|
|
260
277
|
end
|
261
278
|
|
262
279
|
def parse
|
263
|
-
|
264
|
-
|
265
|
-
else
|
266
|
-
do_parse
|
267
|
-
end
|
280
|
+
children = @tokens.size.zero? ? [] : do_parse
|
281
|
+
build Joiner, :JOINER, *children, position: 0
|
268
282
|
end
|
269
283
|
|
270
284
|
def next_token
|
data/lib/hotcell/tong.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Tong
|
3
|
+
module Mixin
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :tong_methods, instance_writter: false
|
8
|
+
self.tong_methods = Set.new
|
9
|
+
|
10
|
+
def self.manipulate *methods
|
11
|
+
self.tong_methods = Set.new(tong_methods.to_a + methods.flatten.map(&:to_s))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_tong
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def tong_invoke method, *arguments
|
20
|
+
if method == '[]'
|
21
|
+
tong_invoke_brackets *arguments
|
22
|
+
elsif tong_invokable? method
|
23
|
+
send(method, *arguments)
|
24
|
+
else
|
25
|
+
tong_missing(method, *arguments)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def tong_missing method, *arguments
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def tong_invokable? method
|
35
|
+
tong_methods.include? method
|
36
|
+
end
|
37
|
+
|
38
|
+
def tong_invoke_brackets *arguments
|
39
|
+
if respond_to? :[]
|
40
|
+
self[*arguments]
|
41
|
+
else
|
42
|
+
tong_invoke *arguments
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
include Mixin
|
48
|
+
|
49
|
+
def tong_methods
|
50
|
+
@tong_methods ||= Set.new((self.class.public_instance_methods -
|
51
|
+
Hotcell::Tong.public_instance_methods).map(&:to_s))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/hotcell/version.rb
CHANGED
File without changes
|
@@ -27,7 +27,7 @@ describe Hotcell::Commands::Include do
|
|
27
27
|
|
28
28
|
describe '#render' do
|
29
29
|
specify { parse("{{ include 'template0' }}").render(render_options).should =~ /Template not found/ }
|
30
|
-
specify { parse("{{ include 'template1' }}").render(render_options).should == 'Hello' }
|
30
|
+
specify { parse("{{ include 'template1' }}").render!(render_options).should == 'Hello' }
|
31
31
|
specify { parse("{{ include 'template2' }}").render(render_options).should == 'Hello, ' }
|
32
32
|
specify { parse(
|
33
33
|
"{{ include 'template2', name: 'Pyrosha' }}"
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Hotcell::Context do
|
4
4
|
its('scope.scope') { should == [{}] }
|
5
|
-
its(:helpers) { should be_a Hotcell::
|
5
|
+
its(:helpers) { should be_a Hotcell::Tong }
|
6
6
|
|
7
7
|
describe '#normalize_options' do
|
8
8
|
def result options = {}
|
@@ -69,7 +69,7 @@ describe Hotcell::Context do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
describe '#
|
72
|
+
describe '#tong_invoke' do
|
73
73
|
subject { described_class.new(
|
74
74
|
variables: { foo: 42, 'bar' => 'baz' }, environment: { 'baz' => 'moo' },
|
75
75
|
helpers: Module.new do
|
@@ -82,12 +82,12 @@ describe Hotcell::Context do
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
) }
|
85
|
-
specify { subject.
|
86
|
-
specify { subject.
|
87
|
-
specify { subject.
|
88
|
-
specify { subject.
|
89
|
-
specify { expect { subject.
|
90
|
-
specify { expect { subject.
|
91
|
-
specify { subject.
|
85
|
+
specify { subject.tong_invoke('foo').should == 42 }
|
86
|
+
specify { subject.tong_invoke('moo').should be_nil }
|
87
|
+
specify { subject.tong_invoke('baz').should be_nil }
|
88
|
+
specify { subject.tong_invoke('bar').should == 'baz' }
|
89
|
+
specify { expect { subject.tong_invoke('bar', 42) }.to raise_error ArgumentError }
|
90
|
+
specify { expect { subject.tong_invoke('strip') }.to raise_error ArgumentError }
|
91
|
+
specify { subject.tong_invoke('strip', ' hello ').should == 'hello' }
|
92
92
|
end
|
93
93
|
end
|
@@ -105,13 +105,13 @@ describe Hotcell::Lexer do
|
|
105
105
|
|
106
106
|
context 'constants' do
|
107
107
|
specify { expression('nil').should == [[:NIL, nil]] }
|
108
|
-
specify { expression('"nil"').should == [[:STRING, 'nil']] }
|
108
|
+
specify { expression('"nil"').should == [[:DOPEN, '"'], [:STRING, 'nil'], [:DCLOSE, '"']] }
|
109
109
|
specify { expression('null').should == [[:NIL, nil]] }
|
110
|
-
specify { expression('"null"').should == [[:STRING, 'null']] }
|
110
|
+
specify { expression('"null"').should == [[:DOPEN, '"'], [:STRING, 'null'], [:DCLOSE, '"']] }
|
111
111
|
specify { expression('false').should == [[:FALSE, false]] }
|
112
|
-
specify { expression('"false"').should == [[:STRING, 'false']] }
|
112
|
+
specify { expression('"false"').should == [[:DOPEN, '"'], [:STRING, 'false'], [:DCLOSE, '"']] }
|
113
113
|
specify { expression('true').should == [[:TRUE, true]] }
|
114
|
-
specify { expression('"true"').should == [[:STRING, 'true']] }
|
114
|
+
specify { expression('"true"').should == [[:DOPEN, '"'], [:STRING, 'true'], [:DCLOSE, '"']] }
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -130,7 +130,7 @@ describe Hotcell::Lexer do
|
|
130
130
|
specify { expression(%q{'при\вет'}).should == [[:STRING, 'при\вет']] }
|
131
131
|
|
132
132
|
context do
|
133
|
-
let(:strings) { data '
|
133
|
+
let(:strings) { data 'strings' }
|
134
134
|
|
135
135
|
specify { expression(strings).delete_if { |token| token.first == :NEWLINE }.should == [
|
136
136
|
[:STRING, 'fo\'o'], [:STRING, 'fo\o'], [:STRING, 'fo\\o'],
|
@@ -141,28 +141,64 @@ describe Hotcell::Lexer do
|
|
141
141
|
|
142
142
|
context 'double quoted' do
|
143
143
|
specify { expression(%q{""}).should == [[:STRING, ""]] }
|
144
|
-
specify { expression(%q{"foo"}).should == [[:STRING, "foo"]] }
|
145
|
-
specify { expression(%q{"fo'o"}).should == [[:STRING, "fo'o"]] }
|
146
|
-
specify { expression(%q{"fo\o"}).should == [[:STRING, "fo\o"]] }
|
147
|
-
specify { expression(%q{"fo\"o"}).should == [[:STRING, "fo\"o"]] }
|
148
|
-
specify { expression(%q{"fo\'o"}).should == [[:STRING, "fo\'o"]] }
|
149
|
-
specify { expression(%q{"fo\no"}).should == [[:STRING, "fo\no"]] }
|
150
|
-
specify { expression(%q{"fo\mo"}).should == [[:STRING, "fo\mo"]] }
|
151
|
-
specify { expression(%q{"foo42"}).should == [[:STRING, "foo42"]] }
|
152
|
-
specify { expression(%q{"привет"}).should == [[:STRING, "привет"]] }
|
144
|
+
specify { expression(%q{"foo"}).should == [[:DOPEN, '"'], [:STRING, "foo"], [:DCLOSE, '"']] }
|
145
|
+
specify { expression(%q{"fo'o"}).should == [[:DOPEN, '"'], [:STRING, "fo'o"], [:DCLOSE, '"']] }
|
146
|
+
specify { expression(%q{"fo\o"}).should == [[:DOPEN, '"'], [:STRING, "fo\o"], [:DCLOSE, '"']] }
|
147
|
+
specify { expression(%q{"fo\"o"}).should == [[:DOPEN, '"'], [:STRING, "fo\"o"], [:DCLOSE, '"']] }
|
148
|
+
specify { expression(%q{"fo\'o"}).should == [[:DOPEN, '"'], [:STRING, "fo\'o"], [:DCLOSE, '"']] }
|
149
|
+
specify { expression(%q{"fo\no"}).should == [[:DOPEN, '"'], [:STRING, "fo\no"], [:DCLOSE, '"']] }
|
150
|
+
specify { expression(%q{"fo\mo"}).should == [[:DOPEN, '"'], [:STRING, "fo\mo"], [:DCLOSE, '"']] }
|
151
|
+
specify { expression(%q{"foo42"}).should == [[:DOPEN, '"'], [:STRING, "foo42"], [:DCLOSE, '"']] }
|
152
|
+
specify { expression(%q{"привет"}).should == [[:DOPEN, '"'], [:STRING, "привет"], [:DCLOSE, '"']] }
|
153
153
|
# RBX can not handle this
|
154
154
|
# specify { expression(%q{"при\вет"}).should == [[:STRING, "при\вет"]] }
|
155
155
|
|
156
156
|
context do
|
157
157
|
let(:strings) { data 'dstrings' }
|
158
158
|
|
159
|
-
specify { expression(strings).delete_if { |token|
|
159
|
+
specify { expression(strings).delete_if { |token|
|
160
|
+
[:NEWLINE, :DOPEN, :DCLOSE].include?(token.first)
|
161
|
+
}.should == [
|
160
162
|
[:STRING, "fo\"o"], [:STRING, "fo\o"], [:STRING, "fo\\o"],
|
161
163
|
[:STRING, "fo\no"], [:STRING, "fo\mo"], [:STRING, "fo\to"],
|
162
164
|
[:STRING, "foo\nbar"]
|
163
165
|
] }
|
164
166
|
end
|
165
167
|
end
|
168
|
+
|
169
|
+
context 'interpolation' do
|
170
|
+
specify { expression('"{ }"').should == [[:DOPEN, '"'], [:STRING, '{ }'], [:DCLOSE, '"']] }
|
171
|
+
specify { expression('"#"').should == [[:DOPEN, '"'], [:STRING, '#'], [:DCLOSE, '"']] }
|
172
|
+
specify { expression('"# "').should == [[:DOPEN, '"'], [:STRING, '# '], [:DCLOSE, '"']] }
|
173
|
+
specify { expression('" #"').should == [[:DOPEN, '"'], [:STRING, ' #'], [:DCLOSE, '"']] }
|
174
|
+
specify { expression('"#{ var }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:IDENTIFER, "var"],
|
175
|
+
[:ICLOSE, "}"], [:DCLOSE, '"']] }
|
176
|
+
specify { expression('"#{ } world"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:ICLOSE, "}"],
|
177
|
+
[:STRING, " world"], [:DCLOSE, '"']] }
|
178
|
+
specify { expression('"hello #{ }"').should == [[:DOPEN, '"'], [:STRING, "hello "], [:IOPEN, "\#{"],
|
179
|
+
[:ICLOSE, "}"], [:DCLOSE, '"']] }
|
180
|
+
specify { expression('"hello#{ } world"').should == [[:DOPEN, '"'], [:STRING, "hello"], [:IOPEN, "\#{"],
|
181
|
+
[:ICLOSE, "}"], [:STRING, " world"], [:DCLOSE, '"']] }
|
182
|
+
specify { expression('"hello #{ 2 + 3 }world"').should == [[:DOPEN, '"'], [:STRING, "hello "],
|
183
|
+
[:IOPEN, "\#{"], [:INTEGER, 2], [:PLUS, "+"], [:INTEGER, 3],
|
184
|
+
[:ICLOSE, "}"], [:STRING, "world"], [:DCLOSE, '"']] }
|
185
|
+
specify { expression('"\#{ var }"').should == [[:DOPEN, '"'], [:STRING, '#{ var }'], [:DCLOSE, '"']] }
|
186
|
+
specify { expression('"#{ } }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:ICLOSE, "}"],
|
187
|
+
[:STRING, ' }'], [:DCLOSE, '"']] }
|
188
|
+
specify { expression('"#{ { } }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:HOPEN, "{"],
|
189
|
+
[:HCLOSE, "}"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
|
190
|
+
specify { expression('"#{ [ ] }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:AOPEN, "["],
|
191
|
+
[:ACLOSE, "]"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
|
192
|
+
specify { expression('"#\{ }"').should == [[:DOPEN, '"'], [:STRING, '#{ }'], [:DCLOSE, '"']] }
|
193
|
+
specify { expression('"##{ }"').should == [[:DOPEN, '"'], [:STRING, '#'], [:IOPEN, "\#{"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
|
194
|
+
specify { expression('"###{ } #{ }"').should == [[:DOPEN, '"'], [:STRING, '##'], [:IOPEN, "\#{"],
|
195
|
+
[:ICLOSE, "}"], [:STRING, ' '], [:IOPEN, "\#{"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
|
196
|
+
|
197
|
+
context 'exceptions' do
|
198
|
+
specify { expect { expression('"#{"') }.to raise_error Hotcell::UnterminatedString, /`" }}`.*1:7/ }
|
199
|
+
specify { expect { expression('"#{ { }"') }.to raise_error Hotcell::UnterminatedString, /`" }}`.*1:11/ }
|
200
|
+
end
|
201
|
+
end
|
166
202
|
end
|
167
203
|
|
168
204
|
context 'regexp' do
|
@@ -204,7 +240,8 @@ describe Hotcell::Lexer do
|
|
204
240
|
[:INTEGER, 42], [:SEMICOLON, ";"], [:REGEXP, /regexp/]
|
205
241
|
] }
|
206
242
|
specify { expression('"hello" /regexp/').should == [
|
207
|
-
[:
|
243
|
+
[:DOPEN, '"'], [:STRING, "hello"], [:DCLOSE, '"'],
|
244
|
+
[:DIVIDE, "/"], [:IDENTIFER, "regexp"], [:DIVIDE, "/"]
|
208
245
|
] }
|
209
246
|
end
|
210
247
|
end
|
@@ -274,11 +311,13 @@ describe Hotcell::Lexer do
|
|
274
311
|
specify { expression("foo(36.6);\n a = \"привет\"").should == [
|
275
312
|
[:IDENTIFER, "foo"], [:POPEN, "("], [:FLOAT, 36.6],
|
276
313
|
[:PCLOSE, ")"], [:SEMICOLON, ";"], [:NEWLINE, "\n"],
|
277
|
-
[:IDENTIFER, "a"], [:ASSIGN, "="], [:
|
314
|
+
[:IDENTIFER, "a"], [:ASSIGN, "="], [:DOPEN, '"'],
|
315
|
+
[:STRING, "привет"], [:DCLOSE, '"']
|
278
316
|
] }
|
279
317
|
specify { expression("'foo'.match(\"^foo$\")").should == [
|
280
318
|
[:STRING, "foo"], [:PERIOD, "."], [:IDENTIFER, "match"],
|
281
|
-
[:POPEN, "("], [:
|
319
|
+
[:POPEN, "("], [:DOPEN, '"'], [:STRING, "^foo$"],
|
320
|
+
[:DCLOSE, '"'], [:PCLOSE, ")"]
|
282
321
|
] }
|
283
322
|
end
|
284
323
|
|
@@ -218,6 +218,54 @@ describe Hotcell::Parser do
|
|
218
218
|
end
|
219
219
|
end
|
220
220
|
|
221
|
+
context 'ternary operator' do
|
222
|
+
specify { parse('{{ true ? 42 : 43 }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
|
223
|
+
specify { parse('{{ false ? 42 : 43 }}').should be_equal_node_to JOINER(TAG(43, mode: :normal)) }
|
224
|
+
specify { parse('{{ cond ? branch1 : branch2 }}').should be_equal_node_to JOINER(
|
225
|
+
TAG(TERNARY(METHOD('cond'), METHOD('branch1'), METHOD('branch2')), mode: :normal)
|
226
|
+
) }
|
227
|
+
specify { parse('{{ cond1 ? branch1 : cond2 ? branch2 : branch3 }}').should be_equal_node_to JOINER(
|
228
|
+
TAG(TERNARY(
|
229
|
+
METHOD('cond1'), METHOD('branch1'),
|
230
|
+
TERNARY(METHOD('cond2'), METHOD('branch2'), METHOD('branch3'))
|
231
|
+
), mode: :normal)
|
232
|
+
) }
|
233
|
+
specify { parse('{{ (cond1 ? branch1 : cond2) ? branch2 : branch3 }}').should be_equal_node_to JOINER(
|
234
|
+
TAG(TERNARY(
|
235
|
+
TERNARY(METHOD('cond1'), METHOD('branch1'), METHOD('cond2')),
|
236
|
+
METHOD('branch2'), METHOD('branch3')
|
237
|
+
), mode: :normal)
|
238
|
+
) }
|
239
|
+
specify { parse('{{ cond1 ? cond2 ? branch1 : branch2 : branch3 }}').should be_equal_node_to JOINER(
|
240
|
+
TAG(TERNARY(
|
241
|
+
METHOD('cond1'),
|
242
|
+
TERNARY(METHOD('cond2'), METHOD('branch1'), METHOD('branch2')),
|
243
|
+
METHOD('branch3')
|
244
|
+
), mode: :normal)
|
245
|
+
) }
|
246
|
+
specify { parse('{{ res = cond ? branch1 + 3 : branch2 && true }}').should be_equal_node_to JOINER(
|
247
|
+
TAG(ASSIGN('res',
|
248
|
+
TERNARY(METHOD('cond'), PLUS(METHOD('branch1'), 3), AND(METHOD('branch2'), true)
|
249
|
+
)), mode: :normal)
|
250
|
+
) }
|
251
|
+
specify { expect { parse('{{ (1 ? 2) : 3 }}') }.to raise_error Hotcell::UnexpectedLexem, "Unexpected PCLOSE `)` at 1:10"}
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'interpolation' do
|
255
|
+
specify { parse('{{ "hello" }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
|
256
|
+
specify { parse('{{ \'#{}\' }}').should be_equal_node_to JOINER(TAG('#{}', mode: :normal)) }
|
257
|
+
specify { parse('{{ "#{}" }}').should be_equal_node_to JOINER(TAG('', mode: :normal)) }
|
258
|
+
specify { parse('{{ "#{ 3 }" }}').should be_equal_node_to JOINER(TAG('3', mode: :normal)) }
|
259
|
+
specify { parse('{{ "#{ var }" }}').should be_equal_node_to JOINER(TAG(DSTRING(METHOD('var')), mode: :normal)) }
|
260
|
+
specify { parse('{{ "#{ var }world" }}').should be_equal_node_to JOINER(TAG(DSTRING(METHOD('var'), 'world'), mode: :normal)) }
|
261
|
+
specify { parse('{{ "hello#{}" }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
|
262
|
+
specify { parse('{{ "hello#{}world" }}').should be_equal_node_to JOINER(TAG('helloworld', mode: :normal)) }
|
263
|
+
specify { parse('{{ "hello#{ 6 * 7 }world" }}').should be_equal_node_to JOINER(TAG('hello42world', mode: :normal)) }
|
264
|
+
specify { parse('{{ "hello#{ var = 6 * 7; var + 1 }world" }}').should be_equal_node_to JOINER(
|
265
|
+
TAG(DSTRING('hello', SEQUENCE(ASSIGN('var', 42), PLUS(METHOD('var'), 1)), 'world'), mode: :normal)
|
266
|
+
) }
|
267
|
+
end
|
268
|
+
|
221
269
|
context 'arrays' do
|
222
270
|
specify { parse('{{ [] }}').should be_equal_node_to JOINER(TAG(ARRAY(), mode: :normal)) }
|
223
271
|
specify { parse('{{ [ 2 ] }}').should be_equal_node_to JOINER(TAG(ARRAY(2), mode: :normal)) }
|
@@ -67,11 +67,11 @@ describe Hotcell::Template do
|
|
67
67
|
specify { described_class.parse(<<-SOURCE
|
68
68
|
{{ for i, in: [1, 2, 3, 4] }}
|
69
69
|
{{ if i % 2 == 1 }}
|
70
|
-
{{ i }}
|
70
|
+
{{ "number\#{i}" }}
|
71
71
|
{{ end if }}
|
72
72
|
{{ end for }}
|
73
73
|
SOURCE
|
74
|
-
).render.gsub(/[\s\n]+/, ' ').strip.should == '
|
74
|
+
).render.gsub(/[\s\n]+/, ' ').strip.should == 'number1 number3' }
|
75
75
|
|
76
76
|
specify { described_class.parse(<<-SOURCE
|
77
77
|
{{ for i, in: [1, 2, 3, 4] }}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Tong do
|
4
|
+
context 'mixed in' do
|
5
|
+
context do
|
6
|
+
let(:klass) do
|
7
|
+
Class.new(Numeric) do
|
8
|
+
include Hotcell::Tong::Mixin
|
9
|
+
|
10
|
+
def foo; end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
subject { klass.new }
|
14
|
+
|
15
|
+
its(:tong_methods) { should be_a Set }
|
16
|
+
its(:tong_methods) { should be_empty }
|
17
|
+
its(:to_tong) { should === subject }
|
18
|
+
specify { subject.tong_invoke('foo').should be_nil }
|
19
|
+
specify { subject.tong_invoke(:foo).should be_nil }
|
20
|
+
specify { subject.tong_invoke('foo', 42, :arg).should be_nil }
|
21
|
+
end
|
22
|
+
|
23
|
+
context do
|
24
|
+
let(:klass) do
|
25
|
+
Class.new(String) do
|
26
|
+
include Hotcell::Tong::Mixin
|
27
|
+
|
28
|
+
manipulate :foo, :bar
|
29
|
+
|
30
|
+
alias_method :foo, :split
|
31
|
+
alias_method :bar, :size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
subject { klass.new('hello world') }
|
35
|
+
|
36
|
+
its(:to_tong) { should === subject }
|
37
|
+
specify { (subject.tong_methods.to_a & %w(foo bar)).should =~ %w(foo bar) }
|
38
|
+
specify { subject.tong_invoke('baz').should be_nil }
|
39
|
+
specify { subject.tong_invoke('baz', 42, :arg).should be_nil }
|
40
|
+
specify { subject.tong_invoke(:foo).should be_nil }
|
41
|
+
specify { subject.tong_invoke('foo').should == %w(hello world) }
|
42
|
+
specify { subject.tong_invoke('bar').should == 11 }
|
43
|
+
specify { expect { subject.tong_invoke('bar', 42) }.to raise_error ArgumentError }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'inherited' do
|
48
|
+
let(:klass) do
|
49
|
+
Class.new(described_class) do
|
50
|
+
def foo
|
51
|
+
'foo'
|
52
|
+
end
|
53
|
+
|
54
|
+
def bar arg1, arg2
|
55
|
+
arg1 + arg2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
subject { klass.new }
|
60
|
+
|
61
|
+
its('tong_methods.to_a') { should =~ %w(foo bar) }
|
62
|
+
its(:to_tong) { should === subject }
|
63
|
+
specify { subject.tong_invoke('send').should be_nil }
|
64
|
+
specify { subject.tong_invoke(:bar).should be_nil }
|
65
|
+
specify { subject.tong_invoke('foo').should == 'foo' }
|
66
|
+
specify { subject.tong_invoke('bar', 5, 8).should == 13 }
|
67
|
+
specify { expect { subject.tong_invoke('bar') }.to raise_error ArgumentError }
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#tong_missing' do
|
71
|
+
let(:klass) do
|
72
|
+
Class.new(described_class) do
|
73
|
+
def tong_missing method, *args
|
74
|
+
"missing #{method} with #{args}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
subject { klass.new }
|
79
|
+
|
80
|
+
specify { subject.tong_invoke('foo').should == 'missing foo with []' }
|
81
|
+
specify { subject.tong_invoke('bar', 42).should == 'missing bar with [42]' }
|
82
|
+
end
|
83
|
+
end
|