mustache 1.0.3 → 1.0.5
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 +5 -13
- data/README.md +108 -89
- data/lib/mustache.rb +13 -4
- data/lib/mustache/context.rb +1 -0
- data/lib/mustache/generator.rb +36 -20
- data/lib/mustache/parser.rb +36 -17
- data/lib/mustache/template.rb +5 -4
- data/lib/mustache/version.rb +1 -1
- data/man/mustache.1 +3 -3
- data/man/mustache.1.html +3 -3
- data/man/mustache.1.ron +2 -2
- data/man/mustache.5 +1 -1
- data/man/mustache.5.html +1 -1
- data/test/helper.rb +5 -3
- data/test/mustache_test.rb +47 -2
- data/test/partial_test.rb +13 -0
- metadata +21 -30
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MGYwNTg4YTNlMGIzMDFlM2RlZWRlYjgxOWVkYzVmMjBkYTY0NjJlMg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bf94eeecd19f735037d7bbb26dec6b31fd744220
|
4
|
+
data.tar.gz: 1950b718fced739f53562898d3577a10ca88cdbf
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ODk0MDY3NTliYjM2NGI1OGUzMzZiNDkzNzA4NjEzNWJhMGRhZGFiZmUwYjJj
|
11
|
-
ODYzNjY3MWU0MDg2NjUzMzdkYTcwZGZhOTk4NWQ1MWZhYzBlNGQ=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NDA2ZDRiNjRlYTg3MjRkZjhmNWFjZDAyMjY3YzU1YzM0NTU4MWNlMzkzMmI0
|
14
|
-
MjAxMjAxOWYyNDdmYWE5MzEwODYzYzExZjBmM2VkN2E3YzdmODYxNGI2NTc3
|
15
|
-
N2U3ZGExOTcxMmE3MDBmMTdmMWI1YmUzNmE4YTFlMTdiMjQwNmM=
|
6
|
+
metadata.gz: 2aa2853641590267b49b7264f8592fed44f3fa430ea10e60ae3ce8b58a5fadea3b532f256b70229a0fbd62835176ead87b8dff0d66ac73750af603c9739e6587
|
7
|
+
data.tar.gz: 31a31d089046ab7d11b6541a02b588d92c26a6b5879185203ddc4ffedd64a41cc6b0651b818066333ee9af4000754a8f1d3885b5647895390ebe3e62d65532ae
|
data/README.md
CHANGED
@@ -44,10 +44,12 @@ in my HTML, or putting JavaScript in my HTML.
|
|
44
44
|
Install the gem locally with:
|
45
45
|
|
46
46
|
$ gem install mustache
|
47
|
-
|
48
|
-
Or add it to your `gemspec`:
|
49
47
|
|
50
|
-
|
48
|
+
Or add it to your `Gemfile`:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
gem "mustache", "~> 1.0"
|
52
|
+
```
|
51
53
|
|
52
54
|
|
53
55
|
## Usage
|
@@ -56,28 +58,30 @@ Quick example:
|
|
56
58
|
|
57
59
|
>> require 'mustache'
|
58
60
|
=> true
|
59
|
-
>> Mustache.render("Hello {{planet}}", :
|
61
|
+
>> Mustache.render("Hello {{planet}}", planet: "World!")
|
60
62
|
=> "Hello World!"
|
61
63
|
|
62
64
|
We've got an `examples` folder but here's the canonical one:
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
```ruby
|
67
|
+
class Simple < Mustache
|
68
|
+
def name
|
69
|
+
"Chris"
|
70
|
+
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
def value
|
73
|
+
10_000
|
74
|
+
end
|
72
75
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
+
def taxed_value
|
77
|
+
value * 0.6
|
78
|
+
end
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
def in_ca
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
81
85
|
|
82
86
|
We simply create a normal Ruby class and define methods. Some methods
|
83
87
|
reference others, some return values, some return only booleans.
|
@@ -92,9 +96,9 @@ Now let's write the template:
|
|
92
96
|
|
93
97
|
This template references our view methods. To bring it all together,
|
94
98
|
here's the code to render actual HTML;
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
```ruby
|
100
|
+
Simple.render
|
101
|
+
```
|
98
102
|
Which returns the following:
|
99
103
|
|
100
104
|
Hello Chris
|
@@ -114,7 +118,7 @@ the `mustache(5)` manpage or
|
|
114
118
|
## Escaping
|
115
119
|
|
116
120
|
Mustache does escape all values when using the standard double
|
117
|
-
Mustache syntax. Characters which will be escaped: `& \ " < >` (as
|
121
|
+
Mustache syntax. Characters which will be escaped: `& \ " < >` (as
|
118
122
|
well as `'` in Ruby `>= 2.0`). To disable escaping, simply use triple
|
119
123
|
mustaches like `{{{unescaped_variable}}}`.
|
120
124
|
|
@@ -136,11 +140,12 @@ Given this template (winner.mustache):
|
|
136
140
|
|
137
141
|
We can fill in the values at will:
|
138
142
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
143
|
+
```ruby
|
144
|
+
view = Winner.new
|
145
|
+
view[:name] = 'George'
|
146
|
+
view[:value] = 100
|
147
|
+
view.render
|
148
|
+
```
|
144
149
|
Which returns:
|
145
150
|
|
146
151
|
Hello George
|
@@ -148,11 +153,10 @@ Which returns:
|
|
148
153
|
|
149
154
|
We can re-use the same object, too:
|
150
155
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
+
```ruby
|
157
|
+
view[:name] = 'Tony'
|
158
|
+
view.render # => Hello Tony\nYou have just won 100 bucks!
|
159
|
+
```
|
156
160
|
|
157
161
|
## Templates
|
158
162
|
|
@@ -165,33 +169,38 @@ follows the classic Ruby naming convention.
|
|
165
169
|
You can set the search path using `Mustache.template_path`. It can be set on a
|
166
170
|
class by class basis:
|
167
171
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
172
|
+
```ruby
|
173
|
+
class Simple < Mustache
|
174
|
+
self.template_path = __dir__
|
175
|
+
end
|
176
|
+
```
|
173
177
|
Now `Simple` will look for `simple.mustache` in the directory it resides
|
174
178
|
in, no matter the cwd.
|
175
179
|
|
176
180
|
If you want to just change what template is used you can set
|
177
181
|
`Mustache.template_file` directly:
|
178
182
|
|
179
|
-
|
180
|
-
|
183
|
+
```ruby
|
184
|
+
Simple.template_file = './blah.mustache'
|
185
|
+
```
|
181
186
|
Mustache also allows you to define the extension it'll use.
|
182
187
|
|
183
|
-
|
184
|
-
|
188
|
+
```ruby
|
189
|
+
Simple.template_extension = 'xml'
|
190
|
+
```
|
185
191
|
Given all other defaults, the above line will cause Mustache to look
|
186
192
|
for './blah.xml'
|
187
193
|
|
188
194
|
Feel free to set the template directly:
|
189
195
|
|
190
|
-
|
196
|
+
```ruby
|
197
|
+
Simple.template = 'Hi {{person}}!'
|
198
|
+
```
|
191
199
|
|
192
200
|
Or set a different template for a single instance:
|
193
|
-
|
194
|
-
|
201
|
+
```ruby
|
202
|
+
Simple.new.template = 'Hi {{person}}!'
|
203
|
+
```
|
195
204
|
|
196
205
|
Whatever works.
|
197
206
|
|
@@ -211,46 +220,50 @@ you want to use in all your views? No problem.
|
|
211
220
|
|
212
221
|
This is just Ruby, after all.
|
213
222
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
223
|
+
```ruby
|
224
|
+
module ViewHelpers
|
225
|
+
def gravatar
|
226
|
+
gravatar_id = Digest::MD5.hexdigest(self[:email].to_s.strip.downcase)
|
227
|
+
gravatar_for_id(gravatar_id)
|
228
|
+
end
|
219
229
|
|
220
|
-
|
221
|
-
|
222
|
-
|
230
|
+
def gravatar_for_id(gid, size = 30)
|
231
|
+
"#{gravatar_host}/avatar/#{gid}?s=#{size}"
|
232
|
+
end
|
223
233
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
234
|
+
def gravatar_host
|
235
|
+
@ssl ? 'https://secure.gravatar.com' : 'http://www.gravatar.com'
|
236
|
+
end
|
237
|
+
end
|
238
|
+
```
|
228
239
|
|
229
240
|
Then just include it:
|
230
241
|
|
231
|
-
|
232
|
-
|
242
|
+
```ruby
|
243
|
+
class Simple < Mustache
|
244
|
+
include ViewHelpers
|
233
245
|
|
234
|
-
|
235
|
-
|
236
|
-
|
246
|
+
def name
|
247
|
+
"Chris"
|
248
|
+
end
|
237
249
|
|
238
|
-
|
239
|
-
|
240
|
-
|
250
|
+
def value
|
251
|
+
10_000
|
252
|
+
end
|
241
253
|
|
242
|
-
|
243
|
-
|
244
|
-
|
254
|
+
def taxed_value
|
255
|
+
value * 0.6
|
256
|
+
end
|
245
257
|
|
246
|
-
|
247
|
-
|
248
|
-
|
258
|
+
def in_ca
|
259
|
+
true
|
260
|
+
end
|
249
261
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
262
|
+
def users
|
263
|
+
User.all
|
264
|
+
end
|
265
|
+
end
|
266
|
+
```
|
254
267
|
|
255
268
|
Great, but what about that `@ssl` ivar in `gravatar_host`? There are
|
256
269
|
many ways we can go about setting it.
|
@@ -259,19 +272,21 @@ Here's an example which illustrates a key feature of Mustache: you
|
|
259
272
|
are free to use the `initialize` method just as you would in any
|
260
273
|
normal class.
|
261
274
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
def initialize(ssl = false)
|
266
|
-
@ssl = ssl
|
267
|
-
end
|
275
|
+
```ruby
|
276
|
+
class Simple < Mustache
|
277
|
+
include ViewHelpers
|
268
278
|
|
269
|
-
|
270
|
-
|
279
|
+
def initialize(ssl = false)
|
280
|
+
@ssl = ssl
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
271
284
|
|
272
285
|
Now:
|
273
286
|
|
274
|
-
|
287
|
+
```ruby
|
288
|
+
Simple.new(request.ssl?).render
|
289
|
+
```
|
275
290
|
|
276
291
|
Finally, our template might look like this:
|
277
292
|
|
@@ -286,7 +301,7 @@ Finally, our template might look like this:
|
|
286
301
|
|
287
302
|
### Sinatra
|
288
303
|
|
289
|
-
Sinatra integration is available with the
|
304
|
+
Sinatra integration is available with the
|
290
305
|
[mustache-sinatra gem](https://github.com/mustache/mustache-sinatra).
|
291
306
|
|
292
307
|
An example Sinatra application is also provided:
|
@@ -305,13 +320,17 @@ contained in a hash set using `set :mustache, hash`.
|
|
305
320
|
Mustache also provides a `Rack::Bug` panel.
|
306
321
|
First you have to install the `rack-bug-mustache_panel` gem, then in your `config.ru` add the following code:
|
307
322
|
|
308
|
-
|
309
|
-
|
323
|
+
```ruby
|
324
|
+
require 'rack/bug/panels/mustache_panel'
|
325
|
+
use Rack::Bug::MustachePanel
|
326
|
+
```
|
310
327
|
|
311
328
|
Using Rails? Add this to your initializer or environment file:
|
312
329
|
|
313
|
-
|
314
|
-
|
330
|
+
```ruby
|
331
|
+
require 'rack/bug/panels/mustache_panel'
|
332
|
+
config.middleware.use "Rack::Bug::MustachePanel"
|
333
|
+
```
|
315
334
|
|
316
335
|
![Rack::Bug][5]
|
317
336
|
|
@@ -390,6 +409,6 @@ or IRC:
|
|
390
409
|
[is]: https://github.com/mustache/mustache/issues
|
391
410
|
[irc]: irc://irc.freenode.net/#{
|
392
411
|
[vim]: https://github.com/mustache/vim-mustache-handlebars
|
393
|
-
[emacs]: https://github.com/mustache/
|
412
|
+
[emacs]: https://github.com/mustache/emacs
|
394
413
|
[tmbundle]: https://github.com/defunkt/Mustache.tmbundle
|
395
414
|
[diff]: https://gist.github.com/defunkt/345490
|
data/lib/mustache.rb
CHANGED
@@ -74,6 +74,12 @@ require 'mustache/utils'
|
|
74
74
|
#
|
75
75
|
class Mustache
|
76
76
|
|
77
|
+
# Initialize a new mustache instance.
|
78
|
+
# @param [Hash] options An options hash
|
79
|
+
def initialize(options = {})
|
80
|
+
@options = options
|
81
|
+
end
|
82
|
+
|
77
83
|
# Instantiates an instance of this class and calls `render` with
|
78
84
|
# the passed args.
|
79
85
|
#
|
@@ -266,13 +272,16 @@ class Mustache
|
|
266
272
|
Mustache::Utils::String.new(classified).underscore(view_namespace)
|
267
273
|
end
|
268
274
|
|
269
|
-
# @param [Template,String] obj
|
270
|
-
|
271
|
-
|
275
|
+
# @param [Template,String] obj Turns `obj` into a template
|
276
|
+
# @param [Hash] options Options for template creation
|
277
|
+
def self.templateify(obj, options = {})
|
278
|
+
obj.is_a?(Template) ? obj : Template.new(obj, options)
|
272
279
|
end
|
273
280
|
|
274
281
|
def templateify(obj)
|
275
|
-
self.
|
282
|
+
opts = {:partial_resolver => self.method(:partial)}
|
283
|
+
opts.merge!(@options) if @options.is_a?(Hash)
|
284
|
+
self.class.templateify(obj, opts)
|
276
285
|
end
|
277
286
|
|
278
287
|
# Return the value of the configuration setting on the superclass, or return
|
data/lib/mustache/context.rb
CHANGED
@@ -159,6 +159,7 @@ class Mustache
|
|
159
159
|
def find_in_hash(obj, key, default)
|
160
160
|
return obj[key] if obj.has_key?(key)
|
161
161
|
return obj[key.to_s] if obj.has_key?(key.to_s)
|
162
|
+
return obj[key] if obj.respond_to?(:default_proc) && obj.default_proc && obj[key]
|
162
163
|
|
163
164
|
# If default is :__missing then we are from #fetch which is hunting through the stack
|
164
165
|
# If default is nil then we are reducing dot notation
|
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.
|
@@ -104,13 +105,14 @@ class Mustache
|
|
104
105
|
# string we can use.
|
105
106
|
code = compile(content)
|
106
107
|
|
107
|
-
#
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
114
116
|
t = Mustache::Template.new(v.call(#{raw.inspect}).to_s)
|
115
117
|
def t.tokens(src=@source)
|
116
118
|
p = Mustache::Parser.new
|
@@ -118,12 +120,22 @@ class Mustache
|
|
118
120
|
p.compile(src)
|
119
121
|
end
|
120
122
|
t.render(ctx.dup)
|
121
|
-
|
122
|
-
|
123
|
-
v = [v] unless v.is_a?(Array) || v.is_a?(Mustache::Enumerable) || defined?(Enumerator) && v.is_a?(Enumerator)
|
123
|
+
compiled
|
124
|
+
end
|
124
125
|
|
125
|
-
|
126
|
-
|
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 { |h| ctx.push(h); r = #{code}; ctx.pop; r }.join
|
137
|
+
else
|
138
|
+
ctx.push(v); r = #{code}; ctx.pop; r
|
127
139
|
end
|
128
140
|
compiled
|
129
141
|
end
|
@@ -157,7 +169,7 @@ class Mustache
|
|
157
169
|
ev(<<-compiled)
|
158
170
|
v = #{compile!(name)}
|
159
171
|
if v.is_a?(Proc)
|
160
|
-
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)'}
|
161
173
|
end
|
162
174
|
v.to_s
|
163
175
|
compiled
|
@@ -168,7 +180,7 @@ class Mustache
|
|
168
180
|
ev(<<-compiled)
|
169
181
|
v = #{compile!(name)}
|
170
182
|
if v.is_a?(Proc)
|
171
|
-
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)'}
|
172
184
|
end
|
173
185
|
ctx.escapeHTML(v.to_s)
|
174
186
|
compiled
|
@@ -180,11 +192,15 @@ class Mustache
|
|
180
192
|
names = names.map { |n| n.to_sym }
|
181
193
|
|
182
194
|
initial, *rest = names
|
183
|
-
|
184
|
-
|
185
|
-
value && ctx.find(value, key)
|
186
|
-
|
187
|
-
|
195
|
+
if rest.any?
|
196
|
+
<<-compiled
|
197
|
+
#{rest.inspect}.reduce(ctx[#{initial.inspect}]) { |value, key| value && ctx.find(value, key) }
|
198
|
+
compiled
|
199
|
+
else
|
200
|
+
<<-compiled
|
201
|
+
ctx[#{initial.inspect}]
|
202
|
+
compiled
|
203
|
+
end
|
188
204
|
end
|
189
205
|
|
190
206
|
# An interpolation-friendly version of a string, for use within a
|
data/lib/mustache/parser.rb
CHANGED
@@ -84,22 +84,35 @@ EOF
|
|
84
84
|
# the rest only allow ALLOWED_CONTENT.
|
85
85
|
ANY_CONTENT = [ '!', '=' ].map(&:freeze)
|
86
86
|
|
87
|
-
|
87
|
+
attr_reader :otag, :ctag
|
88
88
|
|
89
89
|
# Accepts an options hash which does nothing but may be used in
|
90
90
|
# the future.
|
91
91
|
def initialize(options = {})
|
92
|
-
@options =
|
92
|
+
@options = options
|
93
|
+
@option_inline_partials_at_compile_time = options[:inline_partials_at_compile_time]
|
94
|
+
if @option_inline_partials_at_compile_time
|
95
|
+
@partial_resolver = options[:partial_resolver]
|
96
|
+
raise ArgumentError.new "Missing or invalid partial_resolver" unless @partial_resolver.respond_to? :call
|
97
|
+
end
|
98
|
+
|
99
|
+
# Initialize default tags
|
100
|
+
self.otag ||= '{{'
|
101
|
+
self.ctag ||= '}}'
|
93
102
|
end
|
94
103
|
|
95
104
|
# The opening tag delimiter. This may be changed at runtime.
|
96
|
-
def otag
|
97
|
-
|
105
|
+
def otag=(value)
|
106
|
+
regex = regexp value
|
107
|
+
@otag_regex = /([ \t]*)?#{regex}/
|
108
|
+
@otag_not_regex = /(^[ \t]*)?#{regex}/
|
109
|
+
@otag = value
|
98
110
|
end
|
99
111
|
|
100
112
|
# The closing tag delimiter. This too may be changed at runtime.
|
101
|
-
def ctag
|
102
|
-
@
|
113
|
+
def ctag=(value)
|
114
|
+
@ctag_regex = regexp value
|
115
|
+
@ctag = value
|
103
116
|
end
|
104
117
|
|
105
118
|
# Given a string template, returns an array of tokens.
|
@@ -134,9 +147,9 @@ EOF
|
|
134
147
|
private
|
135
148
|
|
136
149
|
|
137
|
-
def content_tags type,
|
150
|
+
def content_tags type, current_ctag_regex
|
138
151
|
if ANY_CONTENT.include?(type)
|
139
|
-
r = /\s*#{regexp(type)}?#{
|
152
|
+
r = /\s*#{regexp(type)}?#{current_ctag_regex}/
|
140
153
|
scan_until_exclusive(r)
|
141
154
|
else
|
142
155
|
@scanner.scan(ALLOWED_CONTENT)
|
@@ -147,8 +160,8 @@ EOF
|
|
147
160
|
send("scan_tag_#{type}", content, fetch, padding, pre_match_position)
|
148
161
|
end
|
149
162
|
|
150
|
-
def find_closing_tag scanner,
|
151
|
-
error "Unclosed tag" unless scanner.scan(
|
163
|
+
def find_closing_tag scanner, current_ctag_regex
|
164
|
+
error "Unclosed tag" unless scanner.scan(current_ctag_regex)
|
152
165
|
end
|
153
166
|
|
154
167
|
# Find {{mustaches}} and add them to the @result array.
|
@@ -158,7 +171,7 @@ EOF
|
|
158
171
|
pre_match_position = @scanner.pos
|
159
172
|
last_index = @result.length
|
160
173
|
|
161
|
-
return unless @scanner.scan
|
174
|
+
return unless @scanner.scan @otag_regex
|
162
175
|
padding = @scanner[1] || ''
|
163
176
|
|
164
177
|
# Don't touch the preceding whitespace unless we're matching the start
|
@@ -171,13 +184,13 @@ EOF
|
|
171
184
|
|
172
185
|
# Since {{= rewrites ctag, we store the ctag which should be used
|
173
186
|
# when parsing this specific tag.
|
174
|
-
|
187
|
+
current_ctag_regex = @ctag_regex
|
175
188
|
type = @scanner.scan(self.class.valid_types)
|
176
189
|
@scanner.skip(/\s*/)
|
177
190
|
|
178
191
|
# ANY_CONTENT tags allow any character inside of them, while
|
179
192
|
# other tags (such as variables) are more strict.
|
180
|
-
content = content_tags(type,
|
193
|
+
content = content_tags(type, current_ctag_regex)
|
181
194
|
|
182
195
|
# We found {{ but we can't figure out what's going on inside.
|
183
196
|
error "Illegal content in tag" if content.empty?
|
@@ -196,7 +209,7 @@ EOF
|
|
196
209
|
@scanner.skip(/\s+/)
|
197
210
|
@scanner.skip(regexp(type)) if type
|
198
211
|
|
199
|
-
find_closing_tag(@scanner,
|
212
|
+
find_closing_tag(@scanner, current_ctag_regex)
|
200
213
|
|
201
214
|
# If this tag was the only non-whitespace content on this line, strip
|
202
215
|
# the remaining whitespace. If not, but we've been hanging on to padding
|
@@ -218,7 +231,7 @@ EOF
|
|
218
231
|
|
219
232
|
# Try to find static text, e.g. raw HTML with no {{mustaches}}.
|
220
233
|
def scan_text
|
221
|
-
text = scan_until_exclusive
|
234
|
+
text = scan_until_exclusive @otag_not_regex
|
222
235
|
|
223
236
|
if text.nil?
|
224
237
|
# Couldn't find any otag, which means the rest is just static text.
|
@@ -263,7 +276,7 @@ EOF
|
|
263
276
|
# Used to quickly convert a string into a regular expression
|
264
277
|
# usable by the string scanner.
|
265
278
|
def regexp(thing)
|
266
|
-
|
279
|
+
Regexp.new Regexp.escape(thing) if thing
|
267
280
|
end
|
268
281
|
|
269
282
|
# Raises a SyntaxError. The message should be the name of the
|
@@ -334,7 +347,13 @@ EOF
|
|
334
347
|
|
335
348
|
|
336
349
|
def scan_tag_open_partial content, fetch, padding, pre_match_position
|
337
|
-
@result <<
|
350
|
+
@result << if @option_inline_partials_at_compile_time
|
351
|
+
partial = @partial_resolver.call content
|
352
|
+
partial.gsub!(/^/, padding) unless padding.empty?
|
353
|
+
self.class.new(@options).compile partial
|
354
|
+
else
|
355
|
+
[:mustache, :partial, content, offset, padding]
|
356
|
+
end
|
338
357
|
end
|
339
358
|
alias_method :'scan_tag_<', :scan_tag_open_partial
|
340
359
|
alias_method :'scan_tag_>', :scan_tag_open_partial
|
data/lib/mustache/template.rb
CHANGED
@@ -18,9 +18,10 @@ class Mustache
|
|
18
18
|
attr_reader :source
|
19
19
|
|
20
20
|
# Expects a Mustache template as a string along with a template
|
21
|
-
# path, which it uses to find partials.
|
22
|
-
def initialize(source)
|
21
|
+
# path, which it uses to find partials. Options may be passed.
|
22
|
+
def initialize(source, options = {})
|
23
23
|
@source = source
|
24
|
+
@options = options
|
24
25
|
end
|
25
26
|
|
26
27
|
# Renders the `@source` Mustache template using the given
|
@@ -46,7 +47,7 @@ class Mustache
|
|
46
47
|
# Does the dirty work of transforming a Mustache template into an
|
47
48
|
# interpolation-friendly Ruby string.
|
48
49
|
def compile(src = @source)
|
49
|
-
Generator.new.compile(tokens(src))
|
50
|
+
Generator.new(@options).compile(tokens(src))
|
50
51
|
end
|
51
52
|
alias_method :to_s, :compile
|
52
53
|
|
@@ -55,7 +56,7 @@ class Mustache
|
|
55
56
|
# @return [Array] Array of tokens.
|
56
57
|
#
|
57
58
|
def tokens(src = @source)
|
58
|
-
Parser.new.compile(src)
|
59
|
+
Parser.new(@options).compile(src)
|
59
60
|
end
|
60
61
|
|
61
62
|
# Returns an array of tags.
|
data/lib/mustache/version.rb
CHANGED
data/man/mustache.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "MUSTACHE" "1" "
|
4
|
+
.TH "MUSTACHE" "1" "November 2016" "DEFUNKT" "Mustache Manual"
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBmustache\fR \- Mustache processor
|
@@ -75,7 +75,7 @@ $ cat template\.mustache
|
|
75
75
|
Hi {{name}}!
|
76
76
|
{{/names}}
|
77
77
|
|
78
|
-
$
|
78
|
+
$ mustache data\.yml template\.mustache
|
79
79
|
Hi chris!
|
80
80
|
Hi mark!
|
81
81
|
Hi scott!
|
@@ -106,7 +106,7 @@ name: scott
|
|
106
106
|
$ cat template\.mustache
|
107
107
|
Hi {{name}}!
|
108
108
|
|
109
|
-
$
|
109
|
+
$ mustache data\.yml template\.mustache
|
110
110
|
Hi chris!
|
111
111
|
Hi mark!
|
112
112
|
Hi scott!
|
data/man/mustache.1.html
CHANGED
@@ -123,7 +123,7 @@ $ cat template.mustache
|
|
123
123
|
Hi {{name}}!
|
124
124
|
{{/names}}
|
125
125
|
|
126
|
-
$
|
126
|
+
$ mustache data.yml template.mustache
|
127
127
|
Hi chris!
|
128
128
|
Hi mark!
|
129
129
|
Hi scott!
|
@@ -146,7 +146,7 @@ name: scott
|
|
146
146
|
$ cat template.mustache
|
147
147
|
Hi {{name}}!
|
148
148
|
|
149
|
-
$
|
149
|
+
$ mustache data.yml template.mustache
|
150
150
|
Hi chris!
|
151
151
|
Hi mark!
|
152
152
|
Hi scott!
|
@@ -204,7 +204,7 @@ data
|
|
204
204
|
|
205
205
|
<ol class='man-decor man-foot man foot'>
|
206
206
|
<li class='tl'>DEFUNKT</li>
|
207
|
-
<li class='tc'>
|
207
|
+
<li class='tc'>November 2016</li>
|
208
208
|
<li class='tr'>mustache(1)</li>
|
209
209
|
</ol>
|
210
210
|
|
data/man/mustache.1.ron
CHANGED
@@ -48,7 +48,7 @@ Now let's combine them.
|
|
48
48
|
Hi {{name}}!
|
49
49
|
{{/names}}
|
50
50
|
|
51
|
-
$
|
51
|
+
$ mustache data.yml template.mustache
|
52
52
|
Hi chris!
|
53
53
|
Hi mark!
|
54
54
|
Hi scott!
|
@@ -70,7 +70,7 @@ For example:
|
|
70
70
|
$ cat template.mustache
|
71
71
|
Hi {{name}}!
|
72
72
|
|
73
|
-
$
|
73
|
+
$ mustache data.yml template.mustache
|
74
74
|
Hi chris!
|
75
75
|
Hi mark!
|
76
76
|
Hi scott!
|
data/man/mustache.5
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "MUSTACHE" "5" "
|
4
|
+
.TH "MUSTACHE" "5" "November 2016" "DEFUNKT" "Mustache Manual"
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBmustache\fR \- Logic\-less templates\.
|
data/man/mustache.5.html
CHANGED
data/test/helper.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter '/test/'
|
4
|
+
end
|
2
5
|
|
3
|
-
require
|
4
|
-
CodeClimate::TestReporter.start
|
6
|
+
require 'minitest/autorun'
|
5
7
|
|
6
8
|
Dir[File.dirname(__FILE__) + '/fixtures/*.rb'].each do |f|
|
7
9
|
require f
|
data/test/mustache_test.rb
CHANGED
@@ -362,7 +362,7 @@ data
|
|
362
362
|
end
|
363
363
|
|
364
364
|
def each *args, &block
|
365
|
-
@people.each
|
365
|
+
@people.each(*args, &block)
|
366
366
|
end
|
367
367
|
end
|
368
368
|
|
@@ -506,6 +506,24 @@ Benvolio is 15
|
|
506
506
|
assert_equal 1, view.calls
|
507
507
|
end
|
508
508
|
|
509
|
+
def test_sections_returning_lambdas_get_called_dynamically_with_text
|
510
|
+
view = Mustache.new
|
511
|
+
view.template = '{{name}}'
|
512
|
+
view[:name] = lambda { '{{dynamic_name}}' }
|
513
|
+
view[:dynamic_name] = 'Chris'
|
514
|
+
|
515
|
+
assert_equal "Chris", view.render.chomp
|
516
|
+
end
|
517
|
+
|
518
|
+
def test_sections_returning_lambdas_get_not_called_dynamically_with_text_if_static
|
519
|
+
view = Mustache.new :static_lambdas => true
|
520
|
+
view.template = '{{name}}'
|
521
|
+
view[:name] = lambda { '{{dynamic_name}}' }
|
522
|
+
view[:dynamic_name] = 'Chris'
|
523
|
+
|
524
|
+
assert_equal "{{dynamic_name}}", view.render.chomp
|
525
|
+
end
|
526
|
+
|
509
527
|
def test_sections_which_refer_to_unary_method_call_them_as_proc
|
510
528
|
kls = Class.new(Mustache) do
|
511
529
|
def unary_method(arg)
|
@@ -706,6 +724,23 @@ text
|
|
706
724
|
end
|
707
725
|
end
|
708
726
|
|
727
|
+
def test_hash_default_proc
|
728
|
+
template = <<template
|
729
|
+
{{greetings.Peter}}
|
730
|
+
{{greetings.Paul}}
|
731
|
+
{{greetings.Mary}}
|
732
|
+
template
|
733
|
+
data = {
|
734
|
+
'greetings' => Hash.new { |hash, key| hash[key] = "Hello, #{key}!" }
|
735
|
+
}
|
736
|
+
|
737
|
+
assert_equal <<expected, Mustache.render(template, data)
|
738
|
+
Hello, Peter!
|
739
|
+
Hello, Paul!
|
740
|
+
Hello, Mary!
|
741
|
+
expected
|
742
|
+
end
|
743
|
+
|
709
744
|
def test_array_of_arrays
|
710
745
|
template = <<template
|
711
746
|
{{#items}}
|
@@ -780,7 +815,17 @@ template
|
|
780
815
|
template = '%%{{title}}%%'
|
781
816
|
|
782
817
|
assert_equal '%%title%%', Mustache.render(template, hashlike)
|
783
|
-
|
784
818
|
end
|
785
819
|
|
820
|
+
def test_instance_with_initialize_render
|
821
|
+
klass = Class.new(Mustache) do
|
822
|
+
def initialize(name)
|
823
|
+
@name = name
|
824
|
+
end
|
825
|
+
attr_reader :name
|
826
|
+
end
|
827
|
+
|
828
|
+
klass.template = "Hi {{name}}!"
|
829
|
+
assert_equal "Hi Dougal!", klass.new("Dougal").render
|
830
|
+
end
|
786
831
|
end
|
data/test/partial_test.rb
CHANGED
@@ -20,6 +20,19 @@ end_partial
|
|
20
20
|
assert_equal "Again, success!", view.render
|
21
21
|
end
|
22
22
|
|
23
|
+
def test_partial_inlining
|
24
|
+
view = Mustache.new :inline_partials_at_compile_time => true
|
25
|
+
view.template = '{{> test/fixtures/inner_partial}}'
|
26
|
+
view[:title] = 'success'
|
27
|
+
|
28
|
+
# Test the rendered result first
|
29
|
+
assert_equal "Again, success!", view.render
|
30
|
+
|
31
|
+
# Now the template should be compiled.
|
32
|
+
# There should be no :partial instruction as the partial has been in-lined.
|
33
|
+
assert_equal false, view.template.tokens.flatten.include?(:partial)
|
34
|
+
end
|
35
|
+
|
23
36
|
def test_view_partial_inherits_context
|
24
37
|
klass = Class.new(TemplatePartial)
|
25
38
|
view = klass.new
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mustache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wanstrath
|
@@ -11,127 +11,118 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2017-03-26 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
|
-
- - ~>
|
20
|
+
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '1.6'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ~>
|
27
|
+
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '1.6'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
|
-
- - ~>
|
34
|
+
- - "~>"
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '10.3'
|
37
37
|
type: :development
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - ~>
|
41
|
+
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '10.3'
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
45
|
name: minitest
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
|
-
- - ~>
|
48
|
+
- - "~>"
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '5.4'
|
51
51
|
type: :development
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- - ~>
|
55
|
+
- - "~>"
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: '5.4'
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: benchmark-ips
|
60
60
|
requirement: !ruby/object:Gem::Requirement
|
61
61
|
requirements:
|
62
|
-
- -
|
62
|
+
- - ">="
|
63
63
|
- !ruby/object:Gem::Version
|
64
64
|
version: '0'
|
65
65
|
type: :development
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- -
|
69
|
+
- - ">="
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
73
|
name: ruby-prof
|
74
74
|
requirement: !ruby/object:Gem::Requirement
|
75
75
|
requirements:
|
76
|
-
- -
|
76
|
+
- - ">="
|
77
77
|
- !ruby/object:Gem::Version
|
78
78
|
version: '0'
|
79
79
|
type: :development
|
80
80
|
prerelease: false
|
81
81
|
version_requirements: !ruby/object:Gem::Requirement
|
82
82
|
requirements:
|
83
|
-
- -
|
83
|
+
- - ">="
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
86
|
- !ruby/object:Gem::Dependency
|
87
87
|
name: rdoc
|
88
88
|
requirement: !ruby/object:Gem::Requirement
|
89
89
|
requirements:
|
90
|
-
- - ~>
|
90
|
+
- - "~>"
|
91
91
|
- !ruby/object:Gem::Version
|
92
92
|
version: '4.1'
|
93
93
|
type: :development
|
94
94
|
prerelease: false
|
95
95
|
version_requirements: !ruby/object:Gem::Requirement
|
96
96
|
requirements:
|
97
|
-
- - ~>
|
97
|
+
- - "~>"
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '4.1'
|
100
100
|
- !ruby/object:Gem::Dependency
|
101
101
|
name: ronn
|
102
102
|
requirement: !ruby/object:Gem::Requirement
|
103
103
|
requirements:
|
104
|
-
- - ~>
|
104
|
+
- - "~>"
|
105
105
|
- !ruby/object:Gem::Version
|
106
106
|
version: '0.7'
|
107
107
|
type: :development
|
108
108
|
prerelease: false
|
109
109
|
version_requirements: !ruby/object:Gem::Requirement
|
110
110
|
requirements:
|
111
|
-
- - ~>
|
111
|
+
- - "~>"
|
112
112
|
- !ruby/object:Gem::Version
|
113
113
|
version: '0.7'
|
114
|
-
description:
|
115
|
-
|
114
|
+
description: |
|
115
|
+
Inspired by ctemplate, Mustache is a framework-agnostic way to render
|
116
116
|
logic-free views.
|
117
117
|
|
118
|
-
|
119
118
|
As ctemplates says, "It emphasizes separating logic from presentation:
|
120
|
-
|
121
119
|
it is impossible to embed application logic in this template
|
122
|
-
|
123
120
|
language.
|
124
121
|
|
125
|
-
|
126
122
|
Think of Mustache as a replacement for your views. Instead of views
|
127
|
-
|
128
123
|
consisting of ERB or HAML with random helpers and arbitrary logic,
|
129
|
-
|
130
124
|
your views are broken into two parts: a Ruby class and an HTML
|
131
|
-
|
132
125
|
template.
|
133
|
-
|
134
|
-
'
|
135
126
|
email: rokusu@gmail.com
|
136
127
|
executables:
|
137
128
|
- mustache
|
@@ -219,17 +210,17 @@ require_paths:
|
|
219
210
|
- lib
|
220
211
|
required_ruby_version: !ruby/object:Gem::Requirement
|
221
212
|
requirements:
|
222
|
-
- -
|
213
|
+
- - ">="
|
223
214
|
- !ruby/object:Gem::Version
|
224
215
|
version: '2.0'
|
225
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
226
217
|
requirements:
|
227
|
-
- -
|
218
|
+
- - ">="
|
228
219
|
- !ruby/object:Gem::Version
|
229
220
|
version: '0'
|
230
221
|
requirements: []
|
231
222
|
rubyforge_project:
|
232
|
-
rubygems_version: 2.
|
223
|
+
rubygems_version: 2.6.8
|
233
224
|
signing_key:
|
234
225
|
specification_version: 4
|
235
226
|
summary: Mustache is a framework-agnostic way to render logic-free views.
|