Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 303 lines (201 sloc) 7.056 kb
f0857e5 @defunkt rtemplate => mustache
defunkt authored
1 Mustache
6ee6bcf @defunkt first draft
defunkt authored
2 =========
3
4 Inspired by [ctemplate](http://code.google.com/p/google-ctemplate/)
5 and
6 [et](http://www.ivan.fomichev.name/2008/05/erlang-template-engine-prototype.html),
3b9f134 @defunkt typo
defunkt authored
7 Mustache is a framework-agnostic way to render logic-free views.
6ee6bcf @defunkt first draft
defunkt authored
8
f753a53 @defunkt todo
defunkt authored
9 It's not a markup language because there is no language. There is no
3aad2f3 @defunkt docs
defunkt authored
10 logic.
11
0b217cf @defunkt talk about tags first. they're important.
defunkt authored
12
3aad2f3 @defunkt docs
defunkt authored
13 Overview
14 --------
15
f0857e5 @defunkt rtemplate => mustache
defunkt authored
16 Think of Mustache as a replacement for your views. Instead of views
3aad2f3 @defunkt docs
defunkt authored
17 consisting of ERB or HAML with random helpers and arbitrary logic,
18 your views are broken into two parts: a Ruby class and an HTML
19 template.
20
21 We call the Ruby class the "view" and the HTML template the
22 "template."
23
24 All your logic, decisions, and code is contained in your view. All
25 your markup is contained in your template. The template does nothing
26 but reference methods in your view.
27
28 This strict separation makes it easier to write clean templates,
29 easier to test your views, and more fun to work on your app's front end.
30
0b217cf @defunkt talk about tags first. they're important.
defunkt authored
31
1d6dfee @defunkt why?
defunkt authored
32 Why?
33 ----
34
35 I like writing Ruby. I like writing HTML. I like writing JavaScript.
36
37 I don't like writing ERB, Haml, Liquid, Django Templates, putting Ruby
38 in my HTML, or putting JavaScript in my HTML.
39
40
3aad2f3 @defunkt docs
defunkt authored
41 Usage
42 -----
43
44 We've got an `examples` folder but here's the canonical one:
45
f0857e5 @defunkt rtemplate => mustache
defunkt authored
46 class Simple < Mustache
3aad2f3 @defunkt docs
defunkt authored
47 def name
48 "Chris"
49 end
50
51 def value
52 10_000
53 end
54
55 def taxed_value
56 value - (value * 0.4)
57 end
58
59 def in_ca
60 true
61 end
62 end
63
64 We simply create a normal Ruby class and define methods. Some methods
65 reference others, some return values, some return only booleans.
66
67 Now let's write the template:
68
69 Hello {{name}}
70 You have just won ${{value}}!
71 {{#in_ca}}
72 Well, ${{taxed_value}}, after taxes.
73 {{/in_ca}}
74
75 This template references our view methods. To bring it all together,
76 here's the code to render actual HTML;
77
78 Simple.new.to_html
79
80 Which returns the following:
81
82 Hello Chris
83 You have just won $10000!
84 Well, $6000.0, after taxes.
85
86 Simple.
87
0b217cf @defunkt talk about tags first. they're important.
defunkt authored
88
89 Tag Types
90 ---------
91
92 Tags are indicated by the double mustaches. `{{name}}` is a tag. Let's
93 talk about the different types of tags.
94
95 ### Variables
96
97 The most basic tag is the variable. A `{{name}}` tag in a basic
98 template will try to call the `name` method on your view. If there is
99 no `name` method, an exception will be raised.
100
101 All variables are HTML escaped by default. If you want, for some
102 reason, to return unescaped HTML you can use the triple mustache:
103 `{{{name}}}`.
104
105 ### Boolean Sections
106
107 A section begins with a pound and ends with a slash. That is,
108 `{{#person}}` begins a "person" section while `{{/person}}` ends it.
109
110 If the `person` method exists and calling it returns false, the HTML
111 between the pound and slash will not be displayed.
112
113 If the `person` method exists and calling it returns true, the HTML
114 between the pound and slash will be rendered and displayed.
115
116 ### Enumerable Sections
117
118 Enumerable sections are syntactically identical to boolean sections in
119 that they begin with a pound and end with a slash. The difference,
120 however, is in the view: if the method called returns an enumerable,
121 the section is repeated as the enumerable is iterated over.
122
123 Each item in the enumerable is expected to be a hash which will then
124 become the context of the corresponding iteration. In this way we can
125 construct loops.
126
127 For example, imagine this template:
128
129 {{#repo}}
130 <b>{{name}}</b>
131 {{/repo}}
132
133 And this view code:
134
135 def repo
136 Repository.all.map { |r| { :name => r.to_s } }
137 end
138
139 When rendered, our view will contain a list of all repository names in
140 the database.
141
142 ### Comments
143
144 Comments begin with a bang and are ignored. The following template:
145
146 <h1>Today{{! ignore me }}.</h1>
147
148 Will render as follows:
149
150 <h1>Today.</h1>
151
152 ### Partials
153
154 Partials begin with a less than sign, like `{{< box}}`.
155
156 If a partial's view is loaded, we use that to render the HTML. If
157 nothing is loaded we render the template directly using our current context.
158
159 In this way partials can reference variables or sections the calling
160 view defines.
161
162
04e852b @defunkt explain the dict style
defunkt authored
163 Dict-Style Views
164 ----------------
165
166 ctemplate and friends want you to hand a dictionary to the template
f0857e5 @defunkt rtemplate => mustache
defunkt authored
167 processor. Naturally Mustache supports a similar concept. Feel free
04e852b @defunkt explain the dict style
defunkt authored
168 to mix the class-based and this more procedural style at your leisure.
169
170 Given this template (dict.html):
171
172 Hello {{name}}
173 You have just won ${{value}}!
174
175 We can fill in the values at will:
176
177 dict = Dict.new
178 dict[:name] = 'George'
179 dict[:value] = 100
180 dict.to_html
181
182 Which returns:
183
184 Hello George
185 You have just won $100!
186
187 We can re-use the same object, too:
188
189 dict[:name] = 'Tony'
190 dict.to_html
191 Hello Tony
192 You have just won $100!
193
0b217cf @defunkt talk about tags first. they're important.
defunkt authored
194
671f6aa @defunkt document template options and make template_file configurable
defunkt authored
195 Templates
196 ---------
197
198 A word on templates. By default, a view will try to find its template
199 on disk by searching for an HTML file in the current directory that
200 follows the classic Ruby naming convention.
201
202 TemplatePartial => ./template_partial.html
203
f0857e5 @defunkt rtemplate => mustache
defunkt authored
204 You can set the search path using `Mustache.path`. It can be set on a
671f6aa @defunkt document template options and make template_file configurable
defunkt authored
205 class by class basis:
206
f0857e5 @defunkt rtemplate => mustache
defunkt authored
207 class Simple < Mustache
671f6aa @defunkt document template options and make template_file configurable
defunkt authored
208 self.path = File.dirname(__FILE__)
209 ... etc ...
210 end
211
212 Now `Simple` will look for `simple.html` in the directory it resides
213 in, no matter the cwd.
214
215 If you want to just change what template is used you can set
f0857e5 @defunkt rtemplate => mustache
defunkt authored
216 `Mustache#template_file` directly:
671f6aa @defunkt document template options and make template_file configurable
defunkt authored
217
218 Simple.new.template_file = './blah.html'
219
220 You can also go ahead and set the template directly:
221
222 Simple.new.template = 'Hi {{person}}!'
223
224 Whatever works.
225
0b217cf @defunkt talk about tags first. they're important.
defunkt authored
226
70af848 @defunkt helpers?!
defunkt authored
227 Helpers
228 -------
229
230 What about global helpers? Maybe you have a nifty `gravatar` function
231 you want to use in all your views? No problem.
232
233 This is just Ruby, after all.
234
235 module ViewHelpers
236 def gravatar(email, size = 30)
237 gravatar_id = Digest::MD5.hexdigest(email.to_s.strip.downcase)
238 gravatar_for_id(gravatar_id, size)
239 end
240
241 def gravatar_for_id(gid, size = 30)
242 "#{gravatar_host}/avatar/#{gid}?s=#{size}"
243 end
244
245 def gravatar_host
246 @ssl ? 'https://secure.gravatar.com' : 'http://www.gravatar.com'
247 end
248 end
249
250 Then just include it:
251
f0857e5 @defunkt rtemplate => mustache
defunkt authored
252 class Simple < Mustache
70af848 @defunkt helpers?!
defunkt authored
253 include ViewHelpers
254
255 def name
256 "Chris"
257 end
258
259 def value
260 10_000
261 end
262
263 def taxed_value
264 value - (value * 0.4)
265 end
266
267 def in_ca
268 true
269 end
270 end
271
272 Great, but what about that `@ssl` ivar in `gravatar_host`? There are
273 many ways we can go about setting it.
274
f0857e5 @defunkt rtemplate => mustache
defunkt authored
275 Here's on example which illustrates a key feature of Mustache: you
70af848 @defunkt helpers?!
defunkt authored
276 are free to use the `initialize` method just as you would in any
277 normal class.
278
f0857e5 @defunkt rtemplate => mustache
defunkt authored
279 class Simple < Mustache
70af848 @defunkt helpers?!
defunkt authored
280 include ViewHelpers
281
282 def initialize(ssl = false)
283 @ssl = ssl
284 end
285
286 ... etc ...
287 end
288
289 Now:
290
291 Simple.new(request.ssl?).to_html
292
293 Convoluted but you get the idea.
294
295
190b84d @defunkt project meta info in the readme (issue tracker, mailing list, etc)
defunkt authored
296 Meta
297 ----
141fa9a @defunkt partials docs
defunkt authored
298
190b84d @defunkt project meta info in the readme (issue tracker, mailing list, etc)
defunkt authored
299 * Code: `git clone git://github.com/defunkt/mustache.git`
300 * Bugs: <http://github.com/defunkt/mustache/issues>
301 * List: <http://groups.google.com/group/mustache-rb>
302 * Boss: Chris Wanstrath :: <http://github.com/defunkt>
Something went wrong with that request. Please try again.