curlybars 0.9.13
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/lib/curlybars.rb +108 -0
- data/lib/curlybars/configuration.rb +41 -0
- data/lib/curlybars/dependency_tracker.rb +8 -0
- data/lib/curlybars/error/base.rb +18 -0
- data/lib/curlybars/error/compile.rb +11 -0
- data/lib/curlybars/error/lex.rb +22 -0
- data/lib/curlybars/error/parse.rb +41 -0
- data/lib/curlybars/error/presenter/not_found.rb +23 -0
- data/lib/curlybars/error/render.rb +11 -0
- data/lib/curlybars/error/validate.rb +18 -0
- data/lib/curlybars/lexer.rb +60 -0
- data/lib/curlybars/method_whitelist.rb +69 -0
- data/lib/curlybars/node/block_helper_else.rb +108 -0
- data/lib/curlybars/node/boolean.rb +24 -0
- data/lib/curlybars/node/each_else.rb +69 -0
- data/lib/curlybars/node/if_else.rb +33 -0
- data/lib/curlybars/node/item.rb +31 -0
- data/lib/curlybars/node/literal.rb +28 -0
- data/lib/curlybars/node/option.rb +25 -0
- data/lib/curlybars/node/output.rb +24 -0
- data/lib/curlybars/node/partial.rb +24 -0
- data/lib/curlybars/node/path.rb +137 -0
- data/lib/curlybars/node/root.rb +29 -0
- data/lib/curlybars/node/string.rb +24 -0
- data/lib/curlybars/node/template.rb +32 -0
- data/lib/curlybars/node/text.rb +24 -0
- data/lib/curlybars/node/unless_else.rb +33 -0
- data/lib/curlybars/node/variable.rb +34 -0
- data/lib/curlybars/node/with_else.rb +54 -0
- data/lib/curlybars/parser.rb +183 -0
- data/lib/curlybars/position.rb +7 -0
- data/lib/curlybars/presenter.rb +288 -0
- data/lib/curlybars/processor/tilde.rb +31 -0
- data/lib/curlybars/processor/token_factory.rb +9 -0
- data/lib/curlybars/railtie.rb +18 -0
- data/lib/curlybars/rendering_support.rb +222 -0
- data/lib/curlybars/safe_buffer.rb +11 -0
- data/lib/curlybars/template_handler.rb +93 -0
- data/lib/curlybars/version.rb +3 -0
- data/spec/acceptance/application_layout_spec.rb +60 -0
- data/spec/acceptance/collection_blocks_spec.rb +28 -0
- data/spec/acceptance/global_helper_spec.rb +25 -0
- data/spec/curlybars/configuration_spec.rb +57 -0
- data/spec/curlybars/error/base_spec.rb +41 -0
- data/spec/curlybars/error/compile_spec.rb +19 -0
- data/spec/curlybars/error/lex_spec.rb +25 -0
- data/spec/curlybars/error/parse_spec.rb +74 -0
- data/spec/curlybars/error/render_spec.rb +19 -0
- data/spec/curlybars/error/validate_spec.rb +19 -0
- data/spec/curlybars/lexer_spec.rb +466 -0
- data/spec/curlybars/method_whitelist_spec.rb +168 -0
- data/spec/curlybars/processor/tilde_spec.rb +60 -0
- data/spec/curlybars/rendering_support_spec.rb +426 -0
- data/spec/curlybars/safe_buffer_spec.rb +46 -0
- data/spec/curlybars/template_handler_spec.rb +222 -0
- data/spec/integration/cache_spec.rb +124 -0
- data/spec/integration/comment_spec.rb +60 -0
- data/spec/integration/exception_spec.rb +31 -0
- data/spec/integration/node/block_helper_else_spec.rb +422 -0
- data/spec/integration/node/each_else_spec.rb +204 -0
- data/spec/integration/node/each_spec.rb +291 -0
- data/spec/integration/node/escape_spec.rb +27 -0
- data/spec/integration/node/helper_spec.rb +176 -0
- data/spec/integration/node/if_else_spec.rb +129 -0
- data/spec/integration/node/if_spec.rb +143 -0
- data/spec/integration/node/output_spec.rb +68 -0
- data/spec/integration/node/partial_spec.rb +66 -0
- data/spec/integration/node/path_spec.rb +286 -0
- data/spec/integration/node/root_spec.rb +15 -0
- data/spec/integration/node/template_spec.rb +86 -0
- data/spec/integration/node/unless_else_spec.rb +129 -0
- data/spec/integration/node/unless_spec.rb +130 -0
- data/spec/integration/node/with_spec.rb +116 -0
- data/spec/integration/processor/tilde_spec.rb +38 -0
- data/spec/integration/processors_spec.rb +30 -0
- metadata +358 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
describe "Collection blocks", type: :request do
|
2
|
+
before do
|
3
|
+
Curlybars.configure do |config|
|
4
|
+
config.presenters_namespace = 'curlybars_presenters'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
example "Rendering collections" do
|
9
|
+
get '/categories'
|
10
|
+
|
11
|
+
expect(body).to eq(<<-HTML.strip_heredoc)
|
12
|
+
<html>
|
13
|
+
<head>
|
14
|
+
<title>Dummy app</title>
|
15
|
+
</head>
|
16
|
+
<body>
|
17
|
+
|
18
|
+
<h1>This are the categories</h1>
|
19
|
+
|
20
|
+
<p>One</p>
|
21
|
+
<p>Two</p>
|
22
|
+
|
23
|
+
|
24
|
+
</body>
|
25
|
+
</html>
|
26
|
+
HTML
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe "Collection blocks", type: :request do
|
2
|
+
before do
|
3
|
+
Curlybars.configure do |config|
|
4
|
+
config.presenters_namespace = 'curlybars_presenters'
|
5
|
+
config.global_helpers_provider_classes = [GlobalHelpers]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
example "Render a global helper" do
|
10
|
+
get '/welcome'
|
11
|
+
|
12
|
+
expect(body).to eq(<<-HTML.strip_heredoc)
|
13
|
+
<html>
|
14
|
+
<head>
|
15
|
+
<title>Dummy app</title>
|
16
|
+
</head>
|
17
|
+
<body>
|
18
|
+
Login as: Admin
|
19
|
+
Account: Testing
|
20
|
+
|
21
|
+
</body>
|
22
|
+
</html>
|
23
|
+
HTML
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
describe Curlybars::Configuration do
|
2
|
+
after do
|
3
|
+
Curlybars.reset
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "#presenters_namespace" do
|
7
|
+
it "defaults to an empty string" do
|
8
|
+
presenters_namespace = Curlybars::Configuration.new.presenters_namespace
|
9
|
+
|
10
|
+
expect(presenters_namespace).to eq('')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#presenters_namespace=" do
|
15
|
+
it "can set value" do
|
16
|
+
config = Curlybars::Configuration.new
|
17
|
+
config.presenters_namespace = 'foo'
|
18
|
+
expect(config.presenters_namespace).to eq('foo')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#custom_processors" do
|
23
|
+
it "can set value" do
|
24
|
+
config = Curlybars::Configuration.new
|
25
|
+
config.custom_processors = ['test']
|
26
|
+
expect(config.custom_processors).to eq(['test'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".configure" do
|
31
|
+
before do
|
32
|
+
Curlybars.configure do |config|
|
33
|
+
config.presenters_namespace = 'bar'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns correct value for presenters_namespace" do
|
38
|
+
presenters_namespace = Curlybars.configuration.presenters_namespace
|
39
|
+
|
40
|
+
expect(presenters_namespace).to eq('bar')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".reset" do
|
45
|
+
it "resets the configuration to default value" do
|
46
|
+
Curlybars.configure do |config|
|
47
|
+
config.presenters_namespace = 'foobarbaz'
|
48
|
+
end
|
49
|
+
|
50
|
+
Curlybars.reset
|
51
|
+
|
52
|
+
presenters_namespace = Curlybars.configuration.presenters_namespace
|
53
|
+
|
54
|
+
expect(presenters_namespace).to eq('')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
describe Curlybars::Error::Base do
|
2
|
+
let(:position) do
|
3
|
+
OpenStruct.new(line_number: 1, line_offset: 0)
|
4
|
+
end
|
5
|
+
|
6
|
+
it "creates an exception with the given message" do
|
7
|
+
message = "message"
|
8
|
+
|
9
|
+
exception = Curlybars::Error::Base.new('id', message, position)
|
10
|
+
|
11
|
+
expect(exception.message).to be message
|
12
|
+
end
|
13
|
+
|
14
|
+
it "doesn't change the backtrace for nil position.file_name" do
|
15
|
+
exception = Curlybars::Error::Base.new('id', 'message', position)
|
16
|
+
|
17
|
+
expect(exception.backtrace).to be nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets the right backtrace for non-nil position.file_name" do
|
21
|
+
position.file_name = 'template.hbs'
|
22
|
+
|
23
|
+
exception = Curlybars::Error::Base.new('id', 'message', position)
|
24
|
+
|
25
|
+
expect(exception.backtrace).not_to be nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets the position as an instance varaible" do
|
29
|
+
exception = Curlybars::Error::Base.new('id', 'message', position)
|
30
|
+
|
31
|
+
expect(exception.position).to be position
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sets the id as an instance varaible" do
|
35
|
+
id = 'id'
|
36
|
+
|
37
|
+
exception = Curlybars::Error::Base.new(id, 'message', position)
|
38
|
+
|
39
|
+
expect(exception.id).to be id
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe Curlybars::Error::Compile do
|
2
|
+
let(:position) do
|
3
|
+
OpenStruct.new(
|
4
|
+
line_number: 2,
|
5
|
+
line_offset: 3,
|
6
|
+
stream_offset: 14,
|
7
|
+
length: 3,
|
8
|
+
file_name: 'template.hbs'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "prefixes the id with `compile.`" do
|
13
|
+
id = 'id'
|
14
|
+
|
15
|
+
exception = Curlybars::Error::Compile.new(id, 'message', position)
|
16
|
+
|
17
|
+
expect(exception.id).to eq 'compile.%s' % id
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe Curlybars::Error::Lex do
|
2
|
+
let(:file_name) { 'template.hbs' }
|
3
|
+
|
4
|
+
let(:source) { "first_line\n0123456789\nthird_line" }
|
5
|
+
|
6
|
+
let(:exception) do
|
7
|
+
OpenStruct.new(line_number: 2, line_offset: 2)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "creates an exception with a meaningful message" do
|
11
|
+
message = ".. 01 `2` 3456789 .. is not permitted symbol in this context"
|
12
|
+
|
13
|
+
lex_exception = Curlybars::Error::Lex.new(source, file_name, exception)
|
14
|
+
|
15
|
+
expect(lex_exception.message).to eq message
|
16
|
+
end
|
17
|
+
|
18
|
+
it "figures out a position from the exception" do
|
19
|
+
backtrace = "%s:%d:%d" % [file_name, exception.line_number, exception.line_offset]
|
20
|
+
|
21
|
+
lex_exception = Curlybars::Error::Lex.new(source, file_name, exception)
|
22
|
+
|
23
|
+
expect(lex_exception.backtrace).to eq [backtrace]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
describe Curlybars::Error::Parse do
|
2
|
+
let(:source) { "first_line\n0123456789\nthird_line" }
|
3
|
+
|
4
|
+
describe "with current token not being EOS" do
|
5
|
+
let(:position) do
|
6
|
+
OpenStruct.new(
|
7
|
+
line_number: 2,
|
8
|
+
line_offset: 3,
|
9
|
+
length: 3,
|
10
|
+
file_name: 'template.hbs'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:exception) do
|
15
|
+
current = double(:current, position: position, type: :type)
|
16
|
+
double(:exception, current: current)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "creates an exception with a meaningful message" do
|
20
|
+
message = ".. 012 `345` 6789 .. is not permitted in this context"
|
21
|
+
|
22
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
23
|
+
|
24
|
+
expect(lex_exception.message).to eq message
|
25
|
+
end
|
26
|
+
|
27
|
+
it "figures out a position from the exception" do
|
28
|
+
backtrace = "%s:%d:%d" % [position.file_name, position.line_number, position.line_offset]
|
29
|
+
|
30
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
31
|
+
|
32
|
+
expect(lex_exception.backtrace).to eq [backtrace]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with current token being EOS" do
|
37
|
+
let(:exception) do
|
38
|
+
current = double(:current, position: nil, type: :EOS)
|
39
|
+
double(:exception, current: current)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "creates an exception with a meaningful message" do
|
43
|
+
message = "A block helper hasn't been closed properly"
|
44
|
+
|
45
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
46
|
+
|
47
|
+
expect(lex_exception.message).to eq message
|
48
|
+
end
|
49
|
+
|
50
|
+
it "creates an exception whose position contains the right line_number" do
|
51
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
52
|
+
|
53
|
+
expect(lex_exception.position.line_number).to be 3
|
54
|
+
end
|
55
|
+
|
56
|
+
it "creates an exception whose position contains the right line_offset" do
|
57
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
58
|
+
|
59
|
+
expect(lex_exception.position.line_offset).to be_zero
|
60
|
+
end
|
61
|
+
|
62
|
+
it "creates an exception whose position contains the right length" do
|
63
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
64
|
+
|
65
|
+
expect(lex_exception.position.length).to be 'third_line'.length
|
66
|
+
end
|
67
|
+
|
68
|
+
it "creates an exception whose position contains a nil file_name" do
|
69
|
+
lex_exception = Curlybars::Error::Parse.new(source, exception)
|
70
|
+
|
71
|
+
expect(lex_exception.position.file_name).to be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe Curlybars::Error::Render do
|
2
|
+
let(:position) do
|
3
|
+
OpenStruct.new(
|
4
|
+
line_number: 2,
|
5
|
+
line_offset: 3,
|
6
|
+
stream_offset: 14,
|
7
|
+
length: 3,
|
8
|
+
file_name: 'template.hbs'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "prefixes the id with `render.`" do
|
13
|
+
id = 'id'
|
14
|
+
|
15
|
+
exception = Curlybars::Error::Render.new(id, 'message', position)
|
16
|
+
|
17
|
+
expect(exception.id).to eq 'render.%s' % id
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe Curlybars::Error::Validate do
|
2
|
+
let(:position) do
|
3
|
+
OpenStruct.new(
|
4
|
+
line_number: 2,
|
5
|
+
line_offset: 3,
|
6
|
+
stream_offset: 14,
|
7
|
+
length: 3,
|
8
|
+
file_name: 'template.hbs'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "prefixes the id with `validate.`" do
|
13
|
+
id = 'id'
|
14
|
+
|
15
|
+
exception = Curlybars::Error::Validate.new(id, 'message', position)
|
16
|
+
|
17
|
+
expect(exception.id).to eq 'validate.%s' % id
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
# rubocop:disable Layout/AlignArray
|
2
|
+
describe Curlybars::Lexer do
|
3
|
+
describe "{{!-- ... --}}" do
|
4
|
+
it "skips begin of block comment" do
|
5
|
+
expect(lex('{{!--')).to produce []
|
6
|
+
end
|
7
|
+
|
8
|
+
it "skips begin and end of block comment" do
|
9
|
+
expect(lex('{{!----}}')).to produce []
|
10
|
+
end
|
11
|
+
|
12
|
+
it "skips a comment block containing curlybar code" do
|
13
|
+
expect(lex('{{!--{{helper}}--}}')).to produce []
|
14
|
+
end
|
15
|
+
|
16
|
+
it "is resilient to whitespaces" do
|
17
|
+
expect(lex('{{!-- --}}')).to produce []
|
18
|
+
end
|
19
|
+
|
20
|
+
it "is resilient to newlines" do
|
21
|
+
expect(lex("{{!--\n--}}")).to produce []
|
22
|
+
end
|
23
|
+
|
24
|
+
it "is skipped when present in plain text" do
|
25
|
+
expect(lex('text {{!----}} text')).to produce [:TEXT, :TEXT]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "{{! ... }}" do
|
30
|
+
it "skips begin of block comment" do
|
31
|
+
expect(lex('{{!')).to produce []
|
32
|
+
end
|
33
|
+
|
34
|
+
it "skips begin and end of block comment" do
|
35
|
+
expect(lex('{{!}}')).to produce []
|
36
|
+
end
|
37
|
+
|
38
|
+
it "is resilient to whitespaces" do
|
39
|
+
expect(lex('{{! }}')).to produce []
|
40
|
+
end
|
41
|
+
it "is resilient to newlines" do
|
42
|
+
expect(lex("{{!\n}}")).to produce []
|
43
|
+
end
|
44
|
+
|
45
|
+
it "is lexed when present in plain text" do
|
46
|
+
expect(lex('text {{!}} text')).to produce [:TEXT, :TEXT]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "{{<integer>}}" do
|
51
|
+
it "is lexed as an integer" do
|
52
|
+
expect(lex("{{7}}")).to produce [:START, :LITERAL, :END]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns the expressed integer" do
|
56
|
+
literal_token = lex("{{7}}").detect { |token| token.type == :LITERAL }
|
57
|
+
expect(literal_token.value).to eq 7
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "{{<boolean>}}" do
|
62
|
+
it "{{true}} is lexed as boolean" do
|
63
|
+
expect(lex("{{true}}")).to produce [:START, :LITERAL, :END]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "{{false}} is lexed as boolean" do
|
67
|
+
expect(lex("{{false}}")).to produce [:START, :LITERAL, :END]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns the expressed boolean" do
|
71
|
+
literal_token = lex("{{true}}").detect { |token| token.type == :LITERAL }
|
72
|
+
expect(literal_token.value).to be_truthy
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "{{''}}" do
|
77
|
+
it "is lexed as a literal" do
|
78
|
+
expect(lex("{{''}}")).to produce [:START, :LITERAL, :END]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns the string between quotes" do
|
82
|
+
literal_token = lex("{{'string'}}").detect { |token| token.type == :LITERAL }
|
83
|
+
expect(literal_token.value).to eq '"string"'
|
84
|
+
end
|
85
|
+
|
86
|
+
it "is resilient to whitespaces" do
|
87
|
+
expect(lex("{{ '' }}")).to produce [:START, :LITERAL, :END]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "is lexed when present in plain text" do
|
91
|
+
expect(lex("text {{''}} text")).to produce [:TEXT, :START, :LITERAL, :END, :TEXT]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '{{""}}' do
|
96
|
+
it "is lexed as a literal" do
|
97
|
+
expect(lex('{{""}}')).to produce [:START, :LITERAL, :END]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns the string between quotes" do
|
101
|
+
literal_token = lex('{{"string"}}').detect { |token| token.type == :LITERAL }
|
102
|
+
expect(literal_token.value).to eq '"string"'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "is resilient to whitespaces" do
|
106
|
+
expect(lex('{{ "" }}')).to produce [:START, :LITERAL, :END]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "is lexed when present in plain text" do
|
110
|
+
expect(lex('text {{""}} text')).to produce [:TEXT, :START, :LITERAL, :END, :TEXT]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "{{@variable}}" do
|
115
|
+
it "is lexed as a varaible" do
|
116
|
+
expect(lex('{{@var}}')).to produce [:START, :VARIABLE, :END]
|
117
|
+
end
|
118
|
+
|
119
|
+
it "returns the identifier after `@`" do
|
120
|
+
variable_token = lex('{{@var}}').detect { |token| token.type == :VARIABLE }
|
121
|
+
expect(variable_token.value).to eq 'var'
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns the identifier after `@` also when using `../`" do
|
125
|
+
variable_token = lex('{{@../var}}').detect { |token| token.type == :VARIABLE }
|
126
|
+
expect(variable_token.value).to eq '../var'
|
127
|
+
end
|
128
|
+
|
129
|
+
it "is resilient to whitespaces" do
|
130
|
+
expect(lex('{{ @var }}')).to produce [:START, :VARIABLE, :END]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "is lexed when present in plain text" do
|
134
|
+
expect(lex('text {{@var}} text')).to produce [:TEXT, :START, :VARIABLE, :END, :TEXT]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "{{path context options}}" do
|
139
|
+
it "is lexed with context and options" do
|
140
|
+
expect(lex('{{path context key=value}}')).to produce [:START, :PATH, :PATH, :KEY, :PATH, :END]
|
141
|
+
end
|
142
|
+
|
143
|
+
it "is lexed without context" do
|
144
|
+
expect(lex('{{path key=value}}')).to produce [:START, :PATH, :KEY, :PATH, :END]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "is lexed without options" do
|
148
|
+
expect(lex('{{path context}}')).to produce [:START, :PATH, :PATH, :END]
|
149
|
+
end
|
150
|
+
|
151
|
+
it "is lexed without context and options" do
|
152
|
+
expect(lex('{{path}}')).to produce [:START, :PATH, :END]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "is resilient to whitespaces" do
|
156
|
+
expect(lex('{{ path }}')).to produce [:START, :PATH, :END]
|
157
|
+
end
|
158
|
+
|
159
|
+
it "is lexed when present in plain text" do
|
160
|
+
expect(lex('text {{ path }} text')).to produce [:TEXT, :START, :PATH, :END, :TEXT]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "{{#if path}}...{{/if}}" do
|
165
|
+
it "is lexed" do
|
166
|
+
expect(lex('{{#if path}} text {{/if}}')).to produce(
|
167
|
+
[:START, :HASH, :IF, :PATH, :END, :TEXT, :START, :SLASH, :IF, :END]
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "is resilient to whitespaces" do
|
172
|
+
expect(lex('{{ # if path }} text {{/ if }}')).to produce(
|
173
|
+
[:START, :HASH, :IF, :PATH, :END, :TEXT, :START, :SLASH, :IF, :END]
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "is lexed when present in plain text" do
|
178
|
+
expect(lex('text {{#if path}} text {{/if}} text')).to produce(
|
179
|
+
[:TEXT, :START, :HASH, :IF, :PATH, :END, :TEXT, :START, :SLASH, :IF, :END, :TEXT]
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "{{#if path}}...{{else}}...{{/if}}" do
|
185
|
+
it "is lexed" do
|
186
|
+
expect(lex('{{#if path}} text {{else}} text {{/if}}')).to produce(
|
187
|
+
[:START, :HASH, :IF, :PATH, :END,
|
188
|
+
:TEXT, :START, :ELSE, :END,
|
189
|
+
:TEXT, :START, :SLASH, :IF, :END]
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "is resilient to whitespaces" do
|
194
|
+
expect(lex('{{ # if path }} text {{ else }} text {{/ if }}')).to produce(
|
195
|
+
[:START, :HASH, :IF, :PATH, :END,
|
196
|
+
:TEXT, :START, :ELSE, :END,
|
197
|
+
:TEXT, :START, :SLASH, :IF, :END]
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "is lexed when present in plain text" do
|
202
|
+
expect(lex('text {{#if path}} text {{else}} text {{/if}} text')).to produce(
|
203
|
+
[:TEXT, :START, :HASH, :IF, :PATH, :END,
|
204
|
+
:TEXT, :START, :ELSE, :END,
|
205
|
+
:TEXT, :START, :SLASH, :IF, :END, :TEXT]
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "{{#unless path}}...{{/unless}}" do
|
211
|
+
it "is lexed" do
|
212
|
+
expect(lex('{{#unless path}} text {{/unless}}')).to produce(
|
213
|
+
[:START, :HASH, :UNLESS, :PATH, :END, :TEXT, :START, :SLASH, :UNLESS, :END]
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "is resilient to whitespaces" do
|
218
|
+
expect(lex('{{ # unless path }} text {{/ unless }}')).to produce(
|
219
|
+
[:START, :HASH, :UNLESS, :PATH, :END, :TEXT, :START, :SLASH, :UNLESS, :END]
|
220
|
+
)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "is lexed when present in plain text" do
|
224
|
+
expect(lex('text {{#unless path}} text {{/unless}} text')).to produce(
|
225
|
+
[:TEXT, :START, :HASH, :UNLESS, :PATH, :END, :TEXT, :START, :SLASH, :UNLESS, :END, :TEXT]
|
226
|
+
)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "{{#unless path}}...{{else}}...{{/unless}}" do
|
231
|
+
it "is lexed" do
|
232
|
+
expect(lex('{{#unless path}} text {{else}} text {{/unless}}')).to produce(
|
233
|
+
[:START, :HASH, :UNLESS, :PATH, :END,
|
234
|
+
:TEXT, :START, :ELSE, :END,
|
235
|
+
:TEXT, :START, :SLASH, :UNLESS, :END]
|
236
|
+
)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "is resilient to whitespaces" do
|
240
|
+
expect(lex('{{ # unless path }} text {{ else }} text {{/ unless }}')).to produce(
|
241
|
+
[:START, :HASH, :UNLESS, :PATH, :END,
|
242
|
+
:TEXT, :START, :ELSE, :END,
|
243
|
+
:TEXT, :START, :SLASH, :UNLESS, :END]
|
244
|
+
)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "is lexed when present in plain text" do
|
248
|
+
expect(lex('text {{#unless path}} text {{else}} text {{/unless}} text')).to produce(
|
249
|
+
[:TEXT, :START, :HASH, :UNLESS, :PATH, :END,
|
250
|
+
:TEXT, :START, :ELSE, :END,
|
251
|
+
:TEXT, :START, :SLASH, :UNLESS, :END, :TEXT]
|
252
|
+
)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "{{#each path}}...{{/each}}" do
|
257
|
+
it "is lexed" do
|
258
|
+
expect(lex('{{#each path}} text {{/each}}')).to produce(
|
259
|
+
[:START, :HASH, :EACH, :PATH, :END, :TEXT, :START, :SLASH, :EACH, :END]
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
263
|
+
it "is resilient to whitespaces" do
|
264
|
+
expect(lex('{{ # each path }} text {{/ each }}')).to produce(
|
265
|
+
[:START, :HASH, :EACH, :PATH, :END, :TEXT, :START, :SLASH, :EACH, :END]
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "is lexed when present in plain text" do
|
270
|
+
expect(lex('text {{#each path}} text {{/each}} text')).to produce(
|
271
|
+
[:TEXT, :START, :HASH, :EACH, :PATH, :END, :TEXT, :START, :SLASH, :EACH, :END, :TEXT]
|
272
|
+
)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "{{#each path}}...{{else}}...{{/each}}" do
|
277
|
+
it "is lexed" do
|
278
|
+
expect(lex('{{#each path}} text {{else}} text {{/each}}')).to produce(
|
279
|
+
[:START, :HASH, :EACH, :PATH, :END,
|
280
|
+
:TEXT, :START, :ELSE, :END,
|
281
|
+
:TEXT, :START, :SLASH, :EACH, :END]
|
282
|
+
)
|
283
|
+
end
|
284
|
+
|
285
|
+
it "is resilient to whitespaces" do
|
286
|
+
expect(lex('{{ # each path }} text {{ else }} text {{/ each }}')).to produce(
|
287
|
+
[:START, :HASH, :EACH, :PATH, :END,
|
288
|
+
:TEXT, :START, :ELSE, :END,
|
289
|
+
:TEXT, :START, :SLASH, :EACH, :END]
|
290
|
+
)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "is lexed when present in plain text" do
|
294
|
+
expect(lex('text {{#each path}} text {{else}} text {{/each}} text')).to produce(
|
295
|
+
[:TEXT, :START, :HASH, :EACH, :PATH, :END,
|
296
|
+
:TEXT, :START, :ELSE, :END,
|
297
|
+
:TEXT, :START, :SLASH, :EACH, :END, :TEXT]
|
298
|
+
)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "{{#with path}}...{{/with}}" do
|
303
|
+
it "is lexed" do
|
304
|
+
expect(lex('{{#with path}} text {{/with}}')).to produce(
|
305
|
+
[:START, :HASH, :WITH, :PATH, :END, :TEXT, :START, :SLASH, :WITH, :END]
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
309
|
+
it "is resilient to whitespaces" do
|
310
|
+
expect(lex('{{ # with path }} text {{/ with }}')).to produce(
|
311
|
+
[:START, :HASH, :WITH, :PATH, :END, :TEXT, :START, :SLASH, :WITH, :END]
|
312
|
+
)
|
313
|
+
end
|
314
|
+
|
315
|
+
it "is lexed when present in plain text" do
|
316
|
+
expect(lex('text {{#with path}} text {{/with}} text')).to produce(
|
317
|
+
[:TEXT, :START, :HASH, :WITH, :PATH, :END, :TEXT, :START, :SLASH, :WITH, :END, :TEXT]
|
318
|
+
)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "{{#path path options}}...{{/path}}" do
|
323
|
+
it "is lexed with context and options" do
|
324
|
+
expect(lex('{{#path context key=value}} text {{/path}}')).to produce(
|
325
|
+
[:START, :HASH, :PATH, :PATH, :KEY, :PATH, :END, :TEXT, :START, :SLASH, :PATH, :END]
|
326
|
+
)
|
327
|
+
end
|
328
|
+
|
329
|
+
it "is lexed without options" do
|
330
|
+
expect(lex('{{#path context}} text {{/path}}')).to produce(
|
331
|
+
[:START, :HASH, :PATH, :PATH, :END, :TEXT, :START, :SLASH, :PATH, :END]
|
332
|
+
)
|
333
|
+
end
|
334
|
+
|
335
|
+
it "is resilient to whitespaces" do
|
336
|
+
expect(lex('{{ # path context key = value}} text {{/ path }}')).to produce(
|
337
|
+
[:START, :HASH, :PATH, :PATH, :KEY, :PATH, :END, :TEXT, :START, :SLASH, :PATH, :END]
|
338
|
+
)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "is lexed when present in plain text" do
|
342
|
+
expect(lex('text {{#path context key=value}} text {{/path}} text')).to produce(
|
343
|
+
[:TEXT, :START, :HASH, :PATH, :PATH, :KEY, :PATH, :END, :TEXT, :START, :SLASH, :PATH, :END, :TEXT]
|
344
|
+
)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "{{>path}}" do
|
349
|
+
it "is lexed" do
|
350
|
+
expect(lex('{{>path}}')).to produce [:START, :GT, :PATH, :END]
|
351
|
+
end
|
352
|
+
|
353
|
+
it "is resilient to whitespaces" do
|
354
|
+
expect(lex('{{ > path }}')).to produce [:START, :GT, :PATH, :END]
|
355
|
+
end
|
356
|
+
|
357
|
+
it "is lexed when present in plain text" do
|
358
|
+
expect(lex('text {{>path}} text')).to produce [:TEXT, :START, :GT, :PATH, :END, :TEXT]
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "when a leading backslash is present" do
|
363
|
+
it "`{` is lexed as plain text" do
|
364
|
+
expect(lex('\{')).to produce [:TEXT]
|
365
|
+
end
|
366
|
+
|
367
|
+
it "returns the original text" do
|
368
|
+
text_token = lex('\{').detect { |token| token.type == :TEXT }
|
369
|
+
expect(text_token.value).to eq '{'
|
370
|
+
end
|
371
|
+
|
372
|
+
it "is lexed when present in plain text" do
|
373
|
+
expect(lex('text \{ text')).to produce [:TEXT, :TEXT, :TEXT]
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe "can lex paths with or without leading `../`s" do
|
378
|
+
it "`path` is lexed as a path" do
|
379
|
+
expect(lex('{{path}}')).to produce [:START, :PATH, :END]
|
380
|
+
end
|
381
|
+
|
382
|
+
it "`../path` is lexed as a path" do
|
383
|
+
expect(lex('{{../path}}')).to produce [:START, :PATH, :END]
|
384
|
+
end
|
385
|
+
|
386
|
+
it "`../../path` is lexed as a path" do
|
387
|
+
expect(lex('{{../../path}}')).to produce [:START, :PATH, :END]
|
388
|
+
end
|
389
|
+
|
390
|
+
it "`path/../` raises an error" do
|
391
|
+
expect do
|
392
|
+
lex('{{path/../}}')
|
393
|
+
end.to raise_error(RLTK::LexingError)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "can lex paths with dashes" do
|
398
|
+
it "`surrounded by other valid chars" do
|
399
|
+
expect(lex('{{a-path}}')).to produce [:START, :PATH, :END]
|
400
|
+
end
|
401
|
+
|
402
|
+
it "at the beginning" do
|
403
|
+
expect(lex('{{-path}}')).to produce [:START, :PATH, :END]
|
404
|
+
end
|
405
|
+
|
406
|
+
it "at the end" do
|
407
|
+
expect(lex('{{path-}}')).to produce [:START, :PATH, :END]
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "can lex paths with identifiers that are numebrs" do
|
412
|
+
it "`surrounded by other valid chars" do
|
413
|
+
expect(lex('{{path.123}}')).to produce [:START, :PATH, :END]
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe "outside a curlybar context" do
|
418
|
+
it "`--}}` is lexed as plain text" do
|
419
|
+
expect(lex('--}}')).to produce [:TEXT]
|
420
|
+
end
|
421
|
+
|
422
|
+
it "`}}` is lexed as plain text" do
|
423
|
+
expect(lex('}}')).to produce [:TEXT]
|
424
|
+
end
|
425
|
+
|
426
|
+
it "`#` is lexed as plain text" do
|
427
|
+
expect(lex('#')).to produce [:TEXT]
|
428
|
+
end
|
429
|
+
|
430
|
+
it "`/` is lexed as plain text" do
|
431
|
+
expect(lex('/')).to produce [:TEXT]
|
432
|
+
end
|
433
|
+
|
434
|
+
it "`>` is lexed as plain text" do
|
435
|
+
expect(lex('>')).to produce [:TEXT]
|
436
|
+
end
|
437
|
+
|
438
|
+
it "`if` is lexed as plain text" do
|
439
|
+
expect(lex('if')).to produce [:TEXT]
|
440
|
+
end
|
441
|
+
|
442
|
+
it "`unless` is lexed as plain text" do
|
443
|
+
expect(lex('unless')).to produce [:TEXT]
|
444
|
+
end
|
445
|
+
|
446
|
+
it "`each` is lexed as plain text" do
|
447
|
+
expect(lex('each')).to produce [:TEXT]
|
448
|
+
end
|
449
|
+
|
450
|
+
it "`with` is lexed as plain text" do
|
451
|
+
expect(lex('with')).to produce [:TEXT]
|
452
|
+
end
|
453
|
+
|
454
|
+
it "`else` is lexed as plain text" do
|
455
|
+
expect(lex('else')).to produce [:TEXT]
|
456
|
+
end
|
457
|
+
|
458
|
+
it "a path is lexed as plain text" do
|
459
|
+
expect(lex('this.is.a.path')).to produce [:TEXT]
|
460
|
+
end
|
461
|
+
|
462
|
+
it "an option is lexed as plain text" do
|
463
|
+
expect(lex('key=value')).to produce [:TEXT]
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|