musterb 0.0.3 → 0.0.4
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.
- data/lib/musterb/chain.rb +7 -0
- data/lib/musterb/evaluator.rb +8 -30
- data/lib/musterb/extract_values.rb +25 -0
- data/lib/musterb/musterbifier.rb +16 -5
- data/lib/musterb/version.rb +1 -1
- data/lib/musterb.rb +2 -0
- data/musterb.gemspec +1 -0
- data/spec/musterb/evaluator_spec.rb +27 -36
- data/spec/musterb/musterbifier_spec.rb +10 -2
- data/spec/performance.rb +74 -0
- metadata +25 -1
data/lib/musterb/evaluator.rb
CHANGED
@@ -1,18 +1,11 @@
|
|
1
1
|
class Musterb::Evaluator
|
2
|
+
include ExtractValues
|
3
|
+
|
2
4
|
def initialize(context)
|
3
5
|
@context = context
|
4
|
-
end
|
6
|
+
end
|
5
7
|
|
6
|
-
def
|
7
|
-
return @context.value if symbol == "."
|
8
|
-
final_context = symbol.split(".").inject(@context) do |con, symbol|
|
9
|
-
new_context con[symbol], con
|
10
|
-
end
|
11
|
-
final_context.value
|
12
|
-
end
|
13
|
-
|
14
|
-
def block(symbol)
|
15
|
-
value = self[symbol]
|
8
|
+
def block_if(value)
|
16
9
|
return if is_falsy? value
|
17
10
|
|
18
11
|
case value
|
@@ -25,8 +18,8 @@ class Musterb::Evaluator
|
|
25
18
|
end
|
26
19
|
end
|
27
20
|
|
28
|
-
def block_unless(
|
29
|
-
yield if is_falsy?
|
21
|
+
def block_unless(value)
|
22
|
+
yield if is_falsy? value
|
30
23
|
end
|
31
24
|
|
32
25
|
private
|
@@ -40,26 +33,11 @@ class Musterb::Evaluator
|
|
40
33
|
else
|
41
34
|
!value
|
42
35
|
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def new_context(value, old_context = @context)
|
46
|
-
case value
|
47
|
-
when Hash
|
48
|
-
Musterb::HashExtractor.new(value, old_context)
|
49
|
-
when nil
|
50
|
-
Musterb::NullExtractor.new(old_context)
|
51
|
-
else
|
52
|
-
Musterb::ObjectExtractor.new(value, old_context)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def old_context
|
57
|
-
@context.parent
|
58
|
-
end
|
36
|
+
end
|
59
37
|
|
60
38
|
def switch_context(value)
|
61
39
|
@context = new_context(value)
|
62
40
|
yield value
|
63
|
-
@context =
|
41
|
+
@context = @context.parent
|
64
42
|
end
|
65
43
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ExtractValues
|
2
|
+
def value
|
3
|
+
@context.value
|
4
|
+
end
|
5
|
+
|
6
|
+
def [](symbol)
|
7
|
+
@context[symbol]
|
8
|
+
end
|
9
|
+
|
10
|
+
def chain(symbol)
|
11
|
+
Musterb::Chain.new self[symbol]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def new_context(value, old_context = @context)
|
16
|
+
case value
|
17
|
+
when Hash
|
18
|
+
Musterb::HashExtractor.new(value, old_context)
|
19
|
+
when nil
|
20
|
+
Musterb::NullExtractor.new(old_context)
|
21
|
+
else
|
22
|
+
Musterb::ObjectExtractor.new(value, old_context)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/musterb/musterbifier.rb
CHANGED
@@ -3,24 +3,35 @@ class Musterb::Musterbifier
|
|
3
3
|
@template = template
|
4
4
|
end
|
5
5
|
|
6
|
+
def fetch(tokens)
|
7
|
+
tokens = tokens.strip.split(".")
|
8
|
+
last_token = tokens.pop
|
9
|
+
fetch_command = tokens.inject("musterb") do |str, token|
|
10
|
+
"#{str}.chain('#{token}')"
|
11
|
+
end
|
12
|
+
"#{fetch_command}['#{last_token}']"
|
13
|
+
end
|
14
|
+
|
6
15
|
def to_erb
|
7
16
|
@template.gsub(/\{\{(\{?[^\}]*\}?)\}\}/) do |match|
|
8
17
|
match = $1
|
9
18
|
case match[0]
|
10
19
|
when '#'
|
11
|
-
"<% musterb.
|
20
|
+
"<% musterb.block_if #{fetch match[1..-1]} do %>"
|
12
21
|
when '^'
|
13
|
-
"<% musterb.block_unless
|
22
|
+
"<% musterb.block_unless #{fetch match[1..-1]} do %>"
|
14
23
|
when "/"
|
15
24
|
"<% end %>"
|
16
25
|
when '{'
|
17
|
-
"<%=
|
26
|
+
"<%= #{fetch match[1..-2]} %>"
|
18
27
|
when '&'
|
19
|
-
"<%=
|
28
|
+
"<%= #{fetch match[1..-1]} %>"
|
20
29
|
when '!'
|
21
30
|
""
|
31
|
+
when '.'
|
32
|
+
"<%== musterb.current %>"
|
22
33
|
else
|
23
|
-
"<%==
|
34
|
+
"<%== #{fetch match} %>"
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
data/lib/musterb/version.rb
CHANGED
data/lib/musterb.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "musterb/version"
|
2
|
+
require "musterb/extract_values"
|
2
3
|
require "musterb/musterbifier"
|
3
4
|
require "musterb/binding_extractor"
|
4
5
|
require "musterb/hash_extractor"
|
5
6
|
require "musterb/object_extractor"
|
6
7
|
require "musterb/null_extractor"
|
7
8
|
require "musterb/evaluator"
|
9
|
+
require "musterb/chain"
|
8
10
|
|
9
11
|
module Musterb
|
10
12
|
def self.to_erb(template)
|
data/musterb.gemspec
CHANGED
@@ -7,74 +7,65 @@ describe Musterb::Evaluator do
|
|
7
7
|
|
8
8
|
it "pulls out values from ." do
|
9
9
|
evaluator = Musterb::Evaluator.new Musterb::ObjectExtractor.new(2, nil)
|
10
|
-
evaluator
|
10
|
+
evaluator.value.should eq 2
|
11
11
|
end
|
12
12
|
|
13
13
|
it "can do nested values" do
|
14
14
|
evaluator = Musterb::Evaluator.new Musterb::ObjectExtractor.new(2, nil)
|
15
|
-
evaluator["
|
15
|
+
evaluator.chain("next")["to_s"].should eq "3"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can deeply nest things" do
|
19
|
+
evaluator = Musterb::Evaluator.new Musterb::ObjectExtractor.new(2, nil)
|
20
|
+
evaluator.chain("next").chain("next")["to_s"].should eq "4"
|
16
21
|
end
|
17
22
|
|
18
23
|
it "does not barf when pulling out a value on nil" do
|
19
24
|
evaluator = Musterb::Evaluator.new Musterb::HashExtractor.new({:foo => nil}, nil)
|
20
|
-
evaluator["
|
25
|
+
evaluator.chain("foo")["bar"].should eq nil
|
21
26
|
end
|
22
27
|
|
23
28
|
context "block" do
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
expect { |b| evaluator.
|
29
|
+
let(:evaluator) { Musterb::Evaluator.new Musterb::NullExtractor.new(nil) }
|
30
|
+
|
31
|
+
it "yields to the block if a value is set" do
|
32
|
+
expect { |b| evaluator.block_if("bar", &b) }.to yield_control
|
28
33
|
end
|
29
34
|
|
30
35
|
it "does not yield to the block if the value is unset" do
|
31
|
-
|
32
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
33
|
-
expect { |b| evaluator.block("foo", &b) }.not_to yield_control
|
36
|
+
expect { |b| evaluator.block_if(nil, &b) }.not_to yield_control
|
34
37
|
end
|
35
38
|
|
36
39
|
it "yields to the block for every element in the array" do
|
37
|
-
|
38
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
39
|
-
expect { |b| evaluator.block("foo", &b) }.to yield_successive_args(1,2,3)
|
40
|
+
expect { |b| evaluator.block_if([1,2,3], &b) }.to yield_successive_args(1,2,3)
|
40
41
|
end
|
41
42
|
|
42
43
|
it "does not yield to an empty array" do
|
43
|
-
|
44
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
45
|
-
expect { |b| evaluator.block("foo", &b) }.not_to yield_control
|
44
|
+
expect { |b| evaluator.block_if([], &b) }.not_to yield_control
|
46
45
|
end
|
47
46
|
|
48
47
|
it "yields to an empty hash" do
|
49
|
-
|
50
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
51
|
-
expect { |b| evaluator.block("foo", &b) }.to yield_control
|
48
|
+
expect { |b| evaluator.block_if({}, &b) }.to yield_control
|
52
49
|
end
|
53
50
|
end
|
54
51
|
|
55
52
|
context "block_unless" do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
expect { |b| evaluator.block_unless("
|
53
|
+
let(:evaluator) { Musterb::Evaluator.new Musterb::NullExtractor.new(nil) }
|
54
|
+
|
55
|
+
it "does not yield to the block if a value is set" do
|
56
|
+
expect { |b| evaluator.block_unless("bar", &b) }.not_to yield_control
|
60
57
|
end
|
61
58
|
|
62
59
|
it "does not yield to the block if the value is unset" do
|
63
|
-
|
64
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
65
|
-
expect { |b| evaluator.block_unless("foo", &b) }.to yield_control
|
60
|
+
expect { |b| evaluator.block_unless(nil, &b) }.to yield_control
|
66
61
|
end
|
67
62
|
|
68
63
|
it "yields to the block for every element in the array" do
|
69
|
-
|
70
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
71
|
-
expect { |b| evaluator.block_unless("foo", &b) }.not_to yield_control
|
64
|
+
expect { |b| evaluator.block_unless([1, 2, 3], &b) }.not_to yield_control
|
72
65
|
end
|
73
66
|
|
74
|
-
it "does not yield to an empty array" do
|
75
|
-
|
76
|
-
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
77
|
-
expect { |b| evaluator.block_unless("foo", &b) }.to yield_control
|
67
|
+
it "does not yield to an empty array" do
|
68
|
+
expect { |b| evaluator.block_unless([], &b) }.to yield_control
|
78
69
|
end
|
79
70
|
end
|
80
71
|
|
@@ -82,7 +73,7 @@ describe Musterb::Evaluator do
|
|
82
73
|
it "switches inside a hash" do
|
83
74
|
hash = { "foo" => "bar"}
|
84
75
|
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
85
|
-
evaluator.
|
76
|
+
evaluator.block_if hash do
|
86
77
|
evaluator['foo'].should eq 'bar'
|
87
78
|
end
|
88
79
|
end
|
@@ -90,7 +81,7 @@ describe Musterb::Evaluator do
|
|
90
81
|
it "resets the context later" do
|
91
82
|
hash = { "foo" => "bar"}
|
92
83
|
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
93
|
-
evaluator.
|
84
|
+
evaluator.block_if(hash) {}
|
94
85
|
evaluator["hash"].should eq hash
|
95
86
|
end
|
96
87
|
|
@@ -98,7 +89,7 @@ describe Musterb::Evaluator do
|
|
98
89
|
foo = "bar"
|
99
90
|
hash = { }
|
100
91
|
evaluator = Musterb::Evaluator.new Musterb::BindingExtractor.new(binding)
|
101
|
-
evaluator.
|
92
|
+
evaluator.block_if hash do
|
102
93
|
evaluator['foo'].should eq 'bar'
|
103
94
|
end
|
104
95
|
end
|
@@ -16,14 +16,22 @@ describe Musterb::Musterbifier do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "replaces blocks correctly" do
|
19
|
-
Musterb::Musterbifier.new("{{#cond}}foo{{/cond}}").to_erb.should eq "<% musterb.
|
19
|
+
Musterb::Musterbifier.new("{{#cond}}foo{{/cond}}").to_erb.should eq "<% musterb.block_if musterb['cond'] do %>foo<% end %>"
|
20
20
|
end
|
21
21
|
|
22
22
|
it "replaces carrot correctly" do
|
23
|
-
Musterb::Musterbifier.new("{{^cond}}foo{{/cond}}").to_erb.should eq "<% musterb.block_unless 'cond' do %>foo<% end %>"
|
23
|
+
Musterb::Musterbifier.new("{{^cond}}foo{{/cond}}").to_erb.should eq "<% musterb.block_unless musterb['cond'] do %>foo<% end %>"
|
24
24
|
end
|
25
25
|
|
26
26
|
it "replaces comments with nothing" do
|
27
27
|
Musterb::Musterbifier.new("{{! foo\n bar}}").to_erb.should eq ""
|
28
28
|
end
|
29
|
+
|
30
|
+
it "replaces . with current value" do
|
31
|
+
Musterb::Musterbifier.new("{{.}}").to_erb.should eq "<%== musterb.current %>"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "replaces foo.bar with a chain" do
|
35
|
+
Musterb::Musterbifier.new("{{foo.bar}}").to_erb.should eq "<%== musterb.chain('foo')['bar'] %>"
|
36
|
+
end
|
29
37
|
end
|
data/spec/performance.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
require 'musterb'
|
5
|
+
require 'mustache'
|
6
|
+
require 'erubis'
|
7
|
+
|
8
|
+
template = <<-EOF
|
9
|
+
{{#products}}
|
10
|
+
<div class="product-item" data-variant-id='{{id}}' data-favorite-id='{{favorite_id}}' data-quick-view-url="{{quick_view_url}}">
|
11
|
+
<h3>Product: {{name}}</h3>
|
12
|
+
|
13
|
+
<a href="{{url}}" class="quickView">
|
14
|
+
<img src="{{thumbnail}}"/>
|
15
|
+
</a>
|
16
|
+
<ul>
|
17
|
+
<li>SKU: {{sku}}</li>
|
18
|
+
<li>Fabrics: {{fabrics}}</li>
|
19
|
+
<li>Themes: {{themes}}</li>
|
20
|
+
<li>Price: {{price}}</li>
|
21
|
+
<li>Designer: {{designer}}
|
22
|
+
<li class="favorite">
|
23
|
+
<div class="not-favorited {{favorited}}">
|
24
|
+
<button class="add-to-favorites">Add to Favorite</button>
|
25
|
+
</div>
|
26
|
+
<div class="favorited {{favorited}}">
|
27
|
+
<button class="remove-from-favorites">Remove from Favorite</button>
|
28
|
+
</div>
|
29
|
+
</li>
|
30
|
+
</ul>
|
31
|
+
</div>
|
32
|
+
{{/products}}
|
33
|
+
EOF
|
34
|
+
|
35
|
+
def random_product
|
36
|
+
{
|
37
|
+
"id" => rand(10000),
|
38
|
+
"favorite_id" => rand(10000),
|
39
|
+
"quick_view_url" => "http://foo.bar",
|
40
|
+
"name" => "Product name",
|
41
|
+
"url" => "http://foo.url",
|
42
|
+
"thumbnail" => "http://foo.image",
|
43
|
+
"sku" => "sku",
|
44
|
+
"fabrics" => "fabrics",
|
45
|
+
"themes" => "themes",
|
46
|
+
"designer" => "designer",
|
47
|
+
"favorited" => "favorited"
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
products = 16.times.map { random_product }
|
52
|
+
|
53
|
+
mustache = lambda { Mustache.render(template, :products => products) }
|
54
|
+
|
55
|
+
erb = Musterb.to_erb(template)
|
56
|
+
compiled_erb = Erubis::Eruby.new(erb)
|
57
|
+
musterb = lambda { compiled_erb.result(:products => products) }
|
58
|
+
|
59
|
+
if mustache.call == musterb.call
|
60
|
+
puts "mustache and musterb are identical"
|
61
|
+
else
|
62
|
+
puts "mustache and musterb differ (this will usually be whitespace)"
|
63
|
+
puts "mustache:"
|
64
|
+
puts mustache.call
|
65
|
+
puts "--------------------------------------------------------------------------------"
|
66
|
+
puts "musterb:"
|
67
|
+
puts musterb.call
|
68
|
+
puts "--------------------------------------------------------------------------------"
|
69
|
+
end
|
70
|
+
|
71
|
+
Benchmark.bmbm do |x|
|
72
|
+
x.report("mustache") { 1000.times { mustache.call } }
|
73
|
+
x.report("musterb") { 1000.times { musterb.call } }
|
74
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: musterb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mustache
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
62
78
|
description: The ability to compile mustache templates to erubis erb templates so
|
63
79
|
that we can make use of the performance of compiling templates
|
64
80
|
email:
|
@@ -83,8 +99,12 @@ files:
|
|
83
99
|
bGliL211c3RlcmIucmI=
|
84
100
|
- !binary |-
|
85
101
|
bGliL211c3RlcmIvYmluZGluZ19leHRyYWN0b3IucmI=
|
102
|
+
- !binary |-
|
103
|
+
bGliL211c3RlcmIvY2hhaW4ucmI=
|
86
104
|
- !binary |-
|
87
105
|
bGliL211c3RlcmIvZXZhbHVhdG9yLnJi
|
106
|
+
- !binary |-
|
107
|
+
bGliL211c3RlcmIvZXh0cmFjdF92YWx1ZXMucmI=
|
88
108
|
- !binary |-
|
89
109
|
bGliL211c3RlcmIvaGFzaF9leHRyYWN0b3IucmI=
|
90
110
|
- !binary |-
|
@@ -111,6 +131,8 @@ files:
|
|
111
131
|
c3BlYy9tdXN0ZXJiL29iamVjdF9leHRyYWN0b3Jfc3BlYy5yYg==
|
112
132
|
- !binary |-
|
113
133
|
c3BlYy9tdXN0ZXJiX3NwZWMucmI=
|
134
|
+
- !binary |-
|
135
|
+
c3BlYy9wZXJmb3JtYW5jZS5yYg==
|
114
136
|
- !binary |-
|
115
137
|
c3BlYy9zcGVjX2hlbHBlci5yYg==
|
116
138
|
homepage: ''
|
@@ -150,5 +172,7 @@ test_files:
|
|
150
172
|
c3BlYy9tdXN0ZXJiL29iamVjdF9leHRyYWN0b3Jfc3BlYy5yYg==
|
151
173
|
- !binary |-
|
152
174
|
c3BlYy9tdXN0ZXJiX3NwZWMucmI=
|
175
|
+
- !binary |-
|
176
|
+
c3BlYy9wZXJmb3JtYW5jZS5yYg==
|
153
177
|
- !binary |-
|
154
178
|
c3BlYy9zcGVjX2hlbHBlci5yYg==
|