hotcell 0.2.0 → 0.3.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.
- 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
|