mustache 0.1.0 → 0.1.1
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/.gitignore +1 -1
- data/README.md +39 -10
- data/Rakefile +5 -8
- data/benchmarks/speed.rb +7 -4
- data/examples/passenger.conf +5 -0
- data/examples/passenger.rb +29 -0
- data/lib/mustache.rb +26 -15
- data/lib/mustache/sinatra.rb +28 -23
- data/lib/mustache/version.rb +3 -0
- data/test/mustache_test.rb +36 -1
- metadata +6 -2
data/.gitignore
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
docs
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ We call the Ruby class the "view" and the HTML template the
|
|
23
23
|
|
24
24
|
All your logic, decisions, and code is contained in your view. All
|
25
25
|
your markup is contained in your template. The template does nothing
|
26
|
-
but reference methods in your view.
|
26
|
+
but reference methods in your view.
|
27
27
|
|
28
28
|
This strict separation makes it easier to write clean templates,
|
29
29
|
easier to test your views, and more fun to work on your app's front end.
|
@@ -41,6 +41,13 @@ in my HTML, or putting JavaScript in my HTML.
|
|
41
41
|
Usage
|
42
42
|
-----
|
43
43
|
|
44
|
+
Quick example:
|
45
|
+
|
46
|
+
>> require 'mustache'
|
47
|
+
=> true
|
48
|
+
>> Mustache.render("Hello {{planet}}", :planet => "World!")
|
49
|
+
=> "Hello World!"
|
50
|
+
|
44
51
|
We've got an `examples` folder but here's the canonical one:
|
45
52
|
|
46
53
|
class Simple < Mustache
|
@@ -100,7 +107,7 @@ no `name` method, an exception will be raised.
|
|
100
107
|
|
101
108
|
All variables are HTML escaped by default. If you want, for some
|
102
109
|
reason, to return unescaped HTML you can use the triple mustache:
|
103
|
-
`{{{name}}}`.
|
110
|
+
`{{{name}}}`.
|
104
111
|
|
105
112
|
### Boolean Sections
|
106
113
|
|
@@ -118,7 +125,7 @@ between the pound and slash will be rendered and displayed.
|
|
118
125
|
Enumerable sections are syntactically identical to boolean sections in
|
119
126
|
that they begin with a pound and end with a slash. The difference,
|
120
127
|
however, is in the view: if the method called returns an enumerable,
|
121
|
-
the section is repeated as the enumerable is iterated over.
|
128
|
+
the section is repeated as the enumerable is iterated over.
|
122
129
|
|
123
130
|
Each item in the enumerable is expected to be a hash which will then
|
124
131
|
become the context of the corresponding iteration. In this way we can
|
@@ -144,7 +151,7 @@ the database.
|
|
144
151
|
Comments begin with a bang and are ignored. The following template:
|
145
152
|
|
146
153
|
<h1>Today{{! ignore me }}.</h1>
|
147
|
-
|
154
|
+
|
148
155
|
Will render as follows:
|
149
156
|
|
150
157
|
<h1>Today.</h1>
|
@@ -173,14 +180,14 @@ Given this template (dict.html):
|
|
173
180
|
You have just won ${{value}}!
|
174
181
|
|
175
182
|
We can fill in the values at will:
|
176
|
-
|
183
|
+
|
177
184
|
dict = Dict.new
|
178
185
|
dict[:name] = 'George'
|
179
186
|
dict[:value] = 100
|
180
187
|
dict.to_html
|
181
188
|
|
182
189
|
Which returns:
|
183
|
-
|
190
|
+
|
184
191
|
Hello George
|
185
192
|
You have just won $100!
|
186
193
|
|
@@ -200,7 +207,7 @@ on disk by searching for an HTML file in the current directory that
|
|
200
207
|
follows the classic Ruby naming convention.
|
201
208
|
|
202
209
|
TemplatePartial => ./template_partial.html
|
203
|
-
|
210
|
+
|
204
211
|
You can set the search path using `Mustache.path`. It can be set on a
|
205
212
|
class by class basis:
|
206
213
|
|
@@ -216,7 +223,7 @@ If you want to just change what template is used you can set
|
|
216
223
|
`Mustache.template_file` directly:
|
217
224
|
|
218
225
|
Simple.template_file = './blah.html'
|
219
|
-
|
226
|
+
|
220
227
|
You can also go ahead and set the template directly:
|
221
228
|
|
222
229
|
Simple.template = 'Hi {{person}}!'
|
@@ -232,7 +239,7 @@ Helpers
|
|
232
239
|
-------
|
233
240
|
|
234
241
|
What about global helpers? Maybe you have a nifty `gravatar` function
|
235
|
-
you want to use in all your views? No problem.
|
242
|
+
you want to use in all your views? No problem.
|
236
243
|
|
237
244
|
This is just Ruby, after all.
|
238
245
|
|
@@ -251,7 +258,7 @@ This is just Ruby, after all.
|
|
251
258
|
end
|
252
259
|
end
|
253
260
|
|
254
|
-
Then just include it:
|
261
|
+
Then just include it:
|
255
262
|
|
256
263
|
class Simple < Mustache
|
257
264
|
include ViewHelpers
|
@@ -297,11 +304,33 @@ Now:
|
|
297
304
|
Convoluted but you get the idea.
|
298
305
|
|
299
306
|
|
307
|
+
Installation
|
308
|
+
------------
|
309
|
+
|
310
|
+
### [Gemcutter](http://gemcutter.org/)
|
311
|
+
|
312
|
+
$ gem install mustache
|
313
|
+
|
314
|
+
### [Rip](http://hellorip.com)
|
315
|
+
|
316
|
+
$ rip install git://github.com/defunkt/mustache.git
|
317
|
+
|
318
|
+
|
319
|
+
Acknowledgements
|
320
|
+
----------------
|
321
|
+
|
322
|
+
Thanks to [Tom Preston-Werner](http://github.com/mojombo) for showing
|
323
|
+
me ctemplate and [Leah Culver](http://github.com/leah) for the name "Mustache."
|
324
|
+
|
325
|
+
|
300
326
|
Meta
|
301
327
|
----
|
302
328
|
|
303
329
|
* Code: `git clone git://github.com/defunkt/mustache.git`
|
330
|
+
* Home: <http://github.com/defunkt/mustache>
|
331
|
+
* Docs: <http://defunkt.github.com/mustache>
|
304
332
|
* Bugs: <http://github.com/defunkt/mustache/issues>
|
305
333
|
* List: <http://groups.google.com/group/mustache-rb>
|
306
334
|
* Test: <http://runcoderun.com/defunkt/mustache>
|
335
|
+
* Gems: <http://gemcutter.org/gems/mustache>
|
307
336
|
* Boss: Chris Wanstrath :: <http://github.com/defunkt>
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rake/testtask'
|
2
|
+
require 'rake/rdoctask'
|
2
3
|
|
3
4
|
task :default => :test
|
4
5
|
|
@@ -25,12 +26,8 @@ rescue LoadError
|
|
25
26
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
f.puts Markdown.new(File.read('README.md')).to_html
|
33
|
-
end
|
34
|
-
|
35
|
-
exec "sdoc -N --main=README.html README.html LICENSE lib"
|
29
|
+
begin
|
30
|
+
require 'sdoc_helpers'
|
31
|
+
rescue LoadError
|
32
|
+
puts "sdoc support not enabled. Please gem install sdoc-helpers."
|
36
33
|
end
|
data/benchmarks/speed.rb
CHANGED
@@ -10,14 +10,14 @@ require 'complex_view'
|
|
10
10
|
template = File.read(File.dirname(__FILE__) + '/complex.erb')
|
11
11
|
|
12
12
|
unless ENV['NOERB']
|
13
|
-
bench 'ERB w/o caching' do
|
14
|
-
ERB.new(template).result(ComplexView.new.send(:binding))
|
15
|
-
end
|
16
|
-
|
17
13
|
erb = ERB.new(template)
|
18
14
|
bench 'ERB w/ caching' do
|
19
15
|
erb.result(ComplexView.new.send(:binding))
|
20
16
|
end
|
17
|
+
|
18
|
+
bench 'ERB w/o caching' do
|
19
|
+
ERB.new(template).result(ComplexView.new.send(:binding))
|
20
|
+
end
|
21
21
|
end
|
22
22
|
|
23
23
|
## mustache
|
@@ -39,8 +39,11 @@ bench '{ w/ caching' do
|
|
39
39
|
tpl.to_html
|
40
40
|
end
|
41
41
|
|
42
|
+
content = File.read(ComplexView.template_file)
|
43
|
+
|
42
44
|
bench '{ w/o caching' do
|
43
45
|
tpl = ComplexView.new
|
46
|
+
tpl.template = content
|
44
47
|
tpl[:item] = items
|
45
48
|
tpl.to_html
|
46
49
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'mustache'
|
3
|
+
|
4
|
+
class Passenger < Mustache
|
5
|
+
|
6
|
+
self.path = File.dirname(__FILE__)
|
7
|
+
self.template_extension = 'conf'
|
8
|
+
|
9
|
+
def server
|
10
|
+
"example.com"
|
11
|
+
end
|
12
|
+
|
13
|
+
def deploy_to
|
14
|
+
"/var/www/example.com"
|
15
|
+
end
|
16
|
+
|
17
|
+
def stage
|
18
|
+
"production"
|
19
|
+
end
|
20
|
+
|
21
|
+
def timestamp
|
22
|
+
Time.now.strftime('%Y%m%d%H%M%S')
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
if $0 == __FILE__
|
28
|
+
puts Passenger.to_text
|
29
|
+
end
|
data/lib/mustache.rb
CHANGED
@@ -22,7 +22,8 @@ class Mustache
|
|
22
22
|
"\"#{compile_sections(src)}\""
|
23
23
|
end
|
24
24
|
|
25
|
-
private
|
25
|
+
## private
|
26
|
+
|
26
27
|
# {{#sections}}okay{{/sections}}
|
27
28
|
#
|
28
29
|
# Sections can return true, false, or an enumerable.
|
@@ -73,7 +74,7 @@ class Mustache
|
|
73
74
|
def compile_partial(name)
|
74
75
|
klass = Mustache.classify(name)
|
75
76
|
if Object.const_defined?(klass)
|
76
|
-
ev("#{klass}.
|
77
|
+
ev("#{klass}.render")
|
77
78
|
else
|
78
79
|
src = File.read(@template_path + '/' + name + '.html')
|
79
80
|
compile(src)[1..-2]
|
@@ -120,8 +121,13 @@ class Mustache
|
|
120
121
|
end
|
121
122
|
|
122
123
|
# Helper method for quickly instantiating and rendering a view.
|
123
|
-
def self.
|
124
|
-
new.
|
124
|
+
def self.render(*args)
|
125
|
+
new.render(*args)
|
126
|
+
end
|
127
|
+
|
128
|
+
class << self
|
129
|
+
alias_method :to_html, :render
|
130
|
+
alias_method :to_text, :render
|
125
131
|
end
|
126
132
|
|
127
133
|
# The path informs your Mustache subclass where to look for its
|
@@ -134,11 +140,11 @@ class Mustache
|
|
134
140
|
@path || '.'
|
135
141
|
end
|
136
142
|
|
137
|
-
# Templates are self.class.name.underscore +
|
143
|
+
# Templates are self.class.name.underscore + extension -- a class of
|
138
144
|
# Dashboard would have a template (relative to the path) of
|
139
145
|
# dashboard.html
|
140
146
|
def self.template_file
|
141
|
-
@template_file ||= path + '/' + underscore(to_s) + '.
|
147
|
+
@template_file ||= path + '/' + underscore(to_s) + '.' + template_extension
|
142
148
|
end
|
143
149
|
|
144
150
|
def self.template_file=(template_file)
|
@@ -149,6 +155,15 @@ class Mustache
|
|
149
155
|
@template ||= templateify(File.read(template_file))
|
150
156
|
end
|
151
157
|
|
158
|
+
# Default extension is 'html'
|
159
|
+
def self.template_extension
|
160
|
+
@template_extension ||= 'html'
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.template_extension=(template_extension)
|
164
|
+
@template_extension = template_extension
|
165
|
+
end
|
166
|
+
|
152
167
|
# template_partial => TemplatePartial
|
153
168
|
def self.classify(underscored)
|
154
169
|
underscored.split(/[-_]/).map { |part| part[0] = part[0].chr.upcase; part }.join
|
@@ -204,15 +219,11 @@ class Mustache
|
|
204
219
|
context[key.to_sym] = value
|
205
220
|
end
|
206
221
|
|
207
|
-
#
|
208
|
-
def to_html
|
209
|
-
render template
|
210
|
-
end
|
211
|
-
|
212
|
-
# Parses our fancy pants template HTML and returns normal HTML with
|
222
|
+
# Parses our fancy pants template file and returns normal file with
|
213
223
|
# all special {{tags}} and {{#sections}}replaced{{/sections}}.
|
214
|
-
def render(
|
215
|
-
|
216
|
-
html.render(context.update(ctx))
|
224
|
+
def render(data = template, ctx = {})
|
225
|
+
self.class.templateify(data).render(context.update(ctx))
|
217
226
|
end
|
227
|
+
alias_method :to_html, :render
|
228
|
+
alias_method :to_text, :render
|
218
229
|
end
|
data/lib/mustache/sinatra.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
|
2
|
-
Support for Mustache in your Sinatra app.
|
3
|
-
|
4
|
-
require 'mustache/sinatra'
|
5
|
-
|
6
|
-
class App < Sinatra::Base
|
7
|
-
helpers Mustache::Sinatra
|
8
|
-
|
9
|
-
get '/stats' do
|
10
|
-
mustache :stats
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
If a `Views::Stats` class exists in the above example,
|
15
|
-
Mustache will try to instantiate and use it for the rendering.
|
16
|
-
|
17
|
-
If no `Views::Stats` class exists Mustache will render the template
|
18
|
-
file directly.
|
19
|
-
|
20
|
-
You can indeed use layouts with this library. Where you'd normally
|
21
|
-
<%= yield %> you instead {{yield}} - the body of the subview is
|
22
|
-
set to the `yield` variable and made available to you.
|
23
|
-
=end
|
1
|
+
require 'sinatra/base'
|
24
2
|
require 'mustache'
|
25
3
|
|
26
4
|
class Mustache
|
5
|
+
# Support for Mustache in your Sinatra app.
|
6
|
+
#
|
7
|
+
# require 'mustache/sinatra'
|
8
|
+
#
|
9
|
+
# class App < Sinatra::Base
|
10
|
+
# helpers Mustache::Sinatra
|
11
|
+
#
|
12
|
+
# get '/stats' do
|
13
|
+
# mustache :stats
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# If a `Views::Stats` class exists in the above example,
|
18
|
+
# Mustache will try to instantiate and use it for the rendering.
|
19
|
+
#
|
20
|
+
# If no `Views::Stats` class exists Mustache will render the template
|
21
|
+
# file directly.
|
22
|
+
#
|
23
|
+
# You can indeed use layouts with this library. Where you'd normally
|
24
|
+
# <%= yield %> you instead {{{yield}}} - the body of the subview is
|
25
|
+
# set to the `yield` variable and made available to you.
|
27
26
|
module Sinatra
|
28
27
|
# Call this in your Sinatra routes.
|
29
28
|
def mustache(template, options={}, locals={})
|
@@ -41,6 +40,10 @@ class Mustache
|
|
41
40
|
instance = Mustache.new
|
42
41
|
end
|
43
42
|
|
43
|
+
instance_variable_names.each do |name|
|
44
|
+
instance.instance_variable_set(name, instance_variable_get(name))
|
45
|
+
end
|
46
|
+
|
44
47
|
locals.each do |local, value|
|
45
48
|
instance[local] = value
|
46
49
|
end
|
@@ -54,3 +57,5 @@ class Mustache
|
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|
60
|
+
|
61
|
+
Sinatra.helpers Mustache::Sinatra
|
data/test/mustache_test.rb
CHANGED
@@ -8,8 +8,20 @@ require 'template_partial'
|
|
8
8
|
require 'escaped'
|
9
9
|
require 'unescaped'
|
10
10
|
require 'comments'
|
11
|
+
require 'passenger'
|
11
12
|
|
12
13
|
class MustacheTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
def test_passenger
|
16
|
+
assert_equal <<-end_passenger, Passenger.to_text
|
17
|
+
<VirtualHost *>
|
18
|
+
ServerName example.com
|
19
|
+
DocumentRoot /var/www/example.com
|
20
|
+
RailsEnv production
|
21
|
+
</VirtualHost>
|
22
|
+
end_passenger
|
23
|
+
end
|
24
|
+
|
13
25
|
def test_complex_view
|
14
26
|
assert_equal <<-end_complex, ComplexView.to_html
|
15
27
|
<h1>Colors</h1>
|
@@ -108,4 +120,27 @@ end_partial
|
|
108
120
|
def test_namespaced_underscore
|
109
121
|
assert_equal 'stat_stuff', Mustache.underscore('Views::StatStuff')
|
110
122
|
end
|
111
|
-
|
123
|
+
|
124
|
+
def test_render
|
125
|
+
assert_equal 'Hello World!', Mustache.render('Hello World!')
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_render_with_params
|
129
|
+
assert_equal 'Hello World!', Mustache.render('Hello {{planet}}!', :planet => 'World')
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_render_from_file
|
133
|
+
expected = <<-data
|
134
|
+
<VirtualHost *>
|
135
|
+
ServerName example.com
|
136
|
+
DocumentRoot /var/www/example.com
|
137
|
+
RailsEnv production
|
138
|
+
</VirtualHost>
|
139
|
+
data
|
140
|
+
template = File.read("examples/passenger.conf")
|
141
|
+
assert_equal expected, Mustache.render(template, :stage => 'production',
|
142
|
+
:server => 'example.com',
|
143
|
+
:deploy_to => '/var/www/example.com' )
|
144
|
+
end
|
145
|
+
|
146
|
+
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.1.
|
4
|
+
version: 0.1.1
|
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-07 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -38,6 +38,8 @@ files:
|
|
38
38
|
- examples/escaped.html
|
39
39
|
- examples/escaped.rb
|
40
40
|
- examples/inner_partial.html
|
41
|
+
- examples/passenger.conf
|
42
|
+
- examples/passenger.rb
|
41
43
|
- examples/simple.html
|
42
44
|
- examples/simple.rb
|
43
45
|
- examples/template_partial.html
|
@@ -48,6 +50,7 @@ files:
|
|
48
50
|
- examples/view_partial.rb
|
49
51
|
- lib/mustache.rb
|
50
52
|
- lib/mustache/sinatra.rb
|
53
|
+
- lib/mustache/version.rb
|
51
54
|
- test/mustache_test.rb
|
52
55
|
has_rdoc: true
|
53
56
|
homepage: http://github.com/defunkt/mustache
|
@@ -82,6 +85,7 @@ test_files:
|
|
82
85
|
- examples/comments.rb
|
83
86
|
- examples/complex_view.rb
|
84
87
|
- examples/escaped.rb
|
88
|
+
- examples/passenger.rb
|
85
89
|
- examples/simple.rb
|
86
90
|
- examples/template_partial.rb
|
87
91
|
- examples/unescaped.rb
|