mustache 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.path`. It can be set on a
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.path = File.dirname(__FILE__)
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
- You can also go ahead and set the template directly:
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
- You can also set a different template for only a single instance:
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,6 @@
1
+ {{=<% %>=}}
2
+ * <% first %>
3
+ <%=| |=%>
4
+ * | second |
5
+ |={{ }}=|
6
+ * {{ third }}
@@ -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
@@ -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
@@ -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 =~ /\{\{\#(.+)\}\}\s*(.+)\{\{\/\1\}\}\s*/m
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("(v = ctx[#{name}]) ? v.respond_to?(:each) ? "\
57
- "(#{ctxtmp}=ctx.dup; r=v.map{|h|ctx.update(h);#{code}}.join; "\
58
- "ctx.replace(#{ctxtmp});r) : #{code} : ''")
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 =~ /\{\{(!|<|\{)?([^\/#]+?)\1?\}\}+/
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)")
@@ -1,3 +1,3 @@
1
1
  class Mustache
2
- Version = '0.2.2'
2
+ Version = '0.3.0'
3
3
  end
@@ -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
- <ul>
28
- <li><strong>red</strong></li>
29
- <li><a href="#Green">green</a></li>
30
- <li><a href="#Blue">blue</a></li>
31
- </ul>
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.2.2
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-11 00:00:00 -07:00
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