hotcell 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -1
- data/.rspec +1 -0
- data/.rvmrc +1 -1
- data/.travis.yml +7 -0
- data/Gemfile +4 -1
- data/README.md +361 -2
- data/Rakefile +28 -6
- data/ext/lexerc/extconf.rb +3 -0
- data/ext/lexerc/lexerc.c +618 -0
- data/ext/lexerc/lexerc.h +20 -0
- data/ext/lexerc/lexerc.rl +167 -0
- data/hotcell.gemspec +8 -7
- data/lib/hotcell/commands/case.rb +59 -0
- data/lib/hotcell/commands/cycle.rb +38 -0
- data/lib/hotcell/commands/for.rb +70 -0
- data/lib/hotcell/commands/if.rb +51 -0
- data/lib/hotcell/commands/include.rb +21 -0
- data/lib/hotcell/commands/scope.rb +13 -0
- data/lib/hotcell/commands/unless.rb +23 -0
- data/lib/hotcell/commands.rb +13 -0
- data/lib/hotcell/config.rb +33 -6
- data/lib/hotcell/context.rb +40 -7
- data/lib/hotcell/errors.rb +37 -28
- data/lib/hotcell/extensions.rb +4 -0
- data/lib/hotcell/lexer.rb +19 -635
- data/lib/hotcell/lexerr.rb +572 -0
- data/lib/hotcell/lexerr.rl +137 -0
- data/lib/hotcell/node/assigner.rb +1 -5
- data/lib/hotcell/node/block.rb +17 -40
- data/lib/hotcell/node/command.rb +29 -22
- data/lib/hotcell/node/hasher.rb +1 -1
- data/lib/hotcell/node/summoner.rb +2 -6
- data/lib/hotcell/node/tag.rb +10 -7
- data/lib/hotcell/node.rb +12 -1
- data/lib/hotcell/parser.rb +474 -408
- data/lib/hotcell/parser.y +175 -117
- data/lib/hotcell/resolver.rb +44 -0
- data/lib/hotcell/source.rb +35 -0
- data/lib/hotcell/template.rb +15 -6
- data/lib/hotcell/version.rb +1 -1
- data/lib/hotcell.rb +15 -10
- data/spec/data/templates/simple.hc +1 -0
- data/spec/lib/hotcell/commands/case_spec.rb +39 -0
- data/spec/lib/hotcell/commands/cycle_spec.rb +29 -0
- data/spec/lib/hotcell/commands/for_spec.rb +65 -0
- data/spec/lib/hotcell/commands/if_spec.rb +35 -0
- data/spec/lib/hotcell/commands/include_spec.rb +39 -0
- data/spec/lib/hotcell/commands/scope_spec.rb +16 -0
- data/spec/lib/hotcell/commands/unless_spec.rb +23 -0
- data/spec/lib/hotcell/config_spec.rb +35 -10
- data/spec/lib/hotcell/context_spec.rb +58 -18
- data/spec/lib/hotcell/lexer_spec.rb +37 -28
- data/spec/lib/hotcell/node/block_spec.rb +28 -56
- data/spec/lib/hotcell/node/command_spec.rb +7 -31
- data/spec/lib/hotcell/node/tag_spec.rb +16 -0
- data/spec/lib/hotcell/parser_spec.rb +152 -123
- data/spec/lib/hotcell/resolver_spec.rb +28 -0
- data/spec/lib/hotcell/source_spec.rb +41 -0
- data/spec/lib/hotcell/template_spec.rb +47 -4
- data/spec/lib/hotcell_spec.rb +2 -1
- data/spec/spec_helper.rb +6 -2
- metadata +54 -24
- data/lib/hotcell/.DS_Store +0 -0
- data/lib/hotcell/lexer.rl +0 -299
- data/misc/rage.rl +0 -1999
- data/misc/unicode2ragel.rb +0 -305
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Commands::If do
|
4
|
+
def parse source
|
5
|
+
Hotcell::Template.parse(source)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#validate!' do
|
9
|
+
specify { expect { parse('{{ if }}{{ end if }}').syntax
|
10
|
+
}.to raise_error Hotcell::ArgumentError, 'Wrond number of arguments for `if` (0 for 1) at 1:4' }
|
11
|
+
specify { expect { parse('{{ if true }}{{ else true }}{{ end if }}').syntax
|
12
|
+
}.to raise_error Hotcell::ArgumentError, 'Wrond number of arguments for `else` (1 for 0) at 1:17' }
|
13
|
+
specify { expect { parse('{{ if true }}{{ elsif }}{{ end if }}').syntax
|
14
|
+
}.to raise_error Hotcell::ArgumentError, 'Wrond number of arguments for `elsif` (0 for 1) at 1:17' }
|
15
|
+
specify { expect { parse('{{ if true }}{{ else }}{{ elsif true }}{{ end if }}').syntax
|
16
|
+
}.to raise_error Hotcell::BlockError, 'Unexpected `else` for `if` command at 1:17' }
|
17
|
+
specify { expect { parse('{{ if true }}{{ else true }}{{ elsif true }}{{ end if }}').syntax
|
18
|
+
}.to raise_error Hotcell::BlockError, 'Unexpected `else` for `if` command at 1:17' }
|
19
|
+
specify { expect { parse('{{ if true }}{{ else }}{{ else }}{{ end if }}').syntax
|
20
|
+
}.to raise_error Hotcell::BlockError, 'Unexpected `else` for `if` command at 1:17' }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#render' do
|
24
|
+
specify { parse('{{ if true }}Hello{{ end if }}').render.should == 'Hello' }
|
25
|
+
specify { parse('{{ if false }}Hello{{ end if }}').render.should == '' }
|
26
|
+
specify { parse('{{ if true }}Hello{{ else }}World{{ end if }}').render.should == 'Hello' }
|
27
|
+
specify { parse('{{ if false }}Hello{{ else }}World{{ end if }}').render.should == 'World' }
|
28
|
+
specify { parse('{{ if true }}Hello{{ elsif true }}Beautiful{{ else }}World{{ end if }}').render.should == 'Hello' }
|
29
|
+
specify { parse('{{ if false }}Hello{{ elsif true }}Beautiful{{ else }}World{{ end if }}').render.should == 'Beautiful' }
|
30
|
+
specify { parse('{{ if false }}Hello{{ elsif false }}Beautiful{{ else }}World{{ end if }}').render.should == 'World' }
|
31
|
+
specify { parse('{{ if false }}Hello{{ elsif true }}Beautiful{{ elsif true }}Ruby{{ else }}World{{ end if }}').render.should == 'Beautiful' }
|
32
|
+
specify { parse('{{ if false }}Hello{{ elsif false }}Beautiful{{ elsif true }}Ruby{{ else }}World{{ end if }}').render.should == 'Ruby' }
|
33
|
+
specify { parse('{{ if false }}Hello{{ elsif false }}Beautiful{{ elsif false }}Ruby{{ else }}World{{ end if }}').render.should == 'World' }
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Commands::Include do
|
4
|
+
def parse source
|
5
|
+
Hotcell::Template.parse(source)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:resolver_class) do
|
9
|
+
Class.new(Hotcell::Resolver) do
|
10
|
+
attr_reader :templates
|
11
|
+
def initialize templates
|
12
|
+
@templates = templates.stringify_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolve path, context = nil
|
16
|
+
templates[path] or raise 'Template not found'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:templates) { {
|
21
|
+
template1: 'Hello',
|
22
|
+
template2: 'Hello, {{ name }}',
|
23
|
+
template3: '{{ name }} - {{ action }}'
|
24
|
+
} }
|
25
|
+
let(:resolver) { resolver_class.new templates }
|
26
|
+
let(:render_options) { { shared: { resolver: resolver } } }
|
27
|
+
|
28
|
+
describe '#render' do
|
29
|
+
specify { parse("{{ include 'template0' }}").render(render_options).should =~ /Template not found/ }
|
30
|
+
specify { parse("{{ include 'template1' }}").render(render_options).should == 'Hello' }
|
31
|
+
specify { parse("{{ include 'template2' }}").render(render_options).should == 'Hello, ' }
|
32
|
+
specify { parse(
|
33
|
+
"{{ include 'template2', name: 'Pyrosha' }}"
|
34
|
+
).render(render_options).should == 'Hello, Pyrosha' }
|
35
|
+
specify { parse(
|
36
|
+
"{{! name = Pyrosha; action = 'CRUSH!' }}{{ include 'template3', name: 'Hulk' }}"
|
37
|
+
).render(render_options).should == 'Hulk - CRUSH!' }
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Commands::Scope do
|
4
|
+
def parse source
|
5
|
+
Hotcell::Template.parse(source)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#render' do
|
9
|
+
specify { parse(
|
10
|
+
'{{ count = 42 }} {{ scope count: count + 8 }}{{ count }}{{ end scope }} {{ count }}'
|
11
|
+
).render.should == '42 50 42' }
|
12
|
+
specify { parse(
|
13
|
+
'{{! captured = scope }}Hello{{ end scope }} {{ captured }}'
|
14
|
+
).render(reraise: true).should == ' Hello' }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Commands::Unless do
|
4
|
+
def parse source
|
5
|
+
Hotcell::Template.parse(source)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#validate!' do
|
9
|
+
specify { expect { parse('{{ unless }}{{ end unless }}').syntax
|
10
|
+
}.to raise_error Hotcell::ArgumentError, 'Wrond number of arguments for `unless` (0 for 1) at 1:4' }
|
11
|
+
specify { expect { parse('{{ unless true }}{{ else false }}{{ end unless }}').syntax
|
12
|
+
}.to raise_error Hotcell::ArgumentError, 'Wrond number of arguments for `else` (1 for 0) at 1:21' }
|
13
|
+
specify { expect { parse('{{ unless true }}{{ else }}{{ else }}{{ end unless }}').syntax
|
14
|
+
}.to raise_error Hotcell::BlockError, 'Unexpected `else` for `unless` command at 1:31' }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#render' do
|
18
|
+
specify { parse('{{ unless true }}Hello{{ end unless }}').render.should == '' }
|
19
|
+
specify { parse('{{ unless false }}Hello{{ end unless }}').render.should == 'Hello' }
|
20
|
+
specify { parse('{{ unless true }}Hello{{ else }}World{{ end unless }}').render.should == 'World' }
|
21
|
+
specify { parse('{{ unless false }}Hello{{ else }}World{{ end unless }}').render.should == 'Hello' }
|
22
|
+
end
|
23
|
+
end
|
@@ -6,36 +6,48 @@ describe Hotcell::Config do
|
|
6
6
|
let(:command_class) { Class.new(Hotcell::Command) }
|
7
7
|
let(:block_class) do
|
8
8
|
Class.new(Hotcell::Block) do
|
9
|
-
|
9
|
+
subcommand :else
|
10
|
+
subcommand :elsif
|
10
11
|
end
|
11
12
|
end
|
12
13
|
let(:misc_class) { Class.new }
|
13
14
|
|
14
15
|
specify { subject.blocks.should == {} }
|
15
|
-
specify { subject.subcommands.should == {} }
|
16
16
|
specify { subject.commands.should == {} }
|
17
|
+
specify { subject.helpers.should == [] }
|
18
|
+
specify { subject.resolver.should be_a Hotcell::Resolver }
|
19
|
+
|
20
|
+
describe '#resolver=' do
|
21
|
+
let(:resolver) { Hotcell::FileSystemResolver.new('/') }
|
22
|
+
before { subject.resolver = resolver }
|
23
|
+
its(:resolver) { should == resolver }
|
24
|
+
end
|
17
25
|
|
18
26
|
describe '#register_command' do
|
19
27
|
context do
|
20
28
|
before { subject.register_command :for, command_class }
|
21
|
-
|
22
|
-
|
23
|
-
specify { subject.commands.should == { 'for' => command_class } }
|
29
|
+
its(:blocks) { should == {} }
|
30
|
+
its(:commands) { should == { 'for' => command_class } }
|
24
31
|
end
|
25
32
|
|
26
33
|
context do
|
27
34
|
before { subject.register_command 'for', block_class }
|
28
|
-
|
29
|
-
|
30
|
-
specify { subject.commands.should == {} }
|
35
|
+
its(:blocks) { should == { 'for' => block_class } }
|
36
|
+
its(:commands) { should == {} }
|
31
37
|
end
|
32
38
|
|
33
39
|
context do
|
34
40
|
before { subject.register_command 'for', block_class }
|
35
41
|
before { subject.register_command :forloop, block_class }
|
36
42
|
before { subject.register_command :include, command_class }
|
37
|
-
|
38
|
-
|
43
|
+
its(:blocks) { should == { 'for' => block_class, 'forloop' => block_class } }
|
44
|
+
its(:commands) { should == { 'include' => command_class } }
|
45
|
+
end
|
46
|
+
|
47
|
+
context do
|
48
|
+
before { subject.register_command 'for' => block_class, forloop: block_class, include: command_class }
|
49
|
+
its(:blocks) { should == { 'for' => block_class, 'forloop' => block_class } }
|
50
|
+
its(:commands) { should == { 'include' => command_class } }
|
39
51
|
end
|
40
52
|
|
41
53
|
context 'errors' do
|
@@ -54,4 +66,17 @@ describe Hotcell::Config do
|
|
54
66
|
end
|
55
67
|
end
|
56
68
|
end
|
69
|
+
|
70
|
+
describe '#register_helpers' do
|
71
|
+
context do
|
72
|
+
3.times do |i|
|
73
|
+
let("helper#{i+1}") { Module.new }
|
74
|
+
end
|
75
|
+
before { subject.register_helpers helper1, helper2 }
|
76
|
+
before { subject.register_helpers helper1 }
|
77
|
+
before { subject.register_helpers [helper3] }
|
78
|
+
|
79
|
+
its(:helpers) { should == [helper1, helper2, helper3] }
|
80
|
+
end
|
81
|
+
end
|
57
82
|
end
|
@@ -1,25 +1,49 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Hotcell::Context do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
context do
|
8
|
-
subject { described_class.new(
|
9
|
-
scope: { foo: 42, 'bar' => 'baz' },
|
10
|
-
variables: { baz: 'moo' },
|
11
|
-
boo: 'goo',
|
12
|
-
'taz' => 'man',
|
13
|
-
rescuer: ->{},
|
14
|
-
reraise: true
|
15
|
-
) }
|
16
|
-
its('scope.scope') { should == [{foo: 42, 'bar' => 'baz', 'baz' => 'moo', 'boo' => 'goo', 'taz' => 'man'}] }
|
17
|
-
end
|
4
|
+
its('scope.scope') { should == [{}] }
|
5
|
+
its(:helpers) { should be_a Hotcell::Manipulator }
|
18
6
|
|
19
|
-
|
20
|
-
|
21
|
-
|
7
|
+
describe '#normalize_options' do
|
8
|
+
def result options = {}
|
9
|
+
default = { scope: {}, shared: {}, helpers: [], reraise: false, rescuer: described_class::DEFAULT_RESCUER }
|
10
|
+
default.merge options
|
22
11
|
end
|
12
|
+
let(:rscr) { ->{} }
|
13
|
+
|
14
|
+
specify { subject.normalize_options({}).should == result }
|
15
|
+
specify { subject.normalize_options(foo: 42, 'bar' => 'baz').should == result(
|
16
|
+
scope: { 'foo' => 42, 'bar' => 'baz' }) }
|
17
|
+
specify { subject.normalize_options(foo: 42, scope: { bar: 'baz' }).should == result(
|
18
|
+
scope: { 'foo' => 42, bar: 'baz' }) }
|
19
|
+
specify { subject.normalize_options(foo: 42, variables: { bar: 'baz' }).should == result(
|
20
|
+
scope: { 'foo' => 42, 'bar' => 'baz' }) }
|
21
|
+
specify { subject.normalize_options(foo: 42, environment: { 'bar' => 'baz' }).should == result(
|
22
|
+
scope: { 'foo' => 42, bar: 'baz' }) }
|
23
|
+
specify { subject.normalize_options(shared: {resolver: 'resolver'}).should == result(
|
24
|
+
shared: {resolver: 'resolver'}) }
|
25
|
+
specify { subject.normalize_options(helpers: 'helper1').should == result(
|
26
|
+
helpers: ['helper1']) }
|
27
|
+
specify { subject.normalize_options(helpers: ['helper1', 'helper2']).should == result(
|
28
|
+
helpers: ['helper1', 'helper2']) }
|
29
|
+
specify { subject.normalize_options(reraise: nil).should == result(reraise: false) }
|
30
|
+
specify { subject.normalize_options(reraise: true).should == result(reraise: true) }
|
31
|
+
specify { subject.normalize_options(reraise: 'foo').should == result(reraise: true) }
|
32
|
+
specify { subject.normalize_options(rescuer: rscr).should == result(rescuer: rscr) }
|
33
|
+
|
34
|
+
specify { subject.normalize_options(
|
35
|
+
scope: { foo: 42, 'bar' => 'baz' },
|
36
|
+
variables: { baz: 'moo' },
|
37
|
+
environment: { hello: 'world' },
|
38
|
+
boo: 'goo',
|
39
|
+
'taz' => 'man',
|
40
|
+
rescuer: rscr,
|
41
|
+
reraise: true
|
42
|
+
).should == result(
|
43
|
+
scope: { foo: 42, hello: 'world', 'bar' => 'baz', 'baz' => 'moo', 'boo' => 'goo', 'taz' => 'man' },
|
44
|
+
rescuer: rscr,
|
45
|
+
reraise: true
|
46
|
+
) }
|
23
47
|
end
|
24
48
|
|
25
49
|
describe '#safe' do
|
@@ -46,8 +70,24 @@ describe Hotcell::Context do
|
|
46
70
|
end
|
47
71
|
|
48
72
|
describe '#manipulator_invoke' do
|
49
|
-
subject { described_class.new(
|
73
|
+
subject { described_class.new(
|
74
|
+
variables: { foo: 42, 'bar' => 'baz' }, environment: { 'baz' => 'moo' },
|
75
|
+
helpers: Module.new do
|
76
|
+
def strip string
|
77
|
+
string.strip
|
78
|
+
end
|
79
|
+
|
80
|
+
def bar
|
81
|
+
'bazzzzz'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
) }
|
50
85
|
specify { subject.manipulator_invoke('foo').should == 42 }
|
51
86
|
specify { subject.manipulator_invoke('moo').should be_nil }
|
87
|
+
specify { subject.manipulator_invoke('baz').should be_nil }
|
88
|
+
specify { subject.manipulator_invoke('bar').should == 'baz' }
|
89
|
+
specify { expect { subject.manipulator_invoke('bar', 42) }.to raise_error ArgumentError }
|
90
|
+
specify { expect { subject.manipulator_invoke('strip') }.to raise_error ArgumentError }
|
91
|
+
specify { subject.manipulator_invoke('strip', ' hello ').should == 'hello' }
|
52
92
|
end
|
53
93
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
describe Hotcell::Lexer do
|
5
5
|
def scan template
|
6
|
-
described_class.new(template).tokens
|
6
|
+
described_class.new(template).tokens.map { |token| [token[0], token[1][0]] }
|
7
7
|
end
|
8
8
|
|
9
9
|
def expression template
|
@@ -65,7 +65,8 @@ describe Hotcell::Lexer do
|
|
65
65
|
context 'numeric' do
|
66
66
|
context 'integer' do
|
67
67
|
specify { expression('42').should == [[:INTEGER, 42]] }
|
68
|
-
specify { expression('-42').should == [[:
|
68
|
+
specify { expression('-42').should == [[:INTEGER, -42]] }
|
69
|
+
specify { expression('- 42').should == [[:MINUS, '-'], [:INTEGER, 42]] }
|
69
70
|
specify { expression('!42').should == [[:NOT, '!'], [:INTEGER, 42]] }
|
70
71
|
specify { expression('42.').should == [[:INTEGER, 42], [:PERIOD, '.']] }
|
71
72
|
specify { expression('42.foo').should == [[:INTEGER, 42], [:PERIOD, '.'], [:IDENTIFER, 'foo']] }
|
@@ -74,7 +75,8 @@ describe Hotcell::Lexer do
|
|
74
75
|
|
75
76
|
context 'float' do
|
76
77
|
specify { expression('42.42').should == [[:FLOAT, 42.42]] }
|
77
|
-
specify { expression('-42.42').should == [[:
|
78
|
+
specify { expression('-42.42').should == [[:FLOAT, -42.42]] }
|
79
|
+
specify { expression('- 42.42').should == [[:MINUS, '-'], [:FLOAT, 42.42]] }
|
78
80
|
specify { expression('!42.42').should == [[:NOT, '!'], [:FLOAT, 42.42]] }
|
79
81
|
specify { expression('0.42').should == [[:FLOAT, 0.42]] }
|
80
82
|
specify { expression('.42').should == [[:FLOAT, 0.42]] }
|
@@ -146,7 +148,8 @@ describe Hotcell::Lexer do
|
|
146
148
|
specify { expression(%q{"fo\mo"}).should == [[:STRING, "fo\mo"]] }
|
147
149
|
specify { expression(%q{"foo42"}).should == [[:STRING, "foo42"]] }
|
148
150
|
specify { expression(%q{"привет"}).should == [[:STRING, "привет"]] }
|
149
|
-
|
151
|
+
# RBX can not handle this
|
152
|
+
# specify { expression(%q{"при\вет"}).should == [[:STRING, "при\вет"]] }
|
150
153
|
|
151
154
|
context do
|
152
155
|
let(:strings) { data 'dstrings' }
|
@@ -168,8 +171,11 @@ describe Hotcell::Lexer do
|
|
168
171
|
specify { expression('/regexp/x').should == [[:REGEXP, /regexp/x]] }
|
169
172
|
specify { expression('/regexp/sdmfri').should == [[:REGEXP, /regexp/im]] }
|
170
173
|
specify { expression('/\.*/').should == [[:REGEXP, /\.*/]] }
|
171
|
-
|
172
|
-
specify { expression('/\//
|
174
|
+
# Funny ruby 2.0 bug. regexp1.to_s == regexp2.to_s, but regexp1 != regexp2
|
175
|
+
# specify { expression('/\//').should == [[:REGEXP, /\//]] }
|
176
|
+
# specify { expression('/\//ix').should == [[:REGEXP, /\//ix]] }
|
177
|
+
specify { expression('/\//').to_s.should == [[:REGEXP, /\//]].to_s }
|
178
|
+
specify { expression('/\//ix').to_s.should == [[:REGEXP, /\//ix]].to_s }
|
173
179
|
specify { expression('/регексп/').should == [[:REGEXP, /регексп/]] }
|
174
180
|
specify { expression('/регексп/m').should == [[:REGEXP, /регексп/m]] }
|
175
181
|
|
@@ -219,32 +225,32 @@ describe Hotcell::Lexer do
|
|
219
225
|
end
|
220
226
|
|
221
227
|
context 'errors' do
|
222
|
-
describe Hotcell::
|
223
|
-
let(:error) { Hotcell::
|
228
|
+
describe Hotcell::UnexpectedSymbol do
|
229
|
+
let(:error) { Hotcell::UnexpectedSymbol }
|
224
230
|
|
225
|
-
specify { expect { expression("hello @world") }.to raise_error(error,
|
226
|
-
specify { expect { expression("@hello world") }.to raise_error(error,
|
227
|
-
specify { expect { expression("hello world@") }.to raise_error(error,
|
228
|
-
specify { expect { expression("hello\n@ world") }.to raise_error(error,
|
229
|
-
specify { expect { expression("hello\n @world") }.to raise_error(error,
|
230
|
-
specify { expect { expression("hello\n world@") }.to raise_error(error,
|
231
|
-
specify { expect { expression("hello@\n world") }.to raise_error(error,
|
232
|
-
specify { expect { expression("@hello\n world") }.to raise_error(error,
|
233
|
-
specify { expect { expression("'привет' @ 'мир'") }.to raise_error(error,
|
231
|
+
specify { expect { expression("hello @world") }.to raise_error(error, /`@`.*1:10/) }
|
232
|
+
specify { expect { expression("@hello world") }.to raise_error(error, /`@`.*1:4/) }
|
233
|
+
specify { expect { expression("hello world@") }.to raise_error(error, /`@`.*1:15/) }
|
234
|
+
specify { expect { expression("hello\n@ world") }.to raise_error(error, /`@`.*2:1/) }
|
235
|
+
specify { expect { expression("hello\n @world") }.to raise_error(error, /`@`.*2:2/) }
|
236
|
+
specify { expect { expression("hello\n world@") }.to raise_error(error, /`@`.*2:7/) }
|
237
|
+
specify { expect { expression("hello@\n world") }.to raise_error(error, /`@`.*1:9/) }
|
238
|
+
specify { expect { expression("@hello\n world") }.to raise_error(error, /`@`.*1:4/) }
|
239
|
+
specify { expect { expression("'привет' @ 'мир'") }.to raise_error(error, /`@`.*1:13/) }
|
234
240
|
end
|
235
241
|
|
236
|
-
describe Hotcell::
|
237
|
-
let(:error) { Hotcell::
|
242
|
+
describe Hotcell::UnterminatedString do
|
243
|
+
let(:error) { Hotcell::UnterminatedString }
|
238
244
|
|
239
|
-
specify { expect { expression("hello 'world") }.to raise_error(error,
|
240
|
-
specify { expect { expression("hello\nwor'ld") }.to raise_error(error,
|
241
|
-
specify { expect { expression("hello 'world\\'") }.to raise_error(error,
|
242
|
-
specify { expect { expression("hello 'wor\\'ld") }.to raise_error(error,
|
243
|
-
specify { expect { expression("\"hello world") }.to raise_error(error,
|
244
|
-
specify { expect { expression("he\"llo\\\" world") }.to raise_error(error,
|
245
|
-
specify { expect { expression("he\"llo\\\" \nworld") }.to raise_error(error,
|
246
|
-
specify { expect { expression("\"hello\\\"\n world") }.to raise_error(error,
|
247
|
-
specify { expect { expression("'привет' 'мир") }.to raise_error(error,
|
245
|
+
specify { expect { expression("hello 'world") }.to raise_error(error, /`'world }}`.*1:10/) }
|
246
|
+
specify { expect { expression("hello\nwor'ld") }.to raise_error(error, /`'ld }}`.*2:4/) }
|
247
|
+
specify { expect { expression("hello 'world\\'") }.to raise_error(error, /`'world\\' }}`.*1:10/) }
|
248
|
+
specify { expect { expression("hello 'wor\\'ld") }.to raise_error(error, /`'wor\\'ld }}`.*1:10/) }
|
249
|
+
specify { expect { expression("\"hello world") }.to raise_error(error, /`"hello world }}`.*1:4/) }
|
250
|
+
specify { expect { expression("he\"llo\\\" world") }.to raise_error(error, /`"llo\\" world }}`.*1:6/) }
|
251
|
+
specify { expect { expression("he\"llo\\\" \nworld") }.to raise_error(error, /`"llo\\" \nworld }}`.*1:6/) }
|
252
|
+
specify { expect { expression("\"hello\\\"\n world") }.to raise_error(error, /`"hello\\"\n world }}`.*1:4/) }
|
253
|
+
specify { expect { expression("'привет' 'мир") }.to raise_error(error, /`'мир }}`.*1:13/) }
|
248
254
|
end
|
249
255
|
end
|
250
256
|
|
@@ -326,6 +332,9 @@ describe Hotcell::Lexer do
|
|
326
332
|
|
327
333
|
context 'template comments' do
|
328
334
|
specify { scan('{{#').should == [[:COMMENT, "{{#"]] }
|
335
|
+
specify { scan('{{{#').should == [[:TOPEN, "{{"], [:HOPEN, "{"], [:COMMENT, "#"]] }
|
336
|
+
specify { scan('{{{{#').should == [[:TOPEN, "{{"], [:HOPEN, "{"], [:HOPEN, "{"], [:COMMENT, "#"]] }
|
337
|
+
specify { scan('{#').should == [[:TEMPLATE, "{#"]] }
|
329
338
|
specify { scan('{{# }}').should == [[:COMMENT, "{{# }}"]] }
|
330
339
|
specify { scan('{{##}}').should == [[:COMMENT, "{{##}}"]] }
|
331
340
|
specify { scan('{{###}}').should == [[:COMMENT, "{{###}}"]] }
|
@@ -21,32 +21,33 @@ describe Hotcell::Block do
|
|
21
21
|
|
22
22
|
let(:context) { Hotcell::Context.new }
|
23
23
|
|
24
|
-
|
24
|
+
context 'complex parsing and rendering' do
|
25
25
|
def parse source
|
26
26
|
Hotcell::Template.parse(source)
|
27
27
|
end
|
28
28
|
|
29
29
|
let(:if_tag) do
|
30
30
|
Class.new(described_class) do
|
31
|
-
|
31
|
+
subcommand :else
|
32
|
+
subcommand :elsif
|
32
33
|
|
33
34
|
def validate!
|
34
|
-
names = subcommands.map { |subcommand| subcommand
|
35
|
+
names = subcommands.map { |subcommand| subcommand.name }
|
35
36
|
valid = names.empty? || (
|
36
|
-
names.any? &&
|
37
|
-
|
37
|
+
names.any? && %w(elsif else).include?(names.last) &&
|
38
|
+
[['elsif'], []].include?(names[0..-2].uniq)
|
38
39
|
)
|
39
|
-
raise Hotcell::
|
40
|
+
raise Hotcell::BlockError.new 'Invalid if syntax', *position_info unless valid
|
40
41
|
end
|
41
42
|
|
42
|
-
def process context,
|
43
|
+
def process context, condition
|
43
44
|
conditions = [[condition]]
|
44
45
|
subnodes.each do |subnode|
|
45
|
-
if subnode.is_a?(
|
46
|
+
if subnode.is_a?(Hotcell::Command)
|
46
47
|
conditions.last[1] = '' if conditions.last[1] == nil
|
47
|
-
conditions << (subnode
|
48
|
+
conditions << (subnode.name == 'elsif' ? [subnode.render_children(context).first] : [true])
|
48
49
|
else
|
49
|
-
conditions.last[1] = subnode
|
50
|
+
conditions.last[1] = subnode.render(context)
|
50
51
|
end
|
51
52
|
end
|
52
53
|
condition = conditions.detect { |condition| !!condition[0] }
|
@@ -57,7 +58,11 @@ describe Hotcell::Block do
|
|
57
58
|
|
58
59
|
before { Hotcell.stub(:commands) { {} } }
|
59
60
|
before { Hotcell.stub(:blocks) { { 'if' => if_tag } } }
|
60
|
-
|
61
|
+
|
62
|
+
specify { parse('{{ if true }}Hello{{ end if }}').render.should == 'Hello' }
|
63
|
+
specify { parse('{{! if true }}Hello{{ end if }}').render.should == '' }
|
64
|
+
specify { parse('{{ res = if true }}Hello{{ end if }} {{ res }}').render.should == 'Hello Hello' }
|
65
|
+
specify { parse('{{! res = if true }}Hello{{ end if }} {{ res }}').render.should == ' Hello' }
|
61
66
|
|
62
67
|
context do
|
63
68
|
subject(:template) { parse(<<-SOURCE
|
@@ -91,79 +96,46 @@ describe Hotcell::Block do
|
|
91
96
|
context do
|
92
97
|
specify { expect {
|
93
98
|
parse("{{ if value == 'hello' }}{{ else }}world{{ elsif }}{{ end if }}").syntax
|
94
|
-
}.to raise_error Hotcell::
|
99
|
+
}.to raise_error Hotcell::BlockError }
|
95
100
|
end
|
96
101
|
end
|
97
102
|
|
98
103
|
describe '.subcommands' do
|
99
104
|
subject { Class.new(described_class) }
|
100
105
|
|
101
|
-
before { subject.
|
102
|
-
its(
|
106
|
+
before { subject.subcommand elsif: Class.new(Hotcell::Command), else: Class.new(Hotcell::Command) }
|
107
|
+
its('subcommands.keys') { should == %w(elsif else) }
|
103
108
|
|
104
109
|
context do
|
105
|
-
before { subject.
|
106
|
-
its(
|
110
|
+
before { subject.subcommand :when }
|
111
|
+
its('subcommands.keys') { should == %w(elsif else when) }
|
107
112
|
end
|
108
113
|
end
|
109
114
|
|
110
115
|
describe '#subcommands' do
|
111
116
|
specify { described_class.new('if',
|
112
|
-
subnodes: [
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
describe '#render_subnodes' do
|
117
|
-
specify { described_class.new('if',
|
118
|
-
subnodes: [{name: 'elsif', args: ARRAY(3, 5)}, JOINER(42), {name: 'else'}, JOINER(43)]
|
119
|
-
).render_subnodes(context).should == [
|
120
|
-
{name: 'elsif', args: [3, 5]}, '42', {name: 'else'}, '43'
|
121
|
-
] }
|
117
|
+
subnodes: [COMMAND('elsif'), JOINER(), COMMAND('else'), JOINER()]).subcommands.should == [
|
118
|
+
COMMAND('elsif'), COMMAND('else')
|
119
|
+
] }
|
122
120
|
end
|
123
121
|
|
124
122
|
describe '#render' do
|
125
123
|
let(:block) do
|
126
124
|
Class.new(described_class) do
|
127
|
-
def process context,
|
125
|
+
def process context, condition
|
128
126
|
"condition #{condition}"
|
129
127
|
end
|
130
128
|
end
|
131
129
|
end
|
132
130
|
|
133
131
|
specify { block.new('if').render(context).should =~ /ArgumentError/ }
|
134
|
-
specify { block.new('if', mode: :silence).render(context).should =~ /ArgumentError/ }
|
135
132
|
specify { block.new('if', true).render(context).should == "condition true" }
|
136
|
-
specify { block.new('if', true, mode: :silence).render(context).should == '' }
|
137
|
-
|
138
|
-
context 'assigning' do
|
139
|
-
before { subject.render(context) }
|
140
|
-
|
141
|
-
context do
|
142
|
-
subject { block.new('if', assign: 'result') }
|
143
|
-
specify { context.key?('result').should be_false }
|
144
|
-
end
|
145
|
-
|
146
|
-
context do
|
147
|
-
subject { block.new('if', mode: :silence, assign: 'result') }
|
148
|
-
specify { context.key?('result').should be_false }
|
149
|
-
end
|
150
|
-
|
151
|
-
context do
|
152
|
-
subject { block.new('if', true, assign: 'result') }
|
153
|
-
specify { context['result'].should == "condition true" }
|
154
|
-
end
|
155
|
-
|
156
|
-
context do
|
157
|
-
subject { block.new('if', 42, mode: :silence, assign: 'result') }
|
158
|
-
specify { context['result'].should == "condition 42" }
|
159
|
-
end
|
160
|
-
end
|
161
133
|
end
|
162
134
|
|
163
135
|
context '#validate!' do
|
164
136
|
let(:block) do
|
165
137
|
Class.new(described_class) do
|
166
|
-
|
138
|
+
subcommand :else
|
167
139
|
def process context, subnodes, condition
|
168
140
|
"condition #{condition}"
|
169
141
|
end
|
@@ -176,12 +148,12 @@ describe Hotcell::Block do
|
|
176
148
|
end
|
177
149
|
|
178
150
|
context do
|
179
|
-
subject { block.new('if', true, subnodes: [
|
151
|
+
subject { block.new('if', true, subnodes: [COMMAND('else')] ) }
|
180
152
|
specify { expect { subject.validate! }.not_to raise_error }
|
181
153
|
end
|
182
154
|
|
183
155
|
context do
|
184
|
-
subject { block.new('if', true, subnodes: [
|
156
|
+
subject { block.new('if', true, subnodes: [COMMAND('case')] ) }
|
185
157
|
specify { expect { subject.validate! }.to raise_error }
|
186
158
|
end
|
187
159
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Hotcell::Command do
|
4
4
|
let(:context) { Hotcell::Context.new }
|
5
5
|
|
6
|
-
|
6
|
+
context 'complex parsing and rendering' do
|
7
7
|
def parse source
|
8
8
|
Hotcell::Template.parse(source)
|
9
9
|
end
|
@@ -11,7 +11,7 @@ describe Hotcell::Command do
|
|
11
11
|
let(:include_tag) do
|
12
12
|
Class.new(described_class) do
|
13
13
|
def validate!
|
14
|
-
raise Hotcell::
|
14
|
+
raise Hotcell::ArgumentError.new('Template path is required', *position_info) if children.count != 1
|
15
15
|
end
|
16
16
|
|
17
17
|
def process context, path
|
@@ -24,10 +24,12 @@ describe Hotcell::Command do
|
|
24
24
|
before { Hotcell.stub(:blocks) { {} } }
|
25
25
|
before { Hotcell.stub(:subcommands) { {} } }
|
26
26
|
|
27
|
+
specify { expect { parse("{{ include }}").syntax }.to raise_error Hotcell::ArgumentError }
|
27
28
|
specify { parse("{{ include 'template/path' }}").render(context).should == 'included template/path' }
|
28
|
-
specify {
|
29
|
-
|
30
|
-
}.
|
29
|
+
specify { parse("{{ include 'template/path' }}").render.should == 'included template/path' }
|
30
|
+
specify { parse("{{! include 'template/path' }}").render.should == '' }
|
31
|
+
specify { parse("{{ res = include 'template/path' }} {{ res }}").render.should == 'included template/path included template/path' }
|
32
|
+
specify { parse("{{! res = include 'template/path' }} {{ res }}").render.should == ' included template/path' }
|
31
33
|
end
|
32
34
|
|
33
35
|
describe '#render' do
|
@@ -40,32 +42,6 @@ describe Hotcell::Command do
|
|
40
42
|
end
|
41
43
|
|
42
44
|
specify { command.new('include').render(context).should =~ /ArgumentError/ }
|
43
|
-
specify { command.new('include', mode: :silence).render(context).should =~ /ArgumentError/ }
|
44
45
|
specify { command.new('include', 'path').render(context).should == "rendered path" }
|
45
|
-
specify { command.new('include', 'path', mode: :silence).render(context).should == '' }
|
46
|
-
|
47
|
-
context 'assigning' do
|
48
|
-
before { subject.render(context) }
|
49
|
-
|
50
|
-
context do
|
51
|
-
subject { command.new('include', assign: 'inclusion') }
|
52
|
-
specify { context.key?('inclusion').should be_false }
|
53
|
-
end
|
54
|
-
|
55
|
-
context do
|
56
|
-
subject { command.new('include', mode: :silence, assign: 'inclusion') }
|
57
|
-
specify { context.key?('inclusion').should be_false }
|
58
|
-
end
|
59
|
-
|
60
|
-
context do
|
61
|
-
subject { command.new('include', 'path', assign: 'inclusion') }
|
62
|
-
specify { context['inclusion'].should == "rendered path" }
|
63
|
-
end
|
64
|
-
|
65
|
-
context do
|
66
|
-
subject { command.new('include', 'path', mode: :silence, assign: 'inclusion') }
|
67
|
-
specify { context['inclusion'].should == "rendered path" }
|
68
|
-
end
|
69
|
-
end
|
70
46
|
end
|
71
47
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hotcell::Tag do
|
4
|
+
let(:context) { Hotcell::Context.new }
|
5
|
+
|
6
|
+
context 'complex parsing and rendering' do
|
7
|
+
def parse source
|
8
|
+
Hotcell::Template.parse(source)
|
9
|
+
end
|
10
|
+
|
11
|
+
specify { parse("{{ 'Hello' }}").render.should == 'Hello' }
|
12
|
+
specify { parse("{{! 'Hello' }}").render.should == '' }
|
13
|
+
specify { parse("{{ res = 'Hello' }} {{ res }}").render.should == 'Hello Hello' }
|
14
|
+
specify { parse("{{! res = 'Hello' }} {{ res }}").render.should == ' Hello' }
|
15
|
+
end
|
16
|
+
end
|