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 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