mustache 0.5.0 → 0.6.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.
Files changed (52) hide show
  1. data/README.md +30 -179
  2. data/Rakefile +87 -46
  3. data/bin/mustache +10 -19
  4. data/lib/mustache/context.rb +55 -12
  5. data/lib/mustache/sinatra.rb +1 -1
  6. data/lib/mustache/template.rb +3 -15
  7. data/lib/mustache/version.rb +1 -1
  8. data/man/mustache.1 +138 -0
  9. data/man/mustache.1.html +168 -0
  10. data/man/mustache.1.ron +94 -0
  11. data/man/mustache.5 +364 -0
  12. data/man/mustache.5.html +300 -0
  13. data/man/mustache.5.ron +220 -0
  14. data/test/helper.rb +1 -0
  15. data/test/mustache_test.rb +25 -16
  16. metadata +32 -65
  17. data/.gitignore +0 -1
  18. data/.kick +0 -26
  19. data/CONTRIBUTORS +0 -6
  20. data/HISTORY.md +0 -70
  21. data/benchmarks/complex.erb +0 -15
  22. data/benchmarks/complex.haml +0 -10
  23. data/benchmarks/helper.rb +0 -20
  24. data/benchmarks/simple.erb +0 -5
  25. data/benchmarks/speed.rb +0 -76
  26. data/contrib/mustache.vim +0 -69
  27. data/contrib/tpl-mode.el +0 -274
  28. data/examples/comments.mustache +0 -1
  29. data/examples/comments.rb +0 -14
  30. data/examples/complex_view.mustache +0 -16
  31. data/examples/complex_view.rb +0 -34
  32. data/examples/delimiters.mustache +0 -6
  33. data/examples/delimiters.rb +0 -22
  34. data/examples/double_section.mustache +0 -7
  35. data/examples/double_section.rb +0 -14
  36. data/examples/escaped.mustache +0 -1
  37. data/examples/escaped.rb +0 -14
  38. data/examples/inner_partial.mustache +0 -1
  39. data/examples/inner_partial.txt +0 -1
  40. data/examples/namespaced.mustache +0 -1
  41. data/examples/namespaced.rb +0 -25
  42. data/examples/partial_with_module.mustache +0 -3
  43. data/examples/partial_with_module.rb +0 -37
  44. data/examples/passenger.conf +0 -5
  45. data/examples/passenger.rb +0 -27
  46. data/examples/simple.mustache +0 -5
  47. data/examples/simple.rb +0 -26
  48. data/examples/template_partial.mustache +0 -2
  49. data/examples/template_partial.rb +0 -18
  50. data/examples/template_partial.txt +0 -4
  51. data/examples/unescaped.mustache +0 -1
  52. data/examples/unescaped.rb +0 -14
data/README.md CHANGED
@@ -7,6 +7,9 @@ framework-agnostic way to render logic-free views.
7
7
  As ctemplates says, "It emphasizes separating logic from presentation:
8
8
  it is impossible to embed application logic in this template language."
9
9
 
10
+ For a list of implementations (other than Ruby) and tips, see
11
+ <http://defunkt.github.com/mustache/>.
12
+
10
13
 
11
14
  Overview
12
15
  --------
@@ -94,139 +97,22 @@ Simple.
94
97
  Tag Types
95
98
  ---------
96
99
 
