hotcell 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|