mustache 0.99.4 → 1.1.3
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/README.md +183 -183
- data/Rakefile +6 -14
- data/bin/mustache +28 -14
- data/lib/mustache/context.rb +98 -46
- data/lib/mustache/context_miss.rb +55 -0
- data/lib/mustache/enumerable.rb +3 -0
- data/lib/mustache/generator.rb +63 -42
- data/lib/mustache/parser.rb +173 -65
- data/lib/mustache/settings.rb +84 -24
- data/lib/mustache/template.rb +74 -4
- data/lib/mustache/utils.rb +31 -0
- data/lib/mustache/version.rb +1 -1
- data/lib/mustache.rb +116 -96
- data/man/mustache.1 +25 -40
- data/man/mustache.1.html +90 -81
- data/man/mustache.1.ron +6 -6
- data/man/mustache.5 +338 -298
- data/man/mustache.5.html +400 -115
- data/man/mustache.5.ron +292 -32
- data/test/autoloading_test.rb +7 -3
- data/test/fixtures/comments.rb +0 -1
- data/test/fixtures/complex_view.rb +0 -1
- data/test/fixtures/crazy_recursive.rb +0 -1
- data/test/fixtures/delimiters.rb +0 -1
- data/test/fixtures/dot_notation.rb +0 -1
- data/test/fixtures/double_section.rb +0 -1
- data/test/fixtures/inverted_section.rb +0 -1
- data/test/fixtures/lambda.rb +0 -1
- data/test/fixtures/liberal.mustache +1 -0
- data/test/fixtures/liberal.rb +25 -0
- data/test/fixtures/method_missing.rb +0 -1
- data/test/fixtures/namespaced.rb +0 -1
- data/test/fixtures/nested_objects.rb +0 -1
- data/test/fixtures/override/passenger.conf +6 -0
- data/test/fixtures/partial_with_module.rb +0 -1
- data/test/fixtures/passenger.rb +0 -1
- data/test/fixtures/recursive.rb +0 -1
- data/test/fixtures/simple.rb +0 -1
- data/test/fixtures/simply_complicated.mustache +25 -0
- data/test/fixtures/template_partial.rb +0 -1
- data/test/fixtures/unescaped.rb +0 -1
- data/test/helper.rb +5 -2
- data/test/mustache_test.rb +210 -25
- data/test/parser_test.rb +90 -7
- data/test/partial_test.rb +16 -4
- data/test/path_test.rb +49 -0
- data/test/spec_test.rb +5 -5
- data/test/template_test.rb +35 -3
- metadata +37 -59
- data/lib/mustache/sinatra.rb +0 -186
- data/lib/rack/bug/panels/mustache_panel/mustache_extension.rb +0 -27
- data/lib/rack/bug/panels/mustache_panel/view.mustache +0 -46
- data/lib/rack/bug/panels/mustache_panel.rb +0 -81
data/lib/mustache/context.rb
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
require 'mustache/context_miss'
|
|
2
|
+
|
|
1
3
|
class Mustache
|
|
2
|
-
# A ContextMiss is raised whenever a tag's target can not be found
|
|
3
|
-
# in the current context if `Mustache#raise_on_context_miss?` is
|
|
4
|
-
# set to true.
|
|
5
|
-
#
|
|
6
|
-
# For example, if your View class does not respond to `music` but
|
|
7
|
-
# your template contains a `{{music}}` tag this exception will be raised.
|
|
8
|
-
#
|
|
9
|
-
# By default it is not raised. See Mustache.raise_on_context_miss.
|
|
10
|
-
class ContextMiss < RuntimeError; end
|
|
11
4
|
|
|
12
5
|
# A Context represents the context which a Mustache template is
|
|
13
6
|
# executed within. All Mustache tags reference keys in the Context.
|
|
7
|
+
#
|
|
14
8
|
class Context
|
|
15
|
-
|
|
9
|
+
|
|
10
|
+
# Initializes a Mustache::Context.
|
|
11
|
+
#
|
|
12
|
+
# @param [Mustache] mustache A Mustache instance.
|
|
13
|
+
#
|
|
16
14
|
def initialize(mustache)
|
|
17
15
|
@stack = [mustache]
|
|
16
|
+
@partial_template_cache = {}
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
# A {{>partial}} tag translates into a call to the context's
|
|
@@ -23,6 +22,7 @@ class Mustache
|
|
|
23
22
|
# If the Mustache view handling the rendering (e.g. the view
|
|
24
23
|
# representing your profile page or some other template) responds
|
|
25
24
|
# to `partial`, we call it and render the result.
|
|
25
|
+
#
|
|
26
26
|
def partial(name, indentation = '')
|
|
27
27
|
# Look for the first Mustache in the stack.
|
|
28
28
|
mustache = mustache_in_stack
|
|
@@ -30,38 +30,55 @@ class Mustache
|
|
|
30
30
|
# Indent the partial template by the given indentation.
|
|
31
31
|
part = mustache.partial(name).to_s.gsub(/^/, indentation)
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
|
|
33
|
+
# Get a template object for the partial and render the result.
|
|
34
|
+
template_for_partial(part).render(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def template_for_partial(partial)
|
|
38
|
+
@partial_template_cache[partial] ||= Template.new(partial)
|
|
35
39
|
end
|
|
36
40
|
|
|
37
|
-
# Find the first Mustache in the stack.
|
|
38
|
-
#
|
|
41
|
+
# Find the first Mustache in the stack.
|
|
42
|
+
#
|
|
43
|
+
# If we're being rendered inside a Mustache object as a context,
|
|
44
|
+
# we'll use that one.
|
|
45
|
+
#
|
|
46
|
+
# @return [Mustache] First Mustache in the stack.
|
|
47
|
+
#
|
|
39
48
|
def mustache_in_stack
|
|
40
|
-
@stack.
|
|
49
|
+
@mustache_in_stack ||= @stack.find { |frame| frame.is_a?(Mustache) }
|
|
41
50
|
end
|
|
42
51
|
|
|
43
52
|
# Allows customization of how Mustache escapes things.
|
|
44
53
|
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
# @param [Object] value Value to escape.
|
|
55
|
+
#
|
|
56
|
+
# @return [String] Escaped string.
|
|
57
|
+
#
|
|
58
|
+
def escape(value)
|
|
59
|
+
mustache_in_stack.escape(value)
|
|
48
60
|
end
|
|
49
61
|
|
|
50
62
|
# Adds a new object to the context's internal stack.
|
|
51
63
|
#
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
# @param [Object] new_obj Object to be added to the internal stack.
|
|
65
|
+
#
|
|
66
|
+
# @return [Context] Returns the Context.
|
|
67
|
+
#
|
|
68
|
+
def push(new_obj)
|
|
69
|
+
@stack.unshift(new_obj)
|
|
70
|
+
@mustache_in_stack = nil
|
|
55
71
|
self
|
|
56
72
|
end
|
|
57
|
-
alias_method :update, :push
|
|
58
73
|
|
|
59
74
|
# Removes the most recently added object from the context's
|
|
60
75
|
# internal stack.
|
|
61
76
|
#
|
|
62
|
-
# Returns the Context.
|
|
77
|
+
# @return [Context] Returns the Context.
|
|
78
|
+
#
|
|
63
79
|
def pop
|
|
64
80
|
@stack.shift
|
|
81
|
+
@mustache_in_stack = nil
|
|
65
82
|
self
|
|
66
83
|
end
|
|
67
84
|
|
|
@@ -80,7 +97,7 @@ class Mustache
|
|
|
80
97
|
# Do we know about a particular key? In other words, will calling
|
|
81
98
|
# `context[key]` give us a result that was set. Basically.
|
|
82
99
|
def has_key?(key)
|
|
83
|
-
|
|
100
|
+
fetch(key, false)
|
|
84
101
|
rescue ContextMiss
|
|
85
102
|
false
|
|
86
103
|
end
|
|
@@ -97,9 +114,7 @@ class Mustache
|
|
|
97
114
|
next if frame == self
|
|
98
115
|
|
|
99
116
|
value = find(frame, name, :__missing)
|
|
100
|
-
|
|
101
|
-
return value
|
|
102
|
-
end
|
|
117
|
+
return value if :__missing != value
|
|
103
118
|
end
|
|
104
119
|
|
|
105
120
|
if default == :__raise || mustache_in_stack.raise_on_context_miss?
|
|
@@ -114,28 +129,65 @@ class Mustache
|
|
|
114
129
|
# If it's an object that responds to the key as a method call,
|
|
115
130
|
# invokes that method. You get the idea.
|
|
116
131
|
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# default
|
|
120
|
-
#
|
|
132
|
+
# @param [Object] obj The object to perform the lookup on.
|
|
133
|
+
# @param [String,Symbol] key The key whose value you want
|
|
134
|
+
# @param [Object] default An optional default value, to return if the key is not found.
|
|
135
|
+
#
|
|
136
|
+
# @return [Object] The value of key in object if it is found, and default otherwise.
|
|
121
137
|
#
|
|
122
|
-
# Returns the value of key in obj if it is found and default otherwise.
|
|
123
138
|
def find(obj, key, default = nil)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
|
|
140
|
+
if mustache_in_stack.context_access_security_level == 5 and !obj.instance_of?(Hash)
|
|
141
|
+
# on level 5, object must be exactly Hash
|
|
142
|
+
return context_level_violation("Detected key access attempt for a non-Hash object using '#{key}'", default)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return find_in_hash(obj.to_hash, key, default) if obj.respond_to?(:to_hash)
|
|
146
|
+
|
|
147
|
+
if mustache_in_stack.context_access_security_level >= 4
|
|
148
|
+
# on levels 4 and 5, method access is not allowed
|
|
149
|
+
return context_level_violation("Detected method access attempt using '#{key}'", default)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
unless obj.respond_to?(key)
|
|
153
|
+
# no match for the key, but it may include a hyphen, so try again replacing hyphens with underscores.
|
|
154
|
+
key = key.to_s.tr('-', '_')
|
|
155
|
+
return default unless obj.respond_to?(key)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if mustache_in_stack.context_access_security_level >= 3 && !obj.class.instance_methods(false).include?(key.to_sym)
|
|
159
|
+
# on level 3, inherited methods are blocked
|
|
160
|
+
return context_level_violation("Detected inherited method '#{key}'", default)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if mustache_in_stack.context_access_security_level >= 2 && MethodBlacklist.include?(key.to_s)
|
|
164
|
+
# on levels 2 and 3, reflection-based methods are blacklisted
|
|
165
|
+
return context_level_violation("Detected blacklisted reflection-related key '#{key}'", default)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
meth = obj.method(key) rescue proc { obj.send(key) }
|
|
169
|
+
meth.arity == 1 ? meth.to_proc : meth.call
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def current
|
|
173
|
+
@stack.first
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
# Fetches a hash key if it exists, or returns the given default.
|
|
180
|
+
def find_in_hash(obj, key, default)
|
|
181
|
+
return obj[key] if obj.has_key?(key)
|
|
182
|
+
return obj[key.to_s] if obj.has_key?(key.to_s)
|
|
183
|
+
return obj[key] if obj.respond_to?(:default_proc) && obj.default_proc && obj[key]
|
|
184
|
+
|
|
185
|
+
# If default is :__missing then we are from #fetch which is hunting through the stack
|
|
186
|
+
# If default is nil then we are reducing dot notation
|
|
187
|
+
if :__missing != default && mustache_in_stack.raise_on_context_miss?
|
|
188
|
+
raise ContextMiss.new("Can't find #{key} in #{obj}")
|
|
137
189
|
else
|
|
138
|
-
default
|
|
190
|
+
obj.fetch(key, default)
|
|
139
191
|
end
|
|
140
192
|
end
|
|
141
193
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Mustache
|
|
2
|
+
|
|
3
|
+
# A ContextMiss is raised whenever a tag's target can not be found
|
|
4
|
+
# in the current context if `Mustache#raise_on_context_miss?` is
|
|
5
|
+
# set to true.
|
|
6
|
+
#
|
|
7
|
+
# For example, if your View class does not respond to `music` but
|
|
8
|
+
# your template contains a `{{music}}` tag this exception will be raised.
|
|
9
|
+
#
|
|
10
|
+
# By default it is not raised. See Mustache.raise_on_context_miss.
|
|
11
|
+
#
|
|
12
|
+
class ContextMiss < RuntimeError; end
|
|
13
|
+
|
|
14
|
+
# A ContextLevelViolation is raised whenever accessing a key in the context is
|
|
15
|
+
# not allowed by the selected context access security level
|
|
16
|
+
#
|
|
17
|
+
# For example, if your context access security level is set to 3 but
|
|
18
|
+
# your template tries to access an inherited method this exception will be raised.
|
|
19
|
+
#
|
|
20
|
+
# By default it is only raised for blacklisted keys in objects.
|
|
21
|
+
# See Mustache.context_access_security_level.
|
|
22
|
+
#
|
|
23
|
+
class ContextLevelViolation < ContextMiss; end
|
|
24
|
+
|
|
25
|
+
class Context
|
|
26
|
+
# Raises ContextLevelViolation or returns default depending on raise_on_context_miss
|
|
27
|
+
#
|
|
28
|
+
# @param [String] message The error message if raise_on_context_miss is set
|
|
29
|
+
# @param [Object] default An optional default value, to return if the key is not found.
|
|
30
|
+
#
|
|
31
|
+
# @return [Object] The default value if errors are not raised
|
|
32
|
+
#
|
|
33
|
+
def context_level_violation(message, default = nil)
|
|
34
|
+
return default unless mustache_in_stack.raise_on_context_miss?
|
|
35
|
+
raise ContextLevelViolation.new(message)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
MethodBlacklist = ["allocate", "attached_object", "superclass", "subclasses", "new", "autoload?", "autoload",
|
|
41
|
+
"included_modules", "include?", "set_temporary_name", "ancestors", "attr", "attr_reader", "attr_writer",
|
|
42
|
+
"attr_accessor", "public_instance_method", "instance_methods", "public_instance_methods",
|
|
43
|
+
"protected_instance_methods", "private_instance_methods", "undefined_instance_methods", "freeze", "const_get",
|
|
44
|
+
"constants", "const_missing", "const_defined?", "const_set", "const_source_location", "remove_class_variable",
|
|
45
|
+
"class_variable_get", "class_variables", "private_constant", "class_variable_set", "class_variable_defined?",
|
|
46
|
+
"public_constant", "include", "deprecate_constant", "singleton_class?", "prepend", "refinements", "define_method",
|
|
47
|
+
"module_exec", "class_exec", "module_eval", "class_eval", "remove_method", "undef_method", "alias_method",
|
|
48
|
+
"method_defined?", "public_method_defined?", "private_method_defined?", "protected_method_defined?",
|
|
49
|
+
"public_class_method", "private_class_method", "instance_method", "singleton_class", "dup", "itself", "methods",
|
|
50
|
+
"singleton_methods", "protected_methods", "private_methods", "public_methods", "instance_variables",
|
|
51
|
+
"instance_variable_get", "instance_variable_set", "instance_variable_defined?", "remove_instance_variable",
|
|
52
|
+
"instance_of?", "kind_of?", "is_a?", "display", "public_send", "extend", "clone", "class", "frozen?", "tap",
|
|
53
|
+
"then", "yield_self", "respond_to?", "method", "public_method", "singleton_method", "define_singleton_method",
|
|
54
|
+
"hash", "object_id", "send", "enum_for", "__send__", "instance_eval", "instance_exec", "__id__"]
|
|
55
|
+
end
|
data/lib/mustache/generator.rb
CHANGED
|
@@ -27,9 +27,10 @@ class Mustache
|
|
|
27
27
|
# $ mustache --compile test.mustache
|
|
28
28
|
# "Hi #{CGI.escapeHTML(ctx[:thing].to_s)}!\n"
|
|
29
29
|
class Generator
|
|
30
|
-
# Options
|
|
30
|
+
# Options can be used to manipulate the resulting ruby code string behavior.
|
|
31
31
|
def initialize(options = {})
|
|
32
32
|
@options = options
|
|
33
|
+
@option_static_lambdas = options[:static_lambdas] == true
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
# Given an array of tokens, returns an interpolatable Ruby string.
|
|
@@ -37,6 +38,10 @@ class Mustache
|
|
|
37
38
|
"\"#{compile!(exp)}\""
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
|
|
40
45
|
# Given an array of tokens, converts them into Ruby code. In
|
|
41
46
|
# particular there are three types of expressions we are concerned
|
|
42
47
|
# with:
|
|
@@ -64,21 +69,26 @@ class Mustache
|
|
|
64
69
|
#
|
|
65
70
|
# [:multi,
|
|
66
71
|
# [:static, "Hello "],
|
|
67
|
-
# [:mustache, :etag,
|
|
72
|
+
# [:mustache, :etag,
|
|
73
|
+
# [:mustache, :fetch, ["name"]]],
|
|
68
74
|
# [:static, "\nYou have just won $"],
|
|
69
|
-
#
|
|
70
|
-
# [:
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
75
|
+
# [:mustache, :etag,
|
|
76
|
+
# [:mustache, :fetch, ["value"]]],
|
|
77
|
+
# [:static, "!\n"],
|
|
78
|
+
# [:mustache,
|
|
79
|
+
# :section,
|
|
80
|
+
# [:mustache, :fetch, ["in_ca"]],
|
|
81
|
+
# [:multi,
|
|
82
|
+
# [:static, "Well, $"],
|
|
83
|
+
# [:mustache, :etag,
|
|
84
|
+
# [:mustache, :fetch, ["taxed_value"]]],
|
|
85
|
+
# [:static, ", after taxes.\n"]],
|
|
86
|
+
# "Well, ${{taxed_value}}, after taxes.\n",
|
|
87
|
+
# ["{{", "}}"]]]
|
|
78
88
|
def compile!(exp)
|
|
79
89
|
case exp.first
|
|
80
90
|
when :multi
|
|
81
|
-
exp[1..-1].
|
|
91
|
+
exp[1..-1].reduce("".dup) { |sum, e| sum << compile!(e) }
|
|
82
92
|
when :static
|
|
83
93
|
str(exp[1])
|
|
84
94
|
when :mustache
|
|
@@ -90,38 +100,49 @@ class Mustache
|
|
|
90
100
|
|
|
91
101
|
# Callback fired when the compiler finds a section token. We're
|
|
92
102
|
# passed the section name and the array of tokens.
|
|
93
|
-
def on_section(name, content, raw, delims)
|
|
103
|
+
def on_section(name, offset, content, raw, delims)
|
|
94
104
|
# Convert the tokenized content of this section into a Ruby
|
|
95
105
|
# string we can use.
|
|
96
106
|
code = compile(content)
|
|
97
107
|
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
# Lambda handling - default handling is to dynamically interpret
|
|
109
|
+
# the returned lambda result as mustache source
|
|
110
|
+
proc_handling = if @option_static_lambdas
|
|
111
|
+
<<-compiled
|
|
112
|
+
v.call(lambda {|v| #{code}}.call(v)).to_s
|
|
113
|
+
compiled
|
|
114
|
+
else
|
|
115
|
+
<<-compiled
|
|
105
116
|
t = Mustache::Template.new(v.call(#{raw.inspect}).to_s)
|
|
106
117
|
def t.tokens(src=@source)
|
|
107
|
-
p = Parser.new
|
|
118
|
+
p = Mustache::Parser.new
|
|
108
119
|
p.otag, p.ctag = #{delims.inspect}
|
|
109
120
|
p.compile(src)
|
|
110
121
|
end
|
|
111
122
|
t.render(ctx.dup)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
v = [v] unless v.is_a?(Array) || defined?(Enumerator) && v.is_a?(Enumerator)
|
|
123
|
+
compiled
|
|
124
|
+
end
|
|
115
125
|
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
# Compile the Ruby for this section now that we know what's
|
|
127
|
+
# inside the section.
|
|
128
|
+
ev(<<-compiled)
|
|
129
|
+
case v = #{compile!(name)}
|
|
130
|
+
when NilClass, FalseClass
|
|
131
|
+
when TrueClass
|
|
132
|
+
#{code}
|
|
133
|
+
when Proc
|
|
134
|
+
#{proc_handling}
|
|
135
|
+
when Array, Enumerator, Mustache::Enumerable
|
|
136
|
+
v.map { |_| ctx.push(_); r = #{code}; ctx.pop; r }.join
|
|
137
|
+
else
|
|
138
|
+
ctx.push(v); r = #{code}; ctx.pop; r
|
|
118
139
|
end
|
|
119
140
|
compiled
|
|
120
141
|
end
|
|
121
142
|
|
|
122
143
|
# Fired when we find an inverted section. Just like `on_section`,
|
|
123
144
|
# we're passed the inverted section name and the array of tokens.
|
|
124
|
-
def on_inverted_section(name, content, raw,
|
|
145
|
+
def on_inverted_section(name, offset, content, raw, delims)
|
|
125
146
|
# Convert the tokenized content of this section into a Ruby
|
|
126
147
|
# string we can use.
|
|
127
148
|
code = compile(content)
|
|
@@ -130,7 +151,7 @@ class Mustache
|
|
|
130
151
|
# what's inside.
|
|
131
152
|
ev(<<-compiled)
|
|
132
153
|
v = #{compile!(name)}
|
|
133
|
-
if v.nil? || v == false || v.respond_to?(:empty?) && v.empty?
|
|
154
|
+
if v.nil? || v == false || (v.class != String && v.respond_to?(:empty?) && v.empty?)
|
|
134
155
|
#{code}
|
|
135
156
|
end
|
|
136
157
|
compiled
|
|
@@ -139,45 +160,45 @@ class Mustache
|
|
|
139
160
|
# Fired when the compiler finds a partial. We want to return code
|
|
140
161
|
# which calls a partial at runtime instead of expanding and
|
|
141
162
|
# including the partial's body to allow for recursive partials.
|
|
142
|
-
def on_partial(name, indentation)
|
|
163
|
+
def on_partial(name, offset, indentation)
|
|
143
164
|
ev("ctx.partial(#{name.to_sym.inspect}, #{indentation.inspect})")
|
|
144
165
|
end
|
|
145
166
|
|
|
146
167
|
# An unescaped tag.
|
|
147
|
-
def on_utag(name)
|
|
168
|
+
def on_utag(name, offset)
|
|
148
169
|
ev(<<-compiled)
|
|
149
170
|
v = #{compile!(name)}
|
|
150
171
|
if v.is_a?(Proc)
|
|
151
|
-
v = Mustache::Template.new(v.call.to_s).render(ctx.dup)
|
|
172
|
+
v = #{@option_static_lambdas ? 'v.call' : 'Mustache::Template.new(v.call.to_s).render(ctx.dup)'}
|
|
152
173
|
end
|
|
153
174
|
v.to_s
|
|
154
175
|
compiled
|
|
155
176
|
end
|
|
156
177
|
|
|
157
178
|
# An escaped tag.
|
|
158
|
-
def on_etag(name)
|
|
179
|
+
def on_etag(name, offset)
|
|
159
180
|
ev(<<-compiled)
|
|
160
181
|
v = #{compile!(name)}
|
|
161
182
|
if v.is_a?(Proc)
|
|
162
|
-
v = Mustache::Template.new(v.call.to_s).render(ctx.dup)
|
|
183
|
+
v = #{@option_static_lambdas ? 'v.call' : 'Mustache::Template.new(v.call.to_s).render(ctx.dup)'}
|
|
163
184
|
end
|
|
164
|
-
ctx.
|
|
185
|
+
ctx.escape(v)
|
|
165
186
|
compiled
|
|
166
187
|
end
|
|
167
188
|
|
|
168
189
|
def on_fetch(names)
|
|
190
|
+
return "ctx.current" if names.empty?
|
|
191
|
+
|
|
169
192
|
names = names.map { |n| n.to_sym }
|
|
170
193
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
initial, *rest = names
|
|
195
|
+
if rest.any?
|
|
196
|
+
<<-compiled
|
|
197
|
+
#{rest.inspect}.reduce(ctx[#{initial.inspect}]) { |value, key| value && ctx.find(value, key) }
|
|
198
|
+
compiled
|
|
175
199
|
else
|
|
176
|
-
initial, *rest = names
|
|
177
200
|
<<-compiled
|
|
178
|
-
|
|
179
|
-
value && ctx.find(value, key)
|
|
180
|
-
}
|
|
201
|
+
ctx[#{initial.inspect}]
|
|
181
202
|
compiled
|
|
182
203
|
end
|
|
183
204
|
end
|