97
- Tags are indicated by the double mustaches. `{{name}}` is a tag. Let's
98
- talk about the different types of tags.
99
-
100
- ### Variables
101
-
102
- The most basic tag is the variable. A `{{name}}` tag in a basic
103
- template will try to call the `name` method on your view. If there is
104
- no `name` method, an exception will be raised.
105
-
106
- All variables are HTML escaped by default. If you want to return
107
- unescaped HTML, use the triple mustache: `{{{name}}}`.
108
-
109
- By default a variable "miss" returns an empty string. You can
110
- configure this by setting `Mustache.raise_on_context_miss` to true.
111
-
112
- ### Boolean Sections
113
-
114
- A section begins with a pound and ends with a slash. That is,
115
- `{{#person}}` begins a "person" section while `{{/person}}` ends it.
116
-
117
- If the `person` method exists and calling it returns false, the HTML
118
- between the pound and slash will not be displayed.
119
-
120
- If the `person` method exists and calling it returns true, the HTML
121
- between the pound and slash will be rendered and displayed.
122
-
123
- ### Enumerable Sections
124
-
125
- Enumerable sections are syntactically identical to boolean sections in
126
- that they begin with a pound and end with a slash. The difference,
127
- however, is in the view: if the method called returns an enumerable,
128
- the section is repeated as the enumerable is iterated over.
129
-
130
- Each item in the enumerable is expected to be a hash which will then
131
- become the context of the corresponding iteration. In this way we can
132
- construct loops.
133
-
134
- For example, imagine this template:
135
-
136
- {{#repo}}
137
- <b>{{name}}</b>
138
- {{/repo}}
139
-
140
- And this view code:
141
-
142
- def repo
143
- Repository.all.map { |r| { :name => r.to_s } }
144
- end
145
-
146
- When rendered, our view will contain a list of all repository names in
147
- the database.
148
-
149
- As a convenience, if a section returns a hash (as opposed to an array
150
- or a boolean) it will be treated as a single item array.
151
-
152
- With the above template, we could use this Ruby code for a single
153
- iteration:
154
-
155
- def repo
156
- { :name => Repository.first.to_s }
157
- end
158
-
159
- This would be treated by Mustache as functionally equivalent to the
160
- following:
161
-
162
- def repo
163
- [ { :name => Repository.first.to_s } ]
164
- end
165
-
166
-
167
- ### Comments
168
-
169
- Comments begin with a bang and are ignored. The following template:
100
+ For a language-agnostic overview of Mustache's template syntax, see
101
+ the `mustache(5)` manpage or
102
+ <http://defunkt.github.com/mustache/mustache.5.html>.
170
103
 
171
- <h1>Today{{! ignore me }}.</h1>
172
104
 
173
- Will render as follows:
174
-
175
- <h1>Today.</h1>
176
-
177
- ### Partials
178
-
179
- Partials begin with a greater than sign, like `{{> box}}`.
180
-
181
- It is useful to think of partials as a "template expansion" - that is,
182
- the actual partial tag will be replaced with the content of the
183
- partial. Therefor partials share the current context.
184
-
185
- For example, this template and partial:
186
-
187
- base.mustache
188
- Names:
189
- {{# names }}
190
- {{> user }}
191
- {{/ names }}
192
-
193
- user.mustache:
194
- <strong>{{ name }}</strong>
195
-
196
- Can be thought of as a single, expanded template:
197
-
198
- Names:
199
- {{# names }}
200
- <strong>{{ name }}</strong>
201
- {{/ names }}
202
-
203
- Have partial-specific code you want to share between view classes?
204
- Consider using a module and including it.
205
-
206
-
207
- ### Set Delimiter
208
-
209
- Set Delimiter tags start with an equal sign and change the tag
210
- delimiters from {{ and }} to custom strings.
211
-
212
- Consider the following contrived example:
213
-
214
- * {{ default_tags }}
215
- {{=<% %>=}}
216
- * <% erb_style_tags %>
217
- <%={{ }}=%>
218
- * {{ default_tags_again }}
219
-
220
- Here we have a list with three items. The first item uses the default
221
- tag style, the second uses erb style as defined by the Set Delimiter
222
- tag, and the third returns to the default style after yet another Set
223
- Delimiter declaration.
105
+ Escaping
106
+ --------
224
107
 
225
- According to [ctemplates][3], this "is useful for languages like TeX, where
226
- double-braces may occur in the text and are awkward to use for
227
- markup."
108
+ Mustache does escape all values when using the standard double
109
+ Mustache syntax. Characters which will be escaped: `& \ " < >`. To
110
+ disable escaping, simply use tripple mustaches like
111
+ `{{{unescaped_variable}}}`.
228
112
 
229
- Custom delimiters may not contain whitespace or the equals sign.
113
+ Example: Using `{{variable}}` inside a template for `5 > 2` will
114
+ result in `5 &gt; 2`, where as the usage of `{{{variable}}}` will
115
+ result in `5 > 2`.
230
116
 
231
117
 
232
118
  Dict-Style Views
@@ -417,69 +303,37 @@ Vim
417
303
  Thanks to [Juvenn Woo](http://github.com/juvenn) for mustache.vim. It
418
304
  is included under the contrib/ directory.
419
305
 
306
+ See <http://gist.github.com/323622> for installation instructions.
420
307
 
421
308
  Emacs
422
309
  -----
423
310
 
424
- tpl-mode.el is included under the contrib/ directory for any Emacs users.
425
- Based on Google's tpl-mode for ctemplates, it adds support for Mustache's
426
- more lenient tag values and includes a few commands for your editing pleasure.
311
+ mustache-mode.el is included under the contrib/ directory for any
312
+ Emacs users. Based on Google's tpl-mode for ctemplates, it adds
313
+ support for Mustache's more lenient tag values and includes a few
314
+ commands for your editing pleasure.
315
+
316
+ See <http://gist.github.com/323619> for installation instructions.
427
317
 
428
318
 
429
319
  TextMate
430
320
  --------
431
321
 
432
- Check out Tekkub's
433
- [Mustache.tmbundle](http://github.com/tekkub/Mustache.tmbundle).
322
+ [Mustache.tmbundle](http://github.com/defunkt/Mustache.tmbundle)
434
323
 
324
+ See <http://gist.github.com/323624> for installation instructions.
435
325
 
436
326
  Command Line
437
327
  ------------
438
328
 
439
- Mustache includes a `mustache` script for rendering templates on the
440
- command line. This can be useful when designing HTML that will
441
- eventually be included in a website: instead of having to format the
442
- HTML as Mustache later, you can do it now!
443
-
444
- The script expects a Mustache template on STDIN with YAML
445
- frontmatter. An example looks like this:
446
-
447
- $ cat complete.mustache
448
- ---
449
- names: [ {name: chris}, {name: mark}, {name: scott} ]
450
- ---
451
- {{#names}}
452
- Hi {{name}}!
453
- {{/names}}
454
-
455
- $ cat complete.mustache | mustache
456
- Hi chris!
457
- Hi mark!
458
- Hi scott!
459
-
460
- It's probably more useful to keep the YAML and HTML in separate files,
461
- though. Luckily `cat` works great for this, too:
462
-
463
- $ cat data.yml
464
- ---
465
- names: [ {name: chris}, {name: mark}, {name: scott} ]
466
- ---
467
-
468
- $ cat template.mustache
469
- {{#names}}
470
- Hi {{name}}!
471
- {{/names}}
472
-
473
- $ cat data.yml template.mustache | mustache
474
- Hi chris!
475
- Hi mark!
476
- Hi scott!
477
-
329
+ See `mustache(1)` man page or
330
+ <http://defunkt.github.com/mustache/mustache.1.html>
331
+ for command line docs.
478
332
 
479
333
  Installation
480
334
  ------------
481
335
 
482
- ### [Gemcutter](http://gemcutter.org/)
336
+ ### [RubyGems](http://rubygems.org/)
483
337
 
484
338
  $ gem install mustache
485
339
 
@@ -487,7 +341,6 @@ Installation
487
341
 
488
342
  $ rip install git://github.com/defunkt/mustache.git
489
343
 
490
-
491
344
  Acknowledgements
492
345
  ----------------
493
346
 
@@ -499,13 +352,11 @@ Meta
499
352
  ----
500
353
 
501
354
  * Code: `git clone git://github.com/defunkt/mustache.git`
502
- * Home: <http://github.com/defunkt/mustache>
503
- * Docs: <http://defunkt.github.com/mustache>
355
+ * Home: <http://defunkt.github.com/mustache>
504
356
  * Bugs: <http://github.com/defunkt/mustache/issues>
505
357
  * List: <http://groups.google.com/group/mustache-rb>
506
358
  * Test: <http://runcoderun.com/defunkt/mustache>
507
- * Gems: <http://gemcutter.org/gems/mustache>
508
- * Boss: Chris Wanstrath :: <http://github.com/defunkt>
359
+ * Gems: <http://rubygems.org/gems/mustache>
509
360
 
510
361
  [1]: http://code.google.com/p/google-ctemplate/
511
362
  [2]: http://www.ivan.fomichev.name/2008/05/erlang-template-engine-prototype.html
data/Rakefile CHANGED
@@ -1,66 +1,107 @@
1
1
  require 'rake/testtask'
2
2
  require 'rake/rdoctask'
3
3
 
4
+ def command?(command)
5
+ system("type #{command} > /dev/null")
6
+ end
7
+
8
+ #
9
+ # Tests
10
+ #
11
+
4
12
  task :default => :test
5
13
 
6
- Rake::TestTask.new do |t|
7
- t.libs << 'lib'
8
- t.pattern = 'test/**/*_test.rb'
9
- t.verbose = false
14
+ if command? :turn
15
+ desc "Run tests"
16
+ task :test do
17
+ exec "turn"
18
+ end
19
+ else
20
+ Rake::TestTask.new do |t|
21
+ t.libs << 'lib'
22
+ t.pattern = 'test/**/*_test.rb'
23
+ t.verbose = false
24
+ end
10
25
  end
11
26
 
12
- desc "Build a gem"
13
- task :gem => [ :gemspec, :build ]
27
+ #
28
+ # Ron
29
+ #
14
30
 
15
- desc "Launch Kicker (like autotest)"
16
- task :kicker do
17
- puts "Kicking... (ctrl+c to cancel)"
18
- exec "kicker -e rake test lib examples"
31
+ if command? :ron
32
+ desc "Show the manual"
33
+ task :man => "man:build" do
34
+ exec "man man/mustache.1"
35
+ end
36
+
37
+ desc "Build the manual"
38
+ task "man:build" do
39
+ sh "ron -br5 --organization=DEFUNKT --manual='Mustache Manual' man/*.ron"
40
+ end
19
41
  end
20
42
 
21
- begin
22
- require 'jeweler'
23
- $LOAD_PATH.unshift 'lib'
24
- require 'mustache/version'
25
- Jeweler::Tasks.new do |gemspec|
26
- gemspec.name = "mustache"
27
- gemspec.summary = "Mustache is a framework-agnostic way to render logic-free views."
28
- gemspec.description = "Mustache is a framework-agnostic way to render logic-free views."
29
- gemspec.email = "chris@ozmm.org"
30
- gemspec.homepage = "http://github.com/defunkt/mustache"
31
- gemspec.authors = ["Chris Wanstrath"]
32
- gemspec.version = Mustache::Version
43
+ if command? :kicker
44
+ desc "Launch Kicker (like autotest)"
45
+ task :kicker do
46
+ puts "Kicking... (ctrl+c to cancel)"
47
+ exec "kicker -e rake test lib examples"
33
48
  end
34
- rescue LoadError
35
- puts "Jeweler not available."
36
- puts "Install it with: gem install jeweler"
37
49
  end
38
50
 
51
+ #
52
+ # Gems
53
+ #
54
+
39
55
  begin
40
- require 'sdoc_helpers'
56
+ require 'mg'
57
+ MG.new("mustache.gemspec")
58
+
59
+ desc "Build a gem."
60
+ task :gem => :package
61
+
62
+ # Ensure tests pass before pushing a gem.
63
+ task :gemcutter => :test
64
+
65
+ desc "Push a new version to Gemcutter and publish docs."
66
+ task :publish => :gemcutter do
67
+ system "git tag v#{Mustache::Version}"
68
+ system "git push origin v#{Mustache::Version}"
69
+ system "git push origin master"
70
+ system "gem push pkg/mustache-#{Mustache::Version}.gem"
71
+ system "git clean -fd"
72
+ exec "rake pages"
73
+ end
41
74
  rescue LoadError
42
- puts "sdoc support not enabled. Please gem install sdoc-helpers."
75
+ warn "mg not available."
76
+ warn "Install it with: gem i mg"
43
77
  end
44
78
 
45
- desc "Push a new version to Gemcutter"
46
- task :publish => [ :test, :gemspec, :build ] do
47
- system "git tag v#{Mustache::Version}"
48
- system "git push origin v#{Mustache::Version}"
49
- system "git push origin master"
50
- system "gem push pkg/mustache-#{Mustache::Version}.gem"
51
- system "git clean -fd"
52
- exec "rake pages"
53
- end
79
+ #
80
+ # Documentation
81
+ #
54
82
 
55
- desc "Install the edge gem"
56
- task :install_edge => [ :dev_version, :gemspec, :build ] do
57
- exec "gem install pkg/mustache-#{Mustache::Version}.gem"
58
- end
83
+ # begin
84
+ # require 'sdoc_helpers'
85
+ # rescue LoadError
86
+ # warn "sdoc support not enabled. Please gem install sdoc-helpers."
87
+ # end
88
+
89
+ desc "Publish to GitHub Pages"
90
+ task :pages => [ "build:man" ] do
91
+ Dir['man/*.html'].each do |f|
92
+ cp f, File.basename(f).sub('.html', '.newhtml')
93
+ end
94
+
95
+ `git commit -am 'generated manual'`
96
+ `git checkout gh-pages`
97
+
98
+ Dir['*.newhtml'].each do |f|
99
+ mv f, f.sub('.newhtml', '.html')
100
+ end
59
101
 
60
- # Sets the current Mustache version to the current dev version
61
- task :dev_version do
62
- $LOAD_PATH.unshift 'lib/mustache'
63
- require 'mustache/version'
64
- version = Mustache::Version + '.' + Time.now.to_i.to_s
65
- Mustache.const_set(:Version, version)
102
+ `git add .`
103
+ `git commit -m updated`
104
+ `git push origin gh-pages`
105
+ `git checkout master`
106
+ puts :done
66
107
  end
data/bin/mustache CHANGED
@@ -3,11 +3,15 @@
3
3
  require 'mustache'
4
4
  require 'yaml'
5
5
 
6
- if STDIN.stat.size > 0
7
- doc = STDIN.read
6
+ if !$stdin.tty?
7
+ doc = $stdin.read
8
8
  if doc =~ /^(\s*---(.*)---\s*)/m
9
- data = YAML.load($2.strip)
10
- puts Mustache.render(doc.sub($1, ''), data)
9
+ yaml = $2.strip
10
+ template = doc.sub($1, '')
11
+
12
+ YAML.each_document(yaml) do |data|
13
+ puts Mustache.render(template, data)
14
+ end
11
15
  else
12
16
  puts doc
13
17
  end
@@ -15,20 +19,7 @@ else
15
19
  puts <<-usage
16
20
  Usage: cat data.yml template.mustache | mustache
17
21
 
18
- Expects a single Mustache template on STDIN complete with YAML
19
- frontmatter.
20
-
21
- Runs template.mustache through Mustache, using the data in data.yml to
22
- replace sections and variables. Useful when developing templates
23
- before hooking them into your website or whatnot.
24
-
25
- The data.yml file should start with --- on a single line and end with
26
- --- on a single line, e.g.
27
-
28
- ---
29
- names: [ {name: chris}, {name: mark}, {name: scott} ]
30
- ---
31
-
32
- The converted document will be printed on STDOUT.
22
+ See mustache(1) or http://defunkt.github.com/mustache/mustache.1.html
23
+ for an overview.
33
24
  usage
34
25
  end
@@ -4,30 +4,73 @@ class Mustache
4
4
  # set to true.
5
5
  #
6
6
  # For example, if your View class does not respond to `music` but
7
- # your template contains a `{{template}}` tag this exception will be raised.
7
+ # your template contains a `{{music}}` tag this exception will be raised.
8
8
  #
9
9
  # By default it is not raised. See Mustache.raise_on_context_miss.
10
10
  class ContextMiss < RuntimeError; end
11
11
 
12
12
  # A Context represents the context which a Mustache template is
13
13
  # executed within. All Mustache tags reference keys in the Context.
14
- class Context < Hash
14
+ class Context
15
+ # Expect to be passed an instance of `Mustache`.
15
16
  def initialize(mustache)
16
17
  @mustache = mustache
17
- super()
18
+ @stack = [@mustache]
18
19
  end
19
20
 
21
+ # Adds a new object to the context's internal stack.
22
+ #
23
+ # Returns the Context.
24
+ def push(new)
25
+ @stack.unshift(new)
26
+ self
27
+ end
28
+ alias_method :update, :push
29
+
30
+ # Removes the most recently added object from the context's
31
+ # internal stack.
32
+ #
33
+ # Returns the Context.
34
+ def pop
35
+ @stack.shift
36
+ self
37
+ end
38
+
39
+ # Can be used to add a value to the context in a hash-like way.
40
+ #
41
+ # context[:name] = "Chris"
42
+ def []=(name, value)
43
+ push(name => value)
44
+ end
45
+
46
+ # Alias for `fetch`.
20
47
  def [](name)
21
- if has_key?(name)
22
- super
23
- elsif has_key?(name.to_s)
24
- super(name.to_s)
25
- elsif @mustache.respond_to?(name)
26
- @mustache.send(name)
27
- elsif @mustache.raise_on_context_miss?
28
- raise ContextMiss.new("Can't find #{name} in #{@mustache.inspect}")
48
+ fetch(name, nil)
49
+ end
50
+
51
+ # Similar to Hash#fetch, finds a value by `name` in the context's
52
+ # stack. You may specify the default return value by passing a
53
+ # second parameter.
54
+ #
55
+ # If no second parameter is passed (or raise_on_context_miss is
56
+ # set to true), will raise a ContextMiss exception on miss.
57
+ def fetch(name, default = :__raise)
58
+ @stack.each do |frame|
59
+ hash = frame.respond_to?(:has_key?)
60
+
61
+ if hash && frame.has_key?(name)
62
+ return frame[name]
63
+ elsif hash && frame.has_key?(name.to_s)
64
+ return frame[name.to_s]
65
+ elsif !hash && frame.respond_to?(name)
66
+ return frame.__send__(name)
67
+ end
68
+ end
69
+
70
+ if default == :__raise || @mustache.raise_on_context_miss?
71
+ raise ContextMiss.new("Can't find #{name} in #{@stack.inspect}")
29
72
  else
30
- nil
73
+ default
31
74
  end
32
75
  end
33
76
  end
@@ -92,7 +92,7 @@ class Mustache
92
92
 
93
93
  def self.registered(app)
94
94
  app.helpers Mustache::Sinatra::Helpers
95
- app.set :mustaches, ::Sinatra::Base.views
95
+ app.set :mustaches, app.views
96
96
  app.set :namespace, app
97
97
  end
98
98
  end
@@ -76,20 +76,8 @@ class Mustache
76
76
  ctxtmp = "ctx#{tmpid}"
77
77
  res << ev(<<-compiled)
78
78
  if v = ctx[#{name}]
79
- v = [v] if v.is_a?(Hash) # shortcut when passed a single hash
80
- if v.respond_to?(:each)
81
- #{ctxtmp} = ctx.dup
82
- begin
83
- r = v.map { |h| ctx.update(h); #{code} }.join
84
- rescue TypeError => e
85
- raise TypeError,
86
- "All elements in {{#{name.to_s[1..-1]}}} are not hashes!"
87
- end
88
- ctx.replace(#{ctxtmp})
89
- r
90
- else
91
- #{code}
92
- end
79
+ v = [v] unless v.is_a?(Array) # shortcut when passed non-array
80
+ v.map { |h| ctx.push(h); c = #{code}; ctx.pop; c }.join
93
81
  end
94
82
  compiled
95
83
  # $' = The string to the right of the last successful match
@@ -106,7 +94,7 @@ class Mustache
106
94
  # 4. Partial tags - {{> partial_name }}
107
95
  def compile_tags(src)
108
96
  res = ""
109
- while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/
97
+ while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/m
110
98
  res << str($`)
111
99
  case $1
112
100
  when '#'
@@ -1,3 +1,3 @@
1
1
  class Mustache
2
- Version = '0.5.0'
2
+ Version = '0.6.0'
3
3
  end