mustache 0.2.2 → 0.3.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.
- data/HISTORY.md +13 -0
- data/README.md +55 -4
- data/Rakefile +13 -0
- data/examples/delimiters.html +6 -0
- data/examples/delimiters.rb +22 -0
- data/lib/mustache.rb +12 -1
- data/lib/mustache/sinatra.rb +4 -1
- data/lib/mustache/template.rb +40 -5
- data/lib/mustache/version.rb +1 -1
- data/test/mustache_test.rb +84 -5
- metadata +5 -2
data/HISTORY.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.3.0 (2009-??-??)
|
2
|
+
|
3
|
+
* Set Delimiter tags are now supported. See the README
|
4
|
+
* Improved error message when an enumerable section did not return all
|
5
|
+
hashes.
|
6
|
+
* Added a shortcut: if a section's value is a single hash, treat is as
|
7
|
+
a one element array whose value is the hash.
|
8
|
+
* Bugfix: String templates set at the class level were not compiled
|
9
|
+
* Added a class-level `compiled?` method for checking if a template
|
10
|
+
has been compiled.
|
11
|
+
* Added an instance-level `compiled?` method.
|
12
|
+
* Cache template compilation in Sinatra
|
13
|
+
|
1
14
|
## 0.2.2 (2009-10-11)
|
2
15
|
|
3
16
|
* Improved documentation
|
data/README.md
CHANGED
@@ -144,6 +144,24 @@ And this view code:
|
|
144
144
|
When rendered, our view will contain a list of all repository names in
|
145
145
|
the database.
|
146
146
|
|
147
|
+
As a convenience, if a section returns a hash (as opposed to an array
|
148
|
+
or a boolean) it will be treated as a single item array.
|
149
|
+
|
150
|
+
With the above template, we could use this Ruby code for a single
|
151
|
+
iteration:
|
152
|
+
|
153
|
+
def repo
|
154
|
+
{ :name => Repository.first.to_s }
|
155
|
+
end
|
156
|
+
|
157
|
+
This would be treated by Mustache as functionally equivalent to the
|
158
|
+
following:
|
159
|
+
|
160
|
+
def repo
|
161
|
+
[ { :name => Repository.first.to_s } ]
|
162
|
+
end
|
163
|
+
|
164
|
+
|
147
165
|
### Comments
|
148
166
|
|
149
167
|
Comments begin with a bang and are ignored. The following template:
|
@@ -165,6 +183,31 @@ In this way partials can reference variables or sections the calling
|
|
165
183
|
view defines.
|
166
184
|
|
167
185
|
|
186
|
+
### Set Delimiter
|
187
|
+
|
188
|
+
Set Delimiter tags start with an equal sign and change the tag
|
189
|
+
delimiters from {{ and }} to custom strings.
|
190
|
+
|
191
|
+
Consider the following contrived example:
|
192
|
+
|
193
|
+
* {{ default_tags }}
|
194
|
+
{{=<% %>=}}
|
195
|
+
* <% erb_style_tags %>
|
196
|
+
<%={{ }}=%>
|
197
|
+
* {{ default_tags_again }}
|
198
|
+
|
199
|
+
Here we have a list with three items. The first item uses the default
|
200
|
+
tag style, the second uses erb style as defined by the Set Delimiter
|
201
|
+
tag, and the third returns to the default style after yet another Set
|
202
|
+
Delimiter declaration.
|
203
|
+
|
204
|
+
According to [ctemplates][3], this "is useful for languages like TeX, where
|
205
|
+
double-braces may occur in the text and are awkward to use for
|
206
|
+
markup."
|
207
|
+
|
208
|
+
Custom delimiters may not contain whitespace or the equals sign.
|
209
|
+
|
210
|
+
|
168
211
|
Dict-Style Views
|
169
212
|
----------------
|
170
213
|
|
@@ -206,11 +249,11 @@ follows the classic Ruby naming convention.
|
|
206
249
|
|
207
250
|
TemplatePartial => ./template_partial.html
|
208
251
|
|
209
|
-
You can set the search path using `Mustache.
|
252
|
+
You can set the search path using `Mustache.template_path`. It can be set on a
|
210
253
|
class by class basis:
|
211
254
|
|
212
255
|
class Simple < Mustache
|
213
|
-
self.
|
256
|
+
self.template_path = File.dirname(__FILE__)
|
214
257
|
... etc ...
|
215
258
|
end
|
216
259
|
|
@@ -222,11 +265,18 @@ If you want to just change what template is used you can set
|
|
222
265
|
|
223
266
|
Simple.template_file = './blah.html'
|
224
267
|
|
225
|
-
|
268
|
+
Mustache also allows you to define the extension it'll use.
|
269
|
+
|
270
|
+
Simple.template_extension = 'xml'
|
271
|
+
|
272
|
+
Given all other defaults, the above line will cause Mustache to look
|
273
|
+
for './blah.xml'
|
274
|
+
|
275
|
+
Feel free to set the template directly:
|
226
276
|
|
227
277
|
Simple.template = 'Hi {{person}}!'
|
228
278
|
|
229
|
-
|
279
|
+
Or set a different template for a single instance:
|
230
280
|
|
231
281
|
Simple.new.template = 'Hi {{person}}!'
|
232
282
|
|
@@ -347,3 +397,4 @@ Meta
|
|
347
397
|
|
348
398
|
[1]: http://code.google.com/p/google-ctemplate/
|
349
399
|
[2]: http://www.ivan.fomichev.name/2008/05/erlang-template-engine-prototype.html
|
400
|
+
[3]: http://google-ctemplate.googlecode.com/svn/trunk/doc/howto.html
|
data/Rakefile
CHANGED
@@ -47,3 +47,16 @@ task :publish => [ :gemspec, :build ] do
|
|
47
47
|
system "git clean -fd"
|
48
48
|
exec "rake pages"
|
49
49
|
end
|
50
|
+
|
51
|
+
desc "Install the edge gem"
|
52
|
+
task :install_edge => [ :dev_version, :gemspec, :build ] do
|
53
|
+
exec "gem install pkg/mustache-#{Mustache::Version}.gem"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets the current Mustache version to the current dev version
|
57
|
+
task :dev_version do
|
58
|
+
$LOAD_PATH.unshift 'lib/mustache'
|
59
|
+
require 'mustache/version'
|
60
|
+
version = Mustache::Version + '.' + Time.now.to_i.to_s
|
61
|
+
Mustache.const_set(:Version, version)
|
62
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'mustache'
|
3
|
+
|
4
|
+
class Delimiters < Mustache
|
5
|
+
self.path = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
def first
|
8
|
+
"It worked the first time."
|
9
|
+
end
|
10
|
+
|
11
|
+
def second
|
12
|
+
"And it worked the second time."
|
13
|
+
end
|
14
|
+
|
15
|
+
def third
|
16
|
+
"Then, surprisingly, it worked the third time."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if $0 == __FILE__
|
21
|
+
puts Delimiters.to_html
|
22
|
+
end
|
data/lib/mustache.rb
CHANGED
@@ -123,7 +123,18 @@ class Mustache
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def self.template=(template)
|
126
|
-
@template = template
|
126
|
+
@template = templateify(template)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Has this template already been compiled? Compilation is somewhat
|
130
|
+
# expensive so it may be useful to check this before attempting it.
|
131
|
+
def self.compiled?
|
132
|
+
@template.is_a? Template
|
133
|
+
end
|
134
|
+
|
135
|
+
# Has this instance or its class already compiled a template?
|
136
|
+
def compiled?
|
137
|
+
(@template && @template.is_a?(Template)) || self.class.compiled?
|
127
138
|
end
|
128
139
|
|
129
140
|
# template_partial => TemplatePartial
|
data/lib/mustache/sinatra.rb
CHANGED
@@ -67,6 +67,9 @@ class Mustache
|
|
67
67
|
require "#{file}".chomp('.rb')
|
68
68
|
klass = namespace::Views.const_get(name)
|
69
69
|
|
70
|
+
# compile and cache the template
|
71
|
+
klass.template = data
|
72
|
+
|
70
73
|
else
|
71
74
|
# Still nothing. Use the stache.
|
72
75
|
klass = Mustache
|
@@ -90,7 +93,7 @@ class Mustache
|
|
90
93
|
# lets us use {{yield}} in layout.html to render the actual page.
|
91
94
|
instance[:yield] = block.call if block
|
92
95
|
|
93
|
-
instance.template = data
|
96
|
+
instance.template = data unless instance.compiled?
|
94
97
|
instance.to_html
|
95
98
|
end
|
96
99
|
end
|
data/lib/mustache/template.rb
CHANGED
@@ -47,15 +47,30 @@ class Mustache
|
|
47
47
|
# If enumerable, the return value is iterated over (a `for` loop).
|
48
48
|
def compile_sections(src)
|
49
49
|
res = ""
|
50
|
-
while src =~
|
50
|
+
while src =~ /#{otag}\#(.+)#{ctag}\s*(.+)#{otag}\/\1#{ctag}\s*/m
|
51
51
|
# $` = The string to the left of the last successful match
|
52
52
|
res << compile_tags($`)
|
53
53
|
name = $1.strip.to_sym.inspect
|
54
54
|
code = compile($2)
|
55
55
|
ctxtmp = "ctx#{tmpid}"
|
56
|
-
res << ev(
|
57
|
-
|
58
|
-
|
56
|
+
res << ev(<<-compiled)
|
57
|
+
if v = ctx[#{name}]
|
58
|
+
v = [v] if v.is_a?(Hash) # shortcut when passed a single hash
|
59
|
+
if v.respond_to?(:each)
|
60
|
+
#{ctxtmp} = ctx.dup
|
61
|
+
begin
|
62
|
+
r = v.map { |h| ctx.update(h); #{code} }.join
|
63
|
+
rescue TypeError => e
|
64
|
+
raise TypeError,
|
65
|
+
"All elements in {{#{name.to_s[1..-1]}}} are not hashes!"
|
66
|
+
end
|
67
|
+
ctx.replace(#{ctxtmp})
|
68
|
+
r
|
69
|
+
else
|
70
|
+
#{code}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
compiled
|
59
74
|
# $' = The string to the right of the last successful match
|
60
75
|
src = $'
|
61
76
|
end
|
@@ -70,11 +85,13 @@ class Mustache
|
|
70
85
|
# 4. Partial tags - {{< partial_name }}
|
71
86
|
def compile_tags(src)
|
72
87
|
res = ""
|
73
|
-
while src =~
|
88
|
+
while src =~ /#{otag}(=|!|<|\{)?([^\/#]+?)\1?#{ctag}+/
|
74
89
|
res << str($`)
|
75
90
|
case $1
|
76
91
|
when '!'
|
77
92
|
# ignore comments
|
93
|
+
when '='
|
94
|
+
self.otag, self.ctag = $2.strip.split(' ', 2)
|
78
95
|
when '<'
|
79
96
|
res << compile_partial($2.strip)
|
80
97
|
when '{'
|
@@ -108,6 +125,24 @@ class Mustache
|
|
108
125
|
s.inspect[1..-2]
|
109
126
|
end
|
110
127
|
|
128
|
+
# {{ - opening tag delimiter
|
129
|
+
def otag
|
130
|
+
@otag ||= Regexp.escape('{{')
|
131
|
+
end
|
132
|
+
|
133
|
+
def otag=(tag)
|
134
|
+
@otag = Regexp.escape(tag)
|
135
|
+
end
|
136
|
+
|
137
|
+
# }} - closing tag delimiter
|
138
|
+
def ctag
|
139
|
+
@ctag ||= Regexp.escape('}}')
|
140
|
+
end
|
141
|
+
|
142
|
+
def ctag=(tag)
|
143
|
+
@ctag = Regexp.escape(tag)
|
144
|
+
end
|
145
|
+
|
111
146
|
# {{}} - an escaped tag
|
112
147
|
def etag(s)
|
113
148
|
ev("CGI.escapeHTML(ctx[#{s.strip.to_sym.inspect}].to_s)")
|
data/lib/mustache/version.rb
CHANGED
data/test/mustache_test.rb
CHANGED
@@ -9,6 +9,7 @@ require 'escaped'
|
|
9
9
|
require 'unescaped'
|
10
10
|
require 'comments'
|
11
11
|
require 'passenger'
|
12
|
+
require 'delimiters'
|
12
13
|
|
13
14
|
class MustacheTest < Test::Unit::TestCase
|
14
15
|
def test_passenger
|
@@ -24,12 +25,23 @@ end_passenger
|
|
24
25
|
def test_complex_view
|
25
26
|
assert_equal <<-end_complex, ComplexView.render
|
26
27
|
<h1>Colors</h1>
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
<ul>
|
29
|
+
<li><strong>red</strong></li>
|
30
|
+
<li><a href="#Green">green</a></li>
|
31
|
+
<li><a href="#Blue">blue</a></li>
|
32
|
+
</ul>
|
32
33
|
end_complex
|
34
|
+
|
35
|
+
# TODO: Preserve indentation
|
36
|
+
# http://github.com/defunkt/mustache/issues#issue/2
|
37
|
+
# assert_equal <<-end_complex, ComplexView.render
|
38
|
+
# <h1>Colors</h1>
|
39
|
+
# <ul>
|
40
|
+
# <li><strong>red</strong></li>
|
41
|
+
# <li><a href="#Green">green</a></li>
|
42
|
+
# <li><a href="#Blue">blue</a></li>
|
43
|
+
# </ul>
|
44
|
+
# end_complex
|
33
45
|
end
|
34
46
|
|
35
47
|
def test_single_line_sections
|
@@ -131,6 +143,18 @@ Welcome
|
|
131
143
|
end_partial
|
132
144
|
end
|
133
145
|
|
146
|
+
|
147
|
+
def test_delimiters
|
148
|
+
assert_equal <<-end_partial, Delimiters.render
|
149
|
+
|
150
|
+
* It worked the first time.
|
151
|
+
|
152
|
+
* And it worked the second time.
|
153
|
+
|
154
|
+
* Then, surprisingly, it worked the third time.
|
155
|
+
end_partial
|
156
|
+
end
|
157
|
+
|
134
158
|
def test_comments
|
135
159
|
assert_equal "<h1>A Comedy of Errors</h1>\n", Comments.render
|
136
160
|
end
|
@@ -182,4 +206,59 @@ data
|
|
182
206
|
:deploy_to => '/var/www/example.com' )
|
183
207
|
end
|
184
208
|
|
209
|
+
def test_reports_type_errors_in_sections
|
210
|
+
instance = Mustache.new
|
211
|
+
instance[:list] = [ :item, 1234 ]
|
212
|
+
instance.template = '{{#list}} <li>{{item}}</li> {{/list}}'
|
213
|
+
|
214
|
+
assert_raise TypeError do
|
215
|
+
instance.render
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_enumerable_sections_accept_a_hash_as_a_context
|
220
|
+
instance = Mustache.new
|
221
|
+
instance[:list] = { :item => 1234 }
|
222
|
+
instance.template = '{{#list}} <li>{{item}}</li> {{/list}}'
|
223
|
+
|
224
|
+
assert_equal '<li>1234</li>', instance.render.strip
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_knows_when_its_been_compiled_when_set_with_string
|
228
|
+
klass = Class.new(Mustache)
|
229
|
+
|
230
|
+
assert ! klass.compiled?
|
231
|
+
klass.template = 'Hi, {{person}}!'
|
232
|
+
assert klass.compiled?
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_knows_when_its_been_compiled_when_using_a_file_template
|
236
|
+
klass = Class.new(Simple)
|
237
|
+
klass.template_file = File.dirname(__FILE__) + '/../examples/simple.html'
|
238
|
+
|
239
|
+
assert ! klass.compiled?
|
240
|
+
klass.render
|
241
|
+
assert klass.compiled?
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_an_instance_knows_when_its_class_is_compiled
|
245
|
+
instance = Simple.new
|
246
|
+
|
247
|
+
assert ! Simple.compiled?
|
248
|
+
assert ! instance.compiled?
|
249
|
+
|
250
|
+
Simple.render
|
251
|
+
|
252
|
+
assert Simple.compiled?
|
253
|
+
assert instance.compiled?
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_knows_when_its_been_compiled_at_the_instance_level
|
257
|
+
klass = Class.new(Mustache)
|
258
|
+
instance = klass.new
|
259
|
+
|
260
|
+
assert ! instance.compiled?
|
261
|
+
instance.template = 'Hi, {{person}}!'
|
262
|
+
assert instance.compiled?
|
263
|
+
end
|
185
264
|
end
|
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: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wanstrath
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -38,6 +38,8 @@ files:
|
|
38
38
|
- examples/comments.rb
|
39
39
|
- examples/complex_view.html
|
40
40
|
- examples/complex_view.rb
|
41
|
+
- examples/delimiters.html
|
42
|
+
- examples/delimiters.rb
|
41
43
|
- examples/escaped.html
|
42
44
|
- examples/escaped.rb
|
43
45
|
- examples/inner_partial.html
|
@@ -91,6 +93,7 @@ test_files:
|
|
91
93
|
- test/mustache_test.rb
|
92
94
|
- examples/comments.rb
|
93
95
|
- examples/complex_view.rb
|
96
|
+
- examples/delimiters.rb
|
94
97
|
- examples/escaped.rb
|
95
98
|
- examples/passenger.rb
|
96
99
|
- examples/simple.rb
|