mustache 0.4.0 → 0.5.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/CONTRIBUTORS +1 -0
- data/HISTORY.md +18 -0
- data/README.md +83 -6
- data/Rakefile +2 -1
- data/bin/mustache +34 -0
- data/contrib/tpl-mode.el +274 -0
- data/examples/namespaced.rb +9 -1
- data/examples/partial_with_module.mustache +3 -0
- data/examples/partial_with_module.rb +37 -0
- data/examples/template_partial.mustache +1 -1
- data/examples/template_partial.txt +1 -1
- data/lib/mustache/context.rb +1 -1
- data/lib/mustache/template.rb +5 -10
- data/lib/mustache/version.rb +1 -1
- data/lib/mustache.rb +2 -0
- data/lib/rack/bug/panels/mustache_panel/mustache_extension.rb +2 -0
- data/lib/rack/bug/panels/mustache_panel/view.mustache +16 -2
- data/test/autoloading_test.rb +15 -1
- data/test/helper.rb +13 -0
- data/test/mustache_test.rb +4 -46
- data/test/partial_test.rb +80 -0
- metadata +14 -8
- data/examples/view_partial.mustache +0 -3
- data/examples/view_partial.rb +0 -18
data/CONTRIBUTORS
CHANGED
data/HISTORY.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 0.5.0 (2009-11-23)
|
|
2
|
+
|
|
3
|
+
* Partial classes are no longer supported. Use modules!
|
|
4
|
+
* Added `mustache` script for rendering templates on the command line.
|
|
5
|
+
* ctemplate compat: Partials are indicated by >, not <
|
|
6
|
+
* Bugfix: Context miss should return nil, not empty string. Fixes 1.9.x
|
|
7
|
+
|
|
8
|
+
## 0.4.2 (2009-10-28)
|
|
9
|
+
|
|
10
|
+
* Bugfix: Ignore bad constant names when autoloading
|
|
11
|
+
|
|
12
|
+
## 0.4.1 (2009-10-27)
|
|
13
|
+
|
|
14
|
+
* Partials now respect the `view_namespace` setting.
|
|
15
|
+
* Added tpl-mode.el to contrib/ for us Emacs users.
|
|
16
|
+
* Rack::Bug bugfix: ensure benchmark is required before using it
|
|
17
|
+
* Rack::Bug: truncate too-large variables (click expands them)
|
|
18
|
+
|
|
1
19
|
## 0.4.0 (2009-10-27)
|
|
2
20
|
|
|
3
21
|
* Stopped raising context miss exceptions by default
|
data/README.md
CHANGED
|
@@ -176,13 +176,32 @@ Will render as follows:
|
|
|
176
176
|
|
|
177
177
|
### Partials
|
|
178
178
|
|
|
179
|
-
Partials begin with a
|
|
179
|
+
Partials begin with a greater than sign, like `{{> box}}`.
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
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.
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
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.
|
|
186
205
|
|
|
187
206
|
|
|
188
207
|
### Set Delimiter
|
|
@@ -376,7 +395,7 @@ An example Sinatra application is also provided:
|
|
|
376
395
|
|
|
377
396
|
|
|
378
397
|
[Rack::Bug][4]
|
|
379
|
-
|
|
398
|
+
--------------
|
|
380
399
|
|
|
381
400
|
Mustache also ships with a `Rack::Bug` panel. In your `config.ru` add
|
|
382
401
|
the following code:
|
|
@@ -399,6 +418,64 @@ Thanks to [Juvenn Woo](http://github.com/juvenn) for mustache.vim. It
|
|
|
399
418
|
is included under the contrib/ directory.
|
|
400
419
|
|
|
401
420
|
|
|
421
|
+
Emacs
|
|
422
|
+
-----
|
|
423
|
+
|
|
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.
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
TextMate
|
|
430
|
+
--------
|
|
431
|
+
|
|
432
|
+
Check out Tekkub's
|
|
433
|
+
[Mustache.tmbundle](http://github.com/tekkub/Mustache.tmbundle).
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
Command Line
|
|
437
|
+
------------
|
|
438
|
+
|
|
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
|
+
|
|
478
|
+
|
|
402
479
|
Installation
|
|
403
480
|
------------
|
|
404
481
|
|
data/Rakefile
CHANGED
|
@@ -15,7 +15,7 @@ task :gem => [ :gemspec, :build ]
|
|
|
15
15
|
desc "Launch Kicker (like autotest)"
|
|
16
16
|
task :kicker do
|
|
17
17
|
puts "Kicking... (ctrl+c to cancel)"
|
|
18
|
-
exec "kicker -e rake test lib"
|
|
18
|
+
exec "kicker -e rake test lib examples"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
begin
|
|
@@ -46,6 +46,7 @@ desc "Push a new version to Gemcutter"
|
|
|
46
46
|
task :publish => [ :test, :gemspec, :build ] do
|
|
47
47
|
system "git tag v#{Mustache::Version}"
|
|
48
48
|
system "git push origin v#{Mustache::Version}"
|
|
49
|
+
system "git push origin master"
|
|
49
50
|
system "gem push pkg/mustache-#{Mustache::Version}.gem"
|
|
50
51
|
system "git clean -fd"
|
|
51
52
|
exec "rake pages"
|
data/bin/mustache
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'mustache'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
if STDIN.stat.size > 0
|
|
7
|
+
doc = STDIN.read
|
|
8
|
+
if doc =~ /^(\s*---(.*)---\s*)/m
|
|
9
|
+
data = YAML.load($2.strip)
|
|
10
|
+
puts Mustache.render(doc.sub($1, ''), data)
|
|
11
|
+
else
|
|
12
|
+
puts doc
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
puts <<-usage
|
|
16
|
+
Usage: cat data.yml template.mustache | mustache
|
|
17
|
+
|
|
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.
|
|
33
|
+
usage
|
|
34
|
+
end
|
data/contrib/tpl-mode.el
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
;;; tpl-mode.el -- a major mode for editing Google CTemplate files.
|
|
2
|
+
;;; By Tony Gentilcore, July 2006
|
|
3
|
+
;;;
|
|
4
|
+
;;; Very minor, backwards compatible changes added for Mustache compatibility
|
|
5
|
+
;;; by Chris Wanstrath, October 2009
|
|
6
|
+
;;;
|
|
7
|
+
;;; TO USE:
|
|
8
|
+
;;; 1) Copy this file somewhere you in emacs load-path. To see what
|
|
9
|
+
;;; your load-path is, run inside emacs: C-h v load-path<RET>
|
|
10
|
+
;;; 2) Add the following two lines to your .emacs file:
|
|
11
|
+
;;; (setq auto-mode-alist (cons '("\\.tpl$" . tpl-mode) auto-mode-alist))
|
|
12
|
+
;;; (autoload 'tpl-mode "tpl-mode" "Major mode for editing CTemplate files." t)
|
|
13
|
+
;;; 3) Optionally (but recommended), add this third line as well:
|
|
14
|
+
;;; (add-hook 'tpl-mode-hook '(lambda () (font-lock-mode 1)))
|
|
15
|
+
;;; ---
|
|
16
|
+
;;;
|
|
17
|
+
;;; While the CTemplate language can be used for any types of text,
|
|
18
|
+
;;; this mode is intended for using CTemplate to write HTML.
|
|
19
|
+
;;;
|
|
20
|
+
;;; The indentation still has minor bugs due to the fact that
|
|
21
|
+
;;; templates do not require valid HTML.
|
|
22
|
+
;;;
|
|
23
|
+
;;; It would be nice to be able to highlight attributes of HTML tags,
|
|
24
|
+
;;; however this is difficult due to the presence of CTemplate symbols
|
|
25
|
+
;;; embedded within attributes.
|
|
26
|
+
|
|
27
|
+
(eval-when-compile
|
|
28
|
+
(require 'font-lock))
|
|
29
|
+
|
|
30
|
+
(defgroup tpl-mode nil
|
|
31
|
+
"Major mode for editing Google CTemplate and Mustache files"
|
|
32
|
+
:group 'languages)
|
|
33
|
+
|
|
34
|
+
(defvar tpl-mode-version "1.1"
|
|
35
|
+
"Version of `tpl-mode.el'.")
|
|
36
|
+
|
|
37
|
+
(defvar tpl-mode-abbrev-table nil
|
|
38
|
+
"Abbrev table for use in tpl-mode buffers.")
|
|
39
|
+
|
|
40
|
+
(define-abbrev-table 'tpl-mode-abbrev-table ())
|
|
41
|
+
|
|
42
|
+
(defcustom tpl-mode-hook nil
|
|
43
|
+
"*Hook that runs upon entering tpl-mode."
|
|
44
|
+
:type 'hook)
|
|
45
|
+
|
|
46
|
+
(defvar tpl-mode-map nil
|
|
47
|
+
"Keymap for tpl-mode major mode")
|
|
48
|
+
|
|
49
|
+
(if tpl-mode-map
|
|
50
|
+
nil
|
|
51
|
+
(setq tpl-mode-map (make-sparse-keymap)))
|
|
52
|
+
|
|
53
|
+
(define-key tpl-mode-map "\t" 'tpl-indent-command)
|
|
54
|
+
(define-key tpl-mode-map "\C-m" 'reindent-then-newline-and-indent)
|
|
55
|
+
(define-key tpl-mode-map "\C-ct" 'tpl-insert-tag)
|
|
56
|
+
(define-key tpl-mode-map "\C-cv" 'tpl-insert-variable)
|
|
57
|
+
(define-key tpl-mode-map "\C-cs" 'tpl-insert-section)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
(defvar tpl-mode-syntax-table nil
|
|
61
|
+
"Syntax table in use in tpl-mode buffers.")
|
|
62
|
+
|
|
63
|
+
;; Syntax table.
|
|
64
|
+
(if tpl-mode-syntax-table
|
|
65
|
+
nil
|
|
66
|
+
(setq tpl-mode-syntax-table (make-syntax-table text-mode-syntax-table))
|
|
67
|
+
(modify-syntax-entry ?< "(> " tpl-mode-syntax-table)
|
|
68
|
+
(modify-syntax-entry ?> ")< " tpl-mode-syntax-table)
|
|
69
|
+
(modify-syntax-entry ?\" ". " tpl-mode-syntax-table)
|
|
70
|
+
(modify-syntax-entry ?\\ ". " tpl-mode-syntax-table)
|
|
71
|
+
(modify-syntax-entry ?' "w " tpl-mode-syntax-table))
|
|
72
|
+
|
|
73
|
+
(defvar tpl-basic-offset 2
|
|
74
|
+
"The basic indentation offset.")
|
|
75
|
+
|
|
76
|
+
;; Constant regular expressions to identify template elements.
|
|
77
|
+
(defconst tpl-mode-tpl-token "[a-zA-Z][a-zA-Z0-9_:=\?!-]*?")
|
|
78
|
+
(defconst tpl-mode-section (concat "\\({{[#/]\s*"
|
|
79
|
+
tpl-mode-tpl-token
|
|
80
|
+
"\s*}}\\)"))
|
|
81
|
+
(defconst tpl-mode-open-section (concat "\\({{#\s*"
|
|
82
|
+
tpl-mode-tpl-token
|
|
83
|
+
"\s*}}\\)"))
|
|
84
|
+
(defconst tpl-mode-close-section (concat "{{/\\(\s*"
|
|
85
|
+
tpl-mode-tpl-token
|
|
86
|
+
"\s*\\)}}"))
|
|
87
|
+
;; TODO(tonyg) Figure out a way to support multiline comments.
|
|
88
|
+
(defconst tpl-mode-comment "\\({{!.*?}}\\)")
|
|
89
|
+
(defconst tpl-mode-include (concat "\\({{>\s*"
|
|
90
|
+
tpl-mode-tpl-token
|
|
91
|
+
"\s*}}\\)"))
|
|
92
|
+
(defconst tpl-mode-variable (concat "\\({{\s*"
|
|
93
|
+
tpl-mode-tpl-token
|
|
94
|
+
"\s*}}\\)"))
|
|
95
|
+
(defconst tpl-mode-builtins
|
|
96
|
+
(concat
|
|
97
|
+
"\\({{\\<\s*"
|
|
98
|
+
(regexp-opt
|
|
99
|
+
'("BI_NEWLINE" "BI_SPACE")
|
|
100
|
+
t)
|
|
101
|
+
"\s*\\>}}\\)"))
|
|
102
|
+
(defconst tpl-mode-close-section-at-start (concat "^[ \t]*?"
|
|
103
|
+
tpl-mode-close-section))
|
|
104
|
+
|
|
105
|
+
;; Constant regular expressions to identify html tags.
|
|
106
|
+
;; Taken from HTML 4.01 / XHTML 1.0 Reference found at:
|
|
107
|
+
;; http://www.w3schools.com/tags/default.asp.
|
|
108
|
+
(defconst tpl-mode-html-constant "\\(&#?[a-z0-9]\\{2,5\\};\\)")
|
|
109
|
+
(defconst tpl-mode-pair-tag
|
|
110
|
+
(concat
|
|
111
|
+
"\\<"
|
|
112
|
+
(regexp-opt
|
|
113
|
+
'("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
|
|
114
|
+
"big" "blockquote" "body" "button" "caption" "center" "cite"
|
|
115
|
+
"code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
|
|
116
|
+
"dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
|
|
117
|
+
"h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
|
|
118
|
+
"kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
|
|
119
|
+
"noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
|
|
120
|
+
"s" "samp" "script" "select" "small" "span" "strike"
|
|
121
|
+
"strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
|
|
122
|
+
"tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
|
|
123
|
+
t)
|
|
124
|
+
"\\>"))
|
|
125
|
+
(defconst tpl-mode-standalone-tag
|
|
126
|
+
(concat
|
|
127
|
+
"\\<"
|
|
128
|
+
(regexp-opt
|
|
129
|
+
'("base" "br" "hr" "img" "input" "meta" "param")
|
|
130
|
+
t)
|
|
131
|
+
"\\>"))
|
|
132
|
+
(defconst tpl-mode-open-tag (concat "<\\("
|
|
133
|
+
tpl-mode-pair-tag
|
|
134
|
+
"\\)"))
|
|
135
|
+
(defconst tpl-mode-close-tag (concat "</\\("
|
|
136
|
+
tpl-mode-pair-tag
|
|
137
|
+
"\\)>"))
|
|
138
|
+
(defconst tpl-mode-close-tag-at-start (concat "^[ \t]*?"
|
|
139
|
+
tpl-mode-close-tag))
|
|
140
|
+
|
|
141
|
+
(defconst tpl-mode-blank-line "^[ \t]*?$")
|
|
142
|
+
(defconst tpl-mode-dangling-open (concat "\\("
|
|
143
|
+
tpl-mode-open-section
|
|
144
|
+
"\\)\\|\\("
|
|
145
|
+
tpl-mode-open-tag
|
|
146
|
+
"\\)[^/]*$"))
|
|
147
|
+
|
|
148
|
+
(defun tpl-indent-command ()
|
|
149
|
+
"Command for indenting text. Just calls tpl-indent."
|
|
150
|
+
(interactive)
|
|
151
|
+
(tpl-indent))
|
|
152
|
+
|
|
153
|
+
(defun tpl-insert-tag (tag)
|
|
154
|
+
"Inserts an HTML tag."
|
|
155
|
+
(interactive "sTag: ")
|
|
156
|
+
(tpl-indent)
|
|
157
|
+
(insert (concat "<" tag ">"))
|
|
158
|
+
(insert "\n\n")
|
|
159
|
+
(insert (concat "</" tag ">"))
|
|
160
|
+
(tpl-indent)
|
|
161
|
+
(forward-line -1)
|
|
162
|
+
(tpl-indent))
|
|
163
|
+
|
|
164
|
+
(defun tpl-insert-variable (variable)
|
|
165
|
+
"Inserts a tpl variable."
|
|
166
|
+
(interactive "sVariable: ")
|
|
167
|
+
(insert (concat "{{" variable "}}")))
|
|
168
|
+
|
|
169
|
+
(defun tpl-insert-section (section)
|
|
170
|
+
"Inserts a tpl section."
|
|
171
|
+
(interactive "sSection: ")
|
|
172
|
+
(tpl-indent)
|
|
173
|
+
(insert (concat "{{#" section "}}\n"))
|
|
174
|
+
(insert "\n")
|
|
175
|
+
(insert (concat "{{/" section "}}"))
|
|
176
|
+
(tpl-indent)
|
|
177
|
+
(forward-line -1)
|
|
178
|
+
(tpl-indent))
|
|
179
|
+
|
|
180
|
+
;; Function to control indenting.
|
|
181
|
+
(defun tpl-indent ()
|
|
182
|
+
"Indent current line"
|
|
183
|
+
;; Set the point to beginning of line.
|
|
184
|
+
(beginning-of-line)
|
|
185
|
+
;; If we are at the beginning of the file, indent to 0.
|
|
186
|
+
(if (bobp)
|
|
187
|
+
(indent-line-to 0)
|
|
188
|
+
(let ((tag-stack 1) (close-tag "") (cur-indent 0) (old-pnt (point-marker))
|
|
189
|
+
(close-at-start) (open-token) (dangling-open))
|
|
190
|
+
(progn
|
|
191
|
+
;; Determine if this is a template line or an html line.
|
|
192
|
+
(if (looking-at "^[ \t]*?{{")
|
|
193
|
+
(setq close-at-start tpl-mode-close-section-at-start
|
|
194
|
+
open-token "{{#")
|
|
195
|
+
(setq close-at-start tpl-mode-close-tag-at-start
|
|
196
|
+
open-token "<"))
|
|
197
|
+
;; If there is a closing tag at the start of the line, search back
|
|
198
|
+
;; for its opener and indent to that level.
|
|
199
|
+
(if (looking-at close-at-start)
|
|
200
|
+
(progn
|
|
201
|
+
(save-excursion
|
|
202
|
+
(setq close-tag (match-string 1))
|
|
203
|
+
;; Keep searching for a match for the close tag until
|
|
204
|
+
;; the tag-stack is 0.
|
|
205
|
+
(while (and (not (bobp))
|
|
206
|
+
(> tag-stack 0)
|
|
207
|
+
(re-search-backward (concat open-token
|
|
208
|
+
"\\(/?\\)"
|
|
209
|
+
close-tag) nil t))
|
|
210
|
+
(if (string-equal (match-string 1) "/")
|
|
211
|
+
;; We found another close tag, so increment tag-stack.
|
|
212
|
+
(setq tag-stack (+ tag-stack 1))
|
|
213
|
+
;; We found an open tag, so decrement tag-stack.
|
|
214
|
+
(setq tag-stack (- tag-stack 1)))
|
|
215
|
+
(setq cur-indent (current-indentation))))
|
|
216
|
+
(if (> tag-stack 0)
|
|
217
|
+
(save-excursion
|
|
218
|
+
(forward-line -1)
|
|
219
|
+
(setq cur-indent (current-indentation)))))
|
|
220
|
+
;; This was not a closing tag, so we check if the previous line
|
|
221
|
+
;; was an opening tag.
|
|
222
|
+
(save-excursion
|
|
223
|
+
;; Keep moving back until we find a line that is not blank
|
|
224
|
+
(while (progn
|
|
225
|
+
(forward-line -1)
|
|
226
|
+
(and (not (bobp)) (looking-at tpl-mode-blank-line))))
|
|
227
|
+
(setq cur-indent (current-indentation))
|
|
228
|
+
(if (re-search-forward tpl-mode-dangling-open old-pnt t)
|
|
229
|
+
(setq cur-indent (+ cur-indent tpl-basic-offset)))))
|
|
230
|
+
;; Finally, we execute the actual indentation.
|
|
231
|
+
(if (> cur-indent 0)
|
|
232
|
+
(indent-line-to cur-indent)
|
|
233
|
+
(indent-line-to 0))))))
|
|
234
|
+
|
|
235
|
+
;; controls highlighting
|
|
236
|
+
(defconst tpl-mode-font-lock-keywords
|
|
237
|
+
(list
|
|
238
|
+
(list tpl-mode-section
|
|
239
|
+
'(1 font-lock-keyword-face))
|
|
240
|
+
(list tpl-mode-comment
|
|
241
|
+
'(1 font-lock-comment-face))
|
|
242
|
+
(list tpl-mode-include
|
|
243
|
+
'(1 font-lock-builtin-face))
|
|
244
|
+
(list tpl-mode-builtins
|
|
245
|
+
'(1 font-lock-variable-name-face))
|
|
246
|
+
(list tpl-mode-variable
|
|
247
|
+
'(1 font-lock-reference-face))
|
|
248
|
+
(list (concat "</?\\(" tpl-mode-pair-tag "\\)")
|
|
249
|
+
'(1 font-lock-function-name-face))
|
|
250
|
+
(list (concat "<\\(" tpl-mode-standalone-tag "\\)")
|
|
251
|
+
'(1 font-lock-function-name-face))
|
|
252
|
+
(list tpl-mode-html-constant
|
|
253
|
+
'(1 font-lock-variable-name-face))))
|
|
254
|
+
|
|
255
|
+
(put 'tpl-mode 'font-lock-defaults '(tpl-font-lock-keywords nil t))
|
|
256
|
+
|
|
257
|
+
(defun tpl-mode ()
|
|
258
|
+
"Major mode for editing Google CTemplate file."
|
|
259
|
+
(interactive)
|
|
260
|
+
(kill-all-local-variables)
|
|
261
|
+
(use-local-map tpl-mode-map)
|
|
262
|
+
(setq major-mode 'tpl-mode)
|
|
263
|
+
(setq mode-name "tpl-mode")
|
|
264
|
+
(setq local-abbrev-table tpl-mode-abbrev-table)
|
|
265
|
+
(setq indent-tabs-mode nil)
|
|
266
|
+
(set-syntax-table tpl-mode-syntax-table)
|
|
267
|
+
;; show trailing whitespace, but only when the user can fix it
|
|
268
|
+
(setq show-trailing-whitespace (not buffer-read-only))
|
|
269
|
+
(make-local-variable 'indent-line-function)
|
|
270
|
+
(setq indent-line-function 'tpl-indent)
|
|
271
|
+
(setq font-lock-defaults '(tpl-mode-font-lock-keywords))
|
|
272
|
+
(run-hooks 'tpl-mode-hook))
|
|
273
|
+
|
|
274
|
+
(provide 'tpl-mode)
|
data/examples/namespaced.rb
CHANGED
|
@@ -9,8 +9,16 @@ module TestViews
|
|
|
9
9
|
"Dragon < Tiger"
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
|
-
end
|
|
13
12
|
|
|
13
|
+
class NamespacedWithPartial < Mustache
|
|
14
|
+
self.path = File.dirname(__FILE__)
|
|
15
|
+
self.template = "My opinion: {{>inner_partial}}"
|
|
16
|
+
|
|
17
|
+
def title
|
|
18
|
+
"Victory"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
14
22
|
|
|
15
23
|
if $0 == __FILE__
|
|
16
24
|
puts TestViews::Namespaced.to_html
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
|
+
require 'mustache'
|
|
3
|
+
|
|
4
|
+
module SimpleView
|
|
5
|
+
def name
|
|
6
|
+
"Bob"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def value
|
|
10
|
+
100_000
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def taxed_value
|
|
14
|
+
value - (value * 0.4)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def in_ca
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class PartialWithModule < Mustache
|
|
23
|
+
include SimpleView
|
|
24
|
+
self.path = File.dirname(__FILE__)
|
|
25
|
+
|
|
26
|
+
def greeting
|
|
27
|
+
"Welcome"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def farewell
|
|
31
|
+
"Fair enough, right?"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if $0 == __FILE__
|
|
36
|
+
puts PartialWithModule.to_html
|
|
37
|
+
end
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
<h1>{{title}}</h1>
|
|
2
|
-
{{
|
|
2
|
+
{{>inner_partial}}
|
data/lib/mustache/context.rb
CHANGED
data/lib/mustache/template.rb
CHANGED
|
@@ -103,10 +103,10 @@ class Mustache
|
|
|
103
103
|
# 1. Escaped variable tags - {{var}}
|
|
104
104
|
# 2. Unescaped variable tags - {{{var}}}
|
|
105
105
|
# 3. Comment variable tags - {{! comment}
|
|
106
|
-
# 4. Partial tags - {{
|
|
106
|
+
# 4. Partial tags - {{> partial_name }}
|
|
107
107
|
def compile_tags(src)
|
|
108
108
|
res = ""
|
|
109
|
-
while src =~ /#{otag}(
|
|
109
|
+
while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/
|
|
110
110
|
res << str($`)
|
|
111
111
|
case $1
|
|
112
112
|
when '#'
|
|
@@ -117,7 +117,7 @@ class Mustache
|
|
|
117
117
|
# ignore comments
|
|
118
118
|
when '='
|
|
119
119
|
self.otag, self.ctag = $2.strip.split(' ', 2)
|
|
120
|
-
when '<'
|
|
120
|
+
when '>', '<'
|
|
121
121
|
res << compile_partial($2.strip)
|
|
122
122
|
when '{'
|
|
123
123
|
res << utag($2.strip)
|
|
@@ -131,13 +131,8 @@ class Mustache
|
|
|
131
131
|
|
|
132
132
|
# Partials are basically a way to render views from inside other views.
|
|
133
133
|
def compile_partial(name)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
ev("#{klass}.render")
|
|
137
|
-
else
|
|
138
|
-
src = File.read("#{@template_path}/#{name}.#{@template_extension}")
|
|
139
|
-
compile(src)[1..-2]
|
|
140
|
-
end
|
|
134
|
+
src = File.read("#{@template_path}/#{name}.#{@template_extension}")
|
|
135
|
+
compile(src)[1..-2]
|
|
141
136
|
end
|
|
142
137
|
|
|
143
138
|
# Generate a temporary id, used when compiling code.
|
data/lib/mustache/version.rb
CHANGED
data/lib/mustache.rb
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
$(function() {
|
|
3
|
+
$('#mustache_variables .variable').each(function() {
|
|
4
|
+
var el = $(this)
|
|
5
|
+
if (el.text().length > 500) {
|
|
6
|
+
var txt = el.text()
|
|
7
|
+
el.click(function() {
|
|
8
|
+
$(this).text(txt)
|
|
9
|
+
}).text( el.text().slice(0, 500) + '...' )
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
});
|
|
13
|
+
</script>
|
|
14
|
+
|
|
1
15
|
<h3>Render Times</h3>
|
|
2
16
|
|
|
3
17
|
<table>
|
|
@@ -16,7 +30,7 @@
|
|
|
16
30
|
|
|
17
31
|
<h3>Variables</h3>
|
|
18
32
|
|
|
19
|
-
<table>
|
|
33
|
+
<table id="mustache_variables">
|
|
20
34
|
<tr>
|
|
21
35
|
<th>Name</th>
|
|
22
36
|
<th>Value</th>
|
|
@@ -25,7 +39,7 @@
|
|
|
25
39
|
{{# variables }}
|
|
26
40
|
<tr>
|
|
27
41
|
<td>{{ key }}</td>
|
|
28
|
-
<td>{{ value }}</td>
|
|
42
|
+
<td class="variable">{{ value }}</td>
|
|
29
43
|
</tr>
|
|
30
44
|
{{/ variables }}
|
|
31
45
|
</table>
|
data/test/autoloading_test.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
|
2
|
+
require 'helper'
|
|
2
3
|
|
|
3
4
|
module TestViews; end
|
|
4
5
|
|
|
@@ -35,4 +36,17 @@ class AutoloadingTest < Test::Unit::TestCase
|
|
|
35
36
|
<h1>Dragon < Tiger</h1>
|
|
36
37
|
end_render
|
|
37
38
|
end
|
|
39
|
+
|
|
40
|
+
def test_namespaced_partial_autoload
|
|
41
|
+
Mustache.view_namespace = TestViews
|
|
42
|
+
klass = Mustache.view_class(:namespaced_with_partial)
|
|
43
|
+
assert_equal TestViews::NamespacedWithPartial, klass
|
|
44
|
+
assert_equal <<-end_render.strip, klass.render
|
|
45
|
+
My opinion: Again, Victory!
|
|
46
|
+
end_render
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_bad_constant_name
|
|
50
|
+
assert_equal Mustache, Mustache.view_class(404)
|
|
51
|
+
end
|
|
38
52
|
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../examples'
|
|
4
|
+
require 'simple'
|
|
5
|
+
require 'complex_view'
|
|
6
|
+
require 'partial_with_module'
|
|
7
|
+
require 'template_partial'
|
|
8
|
+
require 'escaped'
|
|
9
|
+
require 'unescaped'
|
|
10
|
+
require 'comments'
|
|
11
|
+
require 'passenger'
|
|
12
|
+
require 'delimiters'
|
|
13
|
+
require 'double_section'
|
data/test/mustache_test.rb
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../examples'
|
|
4
|
-
require 'simple'
|
|
5
|
-
require 'complex_view'
|
|
6
|
-
require 'view_partial'
|
|
7
|
-
require 'template_partial'
|
|
8
|
-
require 'escaped'
|
|
9
|
-
require 'unescaped'
|
|
10
|
-
require 'comments'
|
|
11
|
-
require 'passenger'
|
|
12
|
-
require 'delimiters'
|
|
13
|
-
require 'double_section'
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
|
2
|
+
require 'helper'
|
|
14
3
|
|
|
15
4
|
class MustacheTest < Test::Unit::TestCase
|
|
16
5
|
def test_passenger
|
|
@@ -113,46 +102,15 @@ end_simple
|
|
|
113
102
|
assert_equal 'Hi mom!', view.render
|
|
114
103
|
end
|
|
115
104
|
|
|
116
|
-
def test_view_partial
|
|
117
|
-
assert_equal <<-end_partial.strip, ViewPartial.render
|
|
118
|
-
<h1>Welcome</h1>
|
|
119
|
-
Hello Chris
|
|
120
|
-
You have just won $10000!
|
|
121
|
-
Well, $6000.0, after taxes.
|
|
122
|
-
|
|
123
|
-
<h3>Fair enough, right?</h3>
|
|
124
|
-
end_partial
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def test_template_partial
|
|
128
|
-
assert_equal <<-end_partial.strip, TemplatePartial.render
|
|
129
|
-
<h1>Welcome</h1>
|
|
130
|
-
Again, Welcome!
|
|
131
|
-
end_partial
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def test_template_partial_with_custom_extension
|
|
135
|
-
partial = Class.new(TemplatePartial)
|
|
136
|
-
partial.template_extension = 'txt'
|
|
137
|
-
partial.template_path = File.dirname(__FILE__) + '/../examples'
|
|
138
|
-
|
|
139
|
-
assert_equal <<-end_partial.strip, partial.render.strip
|
|
140
|
-
Welcome
|
|
141
|
-
-------
|
|
142
|
-
|
|
143
|
-
## Again, Welcome! ##
|
|
144
|
-
end_partial
|
|
145
|
-
end
|
|
146
|
-
|
|
147
105
|
def test_delimiters
|
|
148
|
-
assert_equal <<-
|
|
106
|
+
assert_equal <<-end_template, Delimiters.render
|
|
149
107
|
|
|
150
108
|
* It worked the first time.
|
|
151
109
|
|
|
152
110
|
* And it worked the second time.
|
|
153
111
|
|
|
154
112
|
* Then, surprisingly, it worked the third time.
|
|
155
|
-
|
|
113
|
+
end_template
|
|
156
114
|
end
|
|
157
115
|
|
|
158
116
|
def test_double_section
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
|
2
|
+
require 'helper'
|
|
3
|
+
|
|
4
|
+
class PartialTest < Test::Unit::TestCase
|
|
5
|
+
def test_view_partial
|
|
6
|
+
assert_equal <<-end_partial.strip, PartialWithModule.render
|
|
7
|
+
<h1>Welcome</h1>
|
|
8
|
+
Hello Bob
|
|
9
|
+
You have just won $100000!
|
|
10
|
+
|
|
11
|
+
<h3>Fair enough, right?</h3>
|
|
12
|
+
end_partial
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_view_partial_inherits_context
|
|
16
|
+
klass = Class.new(TemplatePartial)
|
|
17
|
+
klass.template_path = File.dirname(__FILE__) + '/../examples'
|
|
18
|
+
view = klass.new
|
|
19
|
+
view[:titles] = [{:title => :One}, {:title => :Two}]
|
|
20
|
+
view.template = <<-end_template
|
|
21
|
+
<h1>Context Test</h1>
|
|
22
|
+
<ul>
|
|
23
|
+
{{#titles}}
|
|
24
|
+
<li>{{>inner_partial}}</li>
|
|
25
|
+
{{/titles}}
|
|
26
|
+
</ul>
|
|
27
|
+
end_template
|
|
28
|
+
assert_equal <<-end_partial, view.render
|
|
29
|
+
<h1>Context Test</h1>
|
|
30
|
+
<ul>
|
|
31
|
+
<li>Again, One!</li>
|
|
32
|
+
<li>Again, Two!</li>
|
|
33
|
+
</ul>
|
|
34
|
+
end_partial
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_view_partial_inherits_context_of_class_methods
|
|
38
|
+
klass = Class.new(TemplatePartial)
|
|
39
|
+
klass.template_path = File.dirname(__FILE__) + '/../examples'
|
|
40
|
+
klass.send(:define_method, :titles) do
|
|
41
|
+
[{:title => :One}, {:title => :Two}]
|
|
42
|
+
end
|
|
43
|
+
view = klass.new
|
|
44
|
+
view.template = <<-end_template
|
|
45
|
+
<h1>Context Test</h1>
|
|
46
|
+
<ul>
|
|
47
|
+
{{#titles}}
|
|
48
|
+
<li>{{>inner_partial}}</li>
|
|
49
|
+
{{/titles}}
|
|
50
|
+
</ul>
|
|
51
|
+
end_template
|
|
52
|
+
assert_equal <<-end_partial, view.render
|
|
53
|
+
<h1>Context Test</h1>
|
|
54
|
+
<ul>
|
|
55
|
+
<li>Again, One!</li>
|
|
56
|
+
<li>Again, Two!</li>
|
|
57
|
+
</ul>
|
|
58
|
+
end_partial
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_template_partial
|
|
62
|
+
assert_equal <<-end_partial.strip, TemplatePartial.render
|
|
63
|
+
<h1>Welcome</h1>
|
|
64
|
+
Again, Welcome!
|
|
65
|
+
end_partial
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_template_partial_with_custom_extension
|
|
69
|
+
partial = Class.new(TemplatePartial)
|
|
70
|
+
partial.template_extension = 'txt'
|
|
71
|
+
partial.template_path = File.dirname(__FILE__) + '/../examples'
|
|
72
|
+
|
|
73
|
+
assert_equal <<-end_partial.strip, partial.render.strip
|
|
74
|
+
Welcome
|
|
75
|
+
-------
|
|
76
|
+
|
|
77
|
+
## Again, Welcome! ##
|
|
78
|
+
end_partial
|
|
79
|
+
end
|
|
80
|
+
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Wanstrath
|
|
@@ -9,14 +9,14 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-
|
|
13
|
-
default_executable:
|
|
12
|
+
date: 2009-11-23 00:00:00 -08:00
|
|
13
|
+
default_executable: mustache
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
16
16
|
description: Mustache is a framework-agnostic way to render logic-free views.
|
|
17
17
|
email: chris@ozmm.org
|
|
18
|
-
executables:
|
|
19
|
-
|
|
18
|
+
executables:
|
|
19
|
+
- mustache
|
|
20
20
|
extensions: []
|
|
21
21
|
|
|
22
22
|
extra_rdoc_files:
|
|
@@ -35,7 +35,9 @@ files:
|
|
|
35
35
|
- benchmarks/helper.rb
|
|
36
36
|
- benchmarks/simple.erb
|
|
37
37
|
- benchmarks/speed.rb
|
|
38
|
+
- bin/mustache
|
|
38
39
|
- contrib/mustache.vim
|
|
40
|
+
- contrib/tpl-mode.el
|
|
39
41
|
- examples/comments.mustache
|
|
40
42
|
- examples/comments.rb
|
|
41
43
|
- examples/complex_view.mustache
|
|
@@ -50,6 +52,8 @@ files:
|
|
|
50
52
|
- examples/inner_partial.txt
|
|
51
53
|
- examples/namespaced.mustache
|
|
52
54
|
- examples/namespaced.rb
|
|
55
|
+
- examples/partial_with_module.mustache
|
|
56
|
+
- examples/partial_with_module.rb
|
|
53
57
|
- examples/passenger.conf
|
|
54
58
|
- examples/passenger.rb
|
|
55
59
|
- examples/simple.mustache
|
|
@@ -59,8 +63,6 @@ files:
|
|
|
59
63
|
- examples/template_partial.txt
|
|
60
64
|
- examples/unescaped.mustache
|
|
61
65
|
- examples/unescaped.rb
|
|
62
|
-
- examples/view_partial.mustache
|
|
63
|
-
- examples/view_partial.rb
|
|
64
66
|
- lib/mustache.rb
|
|
65
67
|
- lib/mustache/context.rb
|
|
66
68
|
- lib/mustache/sinatra.rb
|
|
@@ -70,7 +72,9 @@ files:
|
|
|
70
72
|
- lib/rack/bug/panels/mustache_panel/mustache_extension.rb
|
|
71
73
|
- lib/rack/bug/panels/mustache_panel/view.mustache
|
|
72
74
|
- test/autoloading_test.rb
|
|
75
|
+
- test/helper.rb
|
|
73
76
|
- test/mustache_test.rb
|
|
77
|
+
- test/partial_test.rb
|
|
74
78
|
has_rdoc: true
|
|
75
79
|
homepage: http://github.com/defunkt/mustache
|
|
76
80
|
licenses: []
|
|
@@ -101,15 +105,17 @@ specification_version: 3
|
|
|
101
105
|
summary: Mustache is a framework-agnostic way to render logic-free views.
|
|
102
106
|
test_files:
|
|
103
107
|
- test/autoloading_test.rb
|
|
108
|
+
- test/helper.rb
|
|
104
109
|
- test/mustache_test.rb
|
|
110
|
+
- test/partial_test.rb
|
|
105
111
|
- examples/comments.rb
|
|
106
112
|
- examples/complex_view.rb
|
|
107
113
|
- examples/delimiters.rb
|
|
108
114
|
- examples/double_section.rb
|
|
109
115
|
- examples/escaped.rb
|
|
110
116
|
- examples/namespaced.rb
|
|
117
|
+
- examples/partial_with_module.rb
|
|
111
118
|
- examples/passenger.rb
|
|
112
119
|
- examples/simple.rb
|
|
113
120
|
- examples/template_partial.rb
|
|
114
121
|
- examples/unescaped.rb
|
|
115
|
-
- examples/view_partial.rb
|
data/examples/view_partial.rb
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
|
-
require 'mustache'
|
|
3
|
-
|
|
4
|
-
class ViewPartial < Mustache
|
|
5
|
-
self.path = File.dirname(__FILE__)
|
|
6
|
-
|
|
7
|
-
def greeting
|
|
8
|
-
"Welcome"
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def farewell
|
|
12
|
-
"Fair enough, right?"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
if $0 == __FILE__
|
|
17
|
-
puts ViewPartial.to_html
|
|
18
|
-
end
|