flavour_saver 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +14 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +48 -5
- data/README.md +153 -271
- data/flavour_saver.gemspec +17 -19
- data/lib/flavour_saver/error.rb +5 -0
- data/lib/flavour_saver/helpers.rb +19 -18
- data/lib/flavour_saver/lexer.rb +123 -50
- data/lib/flavour_saver/nodes.rb +107 -19
- data/lib/flavour_saver/parser.rb +339 -161
- data/lib/flavour_saver/partial.rb +4 -6
- data/lib/flavour_saver/rails_partial.rb +10 -4
- data/lib/flavour_saver/runtime.rb +76 -81
- data/lib/flavour_saver/template.rb +3 -5
- data/lib/flavour_saver/version.rb +1 -1
- data/lib/flavour_saver.rb +17 -17
- data/spec/acceptance/backtrack_spec.rb +9 -9
- data/spec/acceptance/comment_spec.rb +7 -7
- data/spec/acceptance/custom_block_helper_spec.rb +12 -12
- data/spec/acceptance/custom_helper_spec.rb +8 -8
- data/spec/acceptance/ensure_no_rce_spec.rb +64 -53
- data/spec/acceptance/handlebars_qunit_spec.rb +295 -297
- data/spec/acceptance/if_else_spec.rb +6 -6
- data/spec/acceptance/multi_level_with_spec.rb +10 -11
- data/spec/acceptance/nesting_depth_spec.rb +34 -0
- data/spec/acceptance/one_character_identifier_spec.rb +8 -8
- data/spec/acceptance/raw_block_spec.rb +7 -7
- data/spec/acceptance/sections_spec.rb +17 -14
- data/spec/acceptance/segment_literals_spec.rb +5 -5
- data/spec/acceptance/simple_expression_spec.rb +9 -9
- data/spec/acceptance/subexpression_spec.rb +18 -17
- data/spec/acceptance/unless_spec.rb +19 -6
- data/spec/lib/flavour_saver/lexer_spec.rb +103 -89
- data/spec/lib/flavour_saver/nodes_spec.rb +85 -0
- data/spec/lib/flavour_saver/parser_spec.rb +155 -95
- data/spec/lib/flavour_saver/runtime_spec.rb +94 -96
- data/spec/lib/flavour_saver/template_spec.rb +1 -2
- metadata +19 -48
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
4
|
+
describe "Fixture: if_else.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
6
|
let(:context) { Struct.new(:value).new }
|
|
7
|
-
let(:template) { File.expand_path(
|
|
7
|
+
let(:template) { File.expand_path("../../fixtures/if_else.hbs", __FILE__) }
|
|
8
8
|
|
|
9
9
|
it "renders the if block when given a string" do
|
|
10
10
|
context.value = "Alan"
|
|
@@ -26,7 +26,7 @@ describe 'Fixture: if_else.hbs' do
|
|
|
26
26
|
expect(subject).to eq "The given value is falsy: false."
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
it
|
|
29
|
+
it "renders the else block when given nil" do
|
|
30
30
|
context.value = nil
|
|
31
31
|
expect(subject).to eq "The given value is falsy: ."
|
|
32
32
|
end
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
6
|
-
let(:template) { File.expand_path(
|
|
7
|
-
let(:context) { Struct.new(:person
|
|
4
|
+
describe "Fixture: multi_level_with.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
|
+
let(:template) { File.expand_path("../../fixtures/multi_level_with.hbs", __FILE__) }
|
|
7
|
+
let(:context) { Struct.new(:person, :company).new }
|
|
8
8
|
|
|
9
|
-
it
|
|
10
|
-
context.person = Struct.new(:name).new(
|
|
11
|
-
context.company = Struct.new(:name).new(
|
|
12
|
-
expect(subject).to eq
|
|
9
|
+
it "renders correctly when person has a name" do
|
|
10
|
+
context.person = Struct.new(:name).new("Alan")
|
|
11
|
+
context.company = Struct.new(:name).new("Rad, Inc.")
|
|
12
|
+
expect(subject).to eq "Alan - Rad, Inc."
|
|
13
13
|
end
|
|
14
|
-
|
|
15
14
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
|
+
|
|
4
|
+
# The two constructs that use the most stack per level.
|
|
5
|
+
describe "Rendering at the nesting depth limit inside a Fiber" do
|
|
6
|
+
subject do
|
|
7
|
+
# RSpec memoizes `let` behind a mutex that another Fiber can't unlock.
|
|
8
|
+
compiled = Tilt["handlebars"].new { template }
|
|
9
|
+
scope = context
|
|
10
|
+
Fiber.new { compiled.render(scope) }.resume
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:max) { FlavourSaver::Parser::MAX_DEPTH }
|
|
14
|
+
let(:context) do
|
|
15
|
+
Struct.new(:items, :name).new.tap do |node|
|
|
16
|
+
node.items = [node]
|
|
17
|
+
node.name = "leaf"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
before(:all) do
|
|
22
|
+
FlavourSaver.register_helper(:pass) { |value, *| value }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "nested {{#each}} blocks" do
|
|
26
|
+
let(:template) { ("{{#each items}}" * max) + "{{name}}" + ("{{/each}}" * max) }
|
|
27
|
+
specify { expect(subject).to eq "leaf" }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "nested hash subexpressions" do
|
|
31
|
+
let(:template) { "{{pass " + ("(pass name k=" * max) + "name" + (")" * max) + "}}" }
|
|
32
|
+
specify { expect(subject).to eq "leaf" }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
6
|
-
let(:template) { File.expand_path(
|
|
7
|
-
let(:context)
|
|
4
|
+
describe "Fixture: one_character_identifier.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
|
+
let(:template) { File.expand_path("../../fixtures/one_character_identifier.hbs", __FILE__) }
|
|
7
|
+
let(:context) { double(:context) }
|
|
8
8
|
|
|
9
|
-
it
|
|
10
|
-
expect(context).to receive(:a).and_return(
|
|
9
|
+
it "renders correctly" do
|
|
10
|
+
expect(context).to receive(:a).and_return("foo")
|
|
11
11
|
expect(subject).to eq "foo"
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
6
|
-
let(:template) { File.expand_path(
|
|
7
|
-
let(:context)
|
|
4
|
+
describe "Fixture: raw.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
|
+
let(:template) { File.expand_path("../../fixtures/raw.hbs", __FILE__) }
|
|
7
|
+
let(:context) { double(:context) }
|
|
8
8
|
|
|
9
|
-
it
|
|
9
|
+
it "renders correctly" do
|
|
10
10
|
expect(subject).to eq "{{=if brokensyntax}"
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
4
|
+
describe "Fixture: sections.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
6
|
let(:context) { Struct.new(:name, :names, :order).new }
|
|
7
|
-
let(:template) { File.expand_path(
|
|
7
|
+
let(:template) { File.expand_path("../../fixtures/sections.hbs", __FILE__) }
|
|
8
8
|
|
|
9
|
-
it
|
|
10
|
-
context.name =
|
|
9
|
+
it "renders correctly when given a name" do
|
|
10
|
+
context.name = "Alan"
|
|
11
11
|
expect(subject).to eq "Say hello to Alan."
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
it
|
|
15
|
-
context.names = [
|
|
14
|
+
it "renders correctly when given a list of names" do
|
|
15
|
+
context.names = ["Foo", "Bar"]
|
|
16
16
|
expect(subject).to eq "* Foo * Bar"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
it
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
it "renders correctly when given an order" do
|
|
20
|
+
order = Class.new do
|
|
21
|
+
def number
|
|
22
|
+
1234
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
context.order = order.new
|
|
26
|
+
expect(subject).to eq "Number: 1234"
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "flavour_saver"
|
|
2
2
|
|
|
3
3
|
describe FlavourSaver do
|
|
4
4
|
subject { FS.evaluate(template, context) }
|
|
@@ -10,17 +10,17 @@ describe FlavourSaver do
|
|
|
10
10
|
FS.reset_partials
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
describe
|
|
14
|
-
let(:template) {
|
|
13
|
+
describe "segment literal array access" do
|
|
14
|
+
let(:template) { "{{foos.[1].bar}}" }
|
|
15
15
|
|
|
16
16
|
it 'returns "two"' do
|
|
17
17
|
foos = []
|
|
18
18
|
foos << double(:foo)
|
|
19
19
|
foos << double(:foo)
|
|
20
|
-
expect(foos[1]).to receive(:bar).and_return(
|
|
20
|
+
expect(foos[1]).to receive(:bar).and_return("two")
|
|
21
21
|
|
|
22
22
|
allow(context).to receive(:foos).and_return(foos)
|
|
23
|
-
expect(subject).to eq
|
|
23
|
+
expect(subject).to eq "two"
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
6
|
-
let(:template) { File.expand_path(
|
|
7
|
-
let(:context)
|
|
4
|
+
describe "Fixture: simple_expression.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
|
+
let(:template) { File.expand_path("../../fixtures/simple_expression.hbs", __FILE__) }
|
|
7
|
+
let(:context) { double(:context) }
|
|
8
8
|
|
|
9
|
-
it
|
|
10
|
-
expect(context).to receive(:hello).and_return(
|
|
9
|
+
it "renders correctly" do
|
|
10
|
+
expect(context).to receive(:hello).and_return("hello world")
|
|
11
11
|
expect(subject).to eq "hello world"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
it
|
|
14
|
+
it "renders nothing if undefined" do
|
|
15
15
|
expect(subject).to eq ""
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -1,37 +1,38 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
4
|
+
describe "Subexpressions" do
|
|
5
|
+
subject { Tilt["handlebars"].new { template }.render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let(:context) { double(:context) }
|
|
7
|
+
let(:context) { double(:context) }
|
|
9
8
|
before(:all) do
|
|
10
|
-
FlavourSaver.register_helper(:sum) { |a,b| a + b}
|
|
9
|
+
FlavourSaver.register_helper(:sum) { |a, b| a + b }
|
|
11
10
|
end
|
|
12
11
|
context "simple subexpression" do
|
|
13
12
|
let(:template) { "{{sum 1 (sum 1 1)}}" }
|
|
14
|
-
specify{expect(subject).to eq "3"}
|
|
13
|
+
specify { expect(subject).to eq "3" }
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
context "nested subexpressions" do
|
|
18
17
|
let(:template) { "{{sum 1 (sum 1 (sum 1 1))}}" }
|
|
19
|
-
specify{expect(subject).to eq "4"}
|
|
18
|
+
specify { expect(subject).to eq "4" }
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
context "subexpression as argument" do
|
|
23
|
-
before {FlavourSaver.register_helper(:cents) { |a| a[:total] + 10}}
|
|
22
|
+
before { FlavourSaver.register_helper(:cents) { |a| a[:total] + 10 } }
|
|
24
23
|
let(:template) { "{{cents total=(sum 1 1)}}" }
|
|
25
|
-
specify{expect(subject).to eq "12"}
|
|
24
|
+
specify { expect(subject).to eq "12" }
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
context "subexpression in block" do
|
|
29
|
-
before {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
before {
|
|
29
|
+
FlavourSaver.register_helper(:repeat) do |a, &block|
|
|
30
|
+
s = ""
|
|
31
|
+
a.times { s += block.call.contents }
|
|
32
|
+
s
|
|
33
|
+
end
|
|
34
|
+
}
|
|
34
35
|
let(:template) { "{{#repeat (sum 1 2)}}*{{/repeat}}" }
|
|
35
|
-
specify{expect(subject).to eq "***"}
|
|
36
|
+
specify { expect(subject).to eq "***" }
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "tilt"
|
|
2
|
+
require "flavour_saver"
|
|
3
3
|
|
|
4
|
-
describe
|
|
5
|
-
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/,
|
|
4
|
+
describe "Fixture: unless.hbs" do
|
|
5
|
+
subject { Tilt.new(template).render(context).gsub(/[\s\r\n]+/, " ").strip }
|
|
6
6
|
let(:context) { Struct.new(:value).new }
|
|
7
|
-
let(:template) { File.expand_path(
|
|
7
|
+
let(:template) { File.expand_path("../../fixtures/unless.hbs", __FILE__) }
|
|
8
8
|
|
|
9
9
|
it "renders the unless block when given false" do
|
|
10
10
|
context.value = false
|
|
11
11
|
expect(subject).to eq "The given value is falsy: false."
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
it
|
|
14
|
+
it "renders the unless block when given nil" do
|
|
15
15
|
context.value = nil
|
|
16
16
|
expect(subject).to eq "The given value is falsy: ."
|
|
17
17
|
end
|
|
@@ -45,4 +45,17 @@ describe 'Fixture: unless.hbs' do
|
|
|
45
45
|
context.value = [1]
|
|
46
46
|
expect(subject).to eq "The given value is truthy: [1]."
|
|
47
47
|
end
|
|
48
|
+
describe "an inverted section with an else" do
|
|
49
|
+
subject { FlavourSaver.evaluate("{{^value}}falsy{{else}}truthy{{/value}}", context) }
|
|
50
|
+
|
|
51
|
+
it "renders the body when the value is falsy" do
|
|
52
|
+
context.value = false
|
|
53
|
+
expect(subject).to eq "falsy"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "renders the else section when the value is truthy" do
|
|
57
|
+
context.value = true
|
|
58
|
+
expect(subject).to eq "truthy"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
48
61
|
end
|