mustache 1.0.0 → 1.0.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.
- checksums.yaml +13 -5
- data/README.md +5 -5
- data/bin/mustache +1 -1
- data/lib/mustache.rb +12 -14
- data/lib/mustache/context.rb +10 -3
- data/lib/mustache/generator.rb +1 -1
- data/lib/mustache/parser.rb +28 -20
- data/lib/mustache/utils.rb +31 -0
- data/lib/mustache/version.rb +1 -1
- data/test/mustache_test.rb +28 -7
- data/test/spec_test.rb +1 -1
- metadata +41 -18
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2MzZDViZWQ0ODY5NmZhYzc2NjdmZThkNWYzOWUxMjg1NDMzOWUwMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTY0MmE3MmU4M2RkM2RmNjg5N2Q5ZjBiNWRlZTZiN2UxY2ZhM2U0Zg==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YjYyMTdmOTgwMTQ3NGE3MjU1MGI3Nzg4Yzg2Mjk1YTJhY2UzNDZhNjVlMDBi
|
10
|
+
MmZiYTJjYzZmOTA4ZmEyN2VhNDAyNjA5ZmZkM2I4YWUyMGI5ZDQ5ZWVhMWRh
|
11
|
+
ODk3YTk0NDBhYzZkOGYyZDlhMjA1ODA2NWJmYmFlNjA3MTE2YzQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGM4M2U2NDI4ZjRkZTU4ZTIzOTQwZjQ4MmMzZmE4NDczNDI4NTY2YzE3ZmNk
|
14
|
+
NDIzMWJlOWE5NDVmMzUwYWU0MjRiM2VjMTVhODllMTdhNGVkZjAwMTQ2NzAy
|
15
|
+
OWIwMGE2YWY3MjA4ZGIzZmZlZDZkNWI0YjJlMzc1NWEzZGY3NjY=
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Mustache
|
2
2
|
|
3
|
+
[](https://gitter.im/mustache/mustache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
|
3
5
|
Inspired by [ctemplate][1] and [et][2], Mustache is a
|
4
6
|
framework-agnostic way to render logic-free views.
|
5
7
|
|
@@ -283,10 +285,8 @@ Finally, our template might look like this:
|
|
283
285
|
|
284
286
|
### Sinatra
|
285
287
|
|
286
|
-
|
287
|
-
|
288
|
-
<https://github.com/mustache/mustache/blob/master/lib/mustache/sinatra.rb>
|
289
|
-
for complete documentation.
|
288
|
+
Sinatra integration is available with the
|
289
|
+
[mustache-sinatra gem](https://github.com/mustache/mustache-sinatra).
|
290
290
|
|
291
291
|
An example Sinatra application is also provided:
|
292
292
|
<https://github.com/defunkt/mustache-sinatra-example>
|
@@ -388,4 +388,4 @@ You can also find us in #{ on [irc.freenode.net][irc].
|
|
388
388
|
[vim]: https://github.com/mustache/vim-mustache-handlebars
|
389
389
|
[emacs]: https://github.com/mustache/vim-mustache-handlebars
|
390
390
|
[tmbundle]: https://github.com/defunkt/Mustache.tmbundle
|
391
|
-
[diff]: https://gist.github.com/defunkt/345490
|
391
|
+
[diff]: https://gist.github.com/defunkt/345490
|
data/bin/mustache
CHANGED
data/lib/mustache.rb
CHANGED
@@ -2,6 +2,7 @@ require 'mustache/enumerable'
|
|
2
2
|
require 'mustache/template'
|
3
3
|
require 'mustache/context'
|
4
4
|
require 'mustache/settings'
|
5
|
+
require 'mustache/utils'
|
5
6
|
|
6
7
|
# Mustache is the base class from which your Mustache subclasses
|
7
8
|
# should inherit (though it can be used on its own).
|
@@ -165,14 +166,21 @@ class Mustache
|
|
165
166
|
#
|
166
167
|
# Call `render` if you need to process it.
|
167
168
|
def self.partial(name)
|
168
|
-
|
169
|
+
self.new.partial(name)
|
169
170
|
end
|
170
171
|
|
171
172
|
# Override this in your subclass if you want to do fun things like
|
172
173
|
# reading templates from a database. It will be rendered by the
|
173
174
|
# context, so all you need to do is return a string.
|
174
175
|
def partial(name)
|
175
|
-
|
176
|
+
path = "#{template_path}/#{name}.#{template_extension}"
|
177
|
+
|
178
|
+
begin
|
179
|
+
File.read(path)
|
180
|
+
rescue
|
181
|
+
raise if raise_on_context_miss?
|
182
|
+
""
|
183
|
+
end
|
176
184
|
end
|
177
185
|
|
178
186
|
# Override this to provide custom escaping.
|
@@ -246,12 +254,7 @@ class Mustache
|
|
246
254
|
# template_partial => TemplatePartial
|
247
255
|
# template/partial => Template::Partial
|
248
256
|
def self.classify(underscored)
|
249
|
-
|
250
|
-
namespace.split(/[-_]/).map do |part|
|
251
|
-
part[0] = part.chars.first.upcase
|
252
|
-
part
|
253
|
-
end.join
|
254
|
-
end.join('::')
|
257
|
+
Mustache::Utils::String.new(underscored).classify
|
255
258
|
end
|
256
259
|
|
257
260
|
# TemplatePartial => template_partial
|
@@ -260,12 +263,7 @@ class Mustache
|
|
260
263
|
def self.underscore(classified = name)
|
261
264
|
classified = superclass.name if classified.to_s.empty?
|
262
265
|
|
263
|
-
|
264
|
-
|
265
|
-
string.split('::').map do |part|
|
266
|
-
part[0] = part[0].downcase
|
267
|
-
part.gsub(/[A-Z]/) { |s| "_" << s.downcase }
|
268
|
-
end.join('/')
|
266
|
+
Mustache::Utils::String.new(classified).underscore(view_namespace)
|
269
267
|
end
|
270
268
|
|
271
269
|
# @param [Template,String] obj Turns `obj` into a template
|
data/lib/mustache/context.rb
CHANGED
@@ -41,7 +41,7 @@ class Mustache
|
|
41
41
|
# @return [Mustache] First Mustache in the stack.
|
42
42
|
#
|
43
43
|
def mustache_in_stack
|
44
|
-
@stack.find { |frame| frame.is_a?(Mustache) }
|
44
|
+
@mustache_in_stack ||= @stack.find { |frame| frame.is_a?(Mustache) }
|
45
45
|
end
|
46
46
|
|
47
47
|
# Allows customization of how Mustache escapes things.
|
@@ -62,6 +62,7 @@ class Mustache
|
|
62
62
|
#
|
63
63
|
def push(new_obj)
|
64
64
|
@stack.unshift(new_obj)
|
65
|
+
@mustache_in_stack = nil
|
65
66
|
self
|
66
67
|
end
|
67
68
|
|
@@ -72,6 +73,7 @@ class Mustache
|
|
72
73
|
#
|
73
74
|
def pop
|
74
75
|
@stack.shift
|
76
|
+
@mustache_in_stack = nil
|
75
77
|
self
|
76
78
|
end
|
77
79
|
|
@@ -129,7 +131,7 @@ class Mustache
|
|
129
131
|
# @return [Object] The value of key in object if it is found, and default otherwise.
|
130
132
|
#
|
131
133
|
def find(obj, key, default = nil)
|
132
|
-
return find_in_hash(obj, key, default) if obj.respond_to?(:to_hash)
|
134
|
+
return find_in_hash(obj.to_hash, key, default) if obj.respond_to?(:to_hash)
|
133
135
|
|
134
136
|
key = to_tag(key)
|
135
137
|
return default unless obj.respond_to?(key)
|
@@ -138,6 +140,10 @@ class Mustache
|
|
138
140
|
meth.arity == 1 ? meth.to_proc : meth.call
|
139
141
|
end
|
140
142
|
|
143
|
+
def current
|
144
|
+
@stack.first
|
145
|
+
end
|
146
|
+
|
141
147
|
|
142
148
|
private
|
143
149
|
|
@@ -147,7 +153,8 @@ class Mustache
|
|
147
153
|
key.to_s.include?('-') ? key.to_s.tr('-', '_') : key
|
148
154
|
end
|
149
155
|
|
150
|
-
|
156
|
+
# Fetches a hash key if it exists, or returns the given default.
|
157
|
+
def find_in_hash(obj, key, default)
|
151
158
|
return obj[key] if obj.has_key?(key)
|
152
159
|
return obj[key.to_s] if obj.has_key?(key.to_s)
|
153
160
|
|
data/lib/mustache/generator.rb
CHANGED
data/lib/mustache/parser.rb
CHANGED
@@ -84,7 +84,6 @@ EOF
|
|
84
84
|
# the rest only allow ALLOWED_CONTENT.
|
85
85
|
ANY_CONTENT = [ '!', '=' ].map(&:freeze)
|
86
86
|
|
87
|
-
attr_reader :scanner, :result
|
88
87
|
attr_writer :otag, :ctag
|
89
88
|
|
90
89
|
# Accepts an options hash which does nothing but may be used in
|
@@ -124,7 +123,7 @@ EOF
|
|
124
123
|
|
125
124
|
if !@sections.empty?
|
126
125
|
# We have parsed the whole file, but there's still opened sections.
|
127
|
-
type, pos,
|
126
|
+
type, pos, _ = @sections.pop
|
128
127
|
error "Unclosed section #{type.inspect}", pos
|
129
128
|
end
|
130
129
|
|
@@ -135,6 +134,23 @@ EOF
|
|
135
134
|
private
|
136
135
|
|
137
136
|
|
137
|
+
def content_tags type, current_ctag
|
138
|
+
if ANY_CONTENT.include?(type)
|
139
|
+
r = /\s*#{regexp(type)}?#{regexp(current_ctag)}/
|
140
|
+
scan_until_exclusive(r)
|
141
|
+
else
|
142
|
+
@scanner.scan(ALLOWED_CONTENT)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def dispatch_based_on_type type, content, fetch, padding, pre_match_position
|
147
|
+
send("scan_tag_#{type}", content, fetch, padding, pre_match_position)
|
148
|
+
end
|
149
|
+
|
150
|
+
def find_closing_tag scanner, current_ctag
|
151
|
+
error "Unclosed tag" unless scanner.scan(regexp(current_ctag))
|
152
|
+
end
|
153
|
+
|
138
154
|
# Find {{mustaches}} and add them to the @result array.
|
139
155
|
def scan_tags
|
140
156
|
# Scan until we hit an opening delimiter.
|
@@ -161,12 +177,7 @@ EOF
|
|
161
177
|
|
162
178
|
# ANY_CONTENT tags allow any character inside of them, while
|
163
179
|
# other tags (such as variables) are more strict.
|
164
|
-
content =
|
165
|
-
r = /\s*#{regexp(type)}?#{regexp(current_ctag)}/
|
166
|
-
scan_until_exclusive(r)
|
167
|
-
else
|
168
|
-
@scanner.scan(ALLOWED_CONTENT)
|
169
|
-
end
|
180
|
+
content = content_tags(type, current_ctag)
|
170
181
|
|
171
182
|
# We found {{ but we can't figure out what's going on inside.
|
172
183
|
error "Illegal content in tag" if content.empty?
|
@@ -174,14 +185,7 @@ EOF
|
|
174
185
|
fetch = [:mustache, :fetch, content.split('.')]
|
175
186
|
prev = @result
|
176
187
|
|
177
|
-
|
178
|
-
if type
|
179
|
-
# Method#call proves much faster than using send
|
180
|
-
method("scan_tag_#{type}").
|
181
|
-
call(content, fetch, padding, pre_match_position)
|
182
|
-
else
|
183
|
-
@result << [:mustache, :etag, fetch, offset]
|
184
|
-
end
|
188
|
+
dispatch_based_on_type(type, content, fetch, padding, pre_match_position)
|
185
189
|
|
186
190
|
# The closing } in unescaped tags is just a hack for
|
187
191
|
# aesthetics.
|
@@ -192,10 +196,7 @@ EOF
|
|
192
196
|
@scanner.skip(/\s+/)
|
193
197
|
@scanner.skip(regexp(type)) if type
|
194
198
|
|
195
|
-
|
196
|
-
unless close = @scanner.scan(regexp(current_ctag))
|
197
|
-
error "Unclosed tag"
|
198
|
-
end
|
199
|
+
find_closing_tag(@scanner, current_ctag)
|
199
200
|
|
200
201
|
# If this tag was the only non-whitespace content on this line, strip
|
201
202
|
# the remaining whitespace. If not, but we've been hanging on to padding
|
@@ -282,6 +283,13 @@ EOF
|
|
282
283
|
#
|
283
284
|
|
284
285
|
|
286
|
+
# This function handles the cases where the scanned tag does not have
|
287
|
+
# a type.
|
288
|
+
def scan_tag_ content, fetch, padding, pre_match_position
|
289
|
+
@result << [:mustache, :etag, fetch, offset]
|
290
|
+
end
|
291
|
+
|
292
|
+
|
285
293
|
def scan_tag_block content, fetch, padding, pre_match_position
|
286
294
|
block = [:multi]
|
287
295
|
@result << [:mustache, :section, fetch, offset, block]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Mustache
|
2
|
+
module Utils
|
3
|
+
class String
|
4
|
+
def initialize string
|
5
|
+
@string = string
|
6
|
+
end
|
7
|
+
|
8
|
+
def classify
|
9
|
+
@string.split('/').map do |namespace|
|
10
|
+
namespace.split(/[-_]/).map do |part|
|
11
|
+
part[0] = part.chars.first.upcase
|
12
|
+
part
|
13
|
+
end.join
|
14
|
+
end.join('::')
|
15
|
+
end
|
16
|
+
|
17
|
+
def underscore(view_namespace)
|
18
|
+
@string
|
19
|
+
.dup
|
20
|
+
.split("#{view_namespace}::")
|
21
|
+
.last
|
22
|
+
.split('::')
|
23
|
+
.map do |part|
|
24
|
+
part[0] = part[0].downcase
|
25
|
+
part.gsub(/[A-Z]/) { |s| "_" << s.downcase }
|
26
|
+
end
|
27
|
+
.join('/')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mustache/version.rb
CHANGED
data/test/mustache_test.rb
CHANGED
@@ -278,14 +278,19 @@ data
|
|
278
278
|
RailsEnv production
|
279
279
|
</VirtualHost>
|
280
280
|
data
|
281
|
-
old_path
|
282
|
-
old_extension
|
281
|
+
old_path = Mustache.template_path
|
282
|
+
old_extension = Mustache.template_extension
|
283
283
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
284
|
+
begin
|
285
|
+
Mustache.template_path = File.dirname(__FILE__) + "/fixtures"
|
286
|
+
Mustache.template_extension = "conf"
|
287
|
+
|
288
|
+
assert_equal expected, Mustache.render(:passenger, :stage => 'production',
|
289
|
+
:server => 'example.com',
|
290
|
+
:deploy_to => '/var/www/example.com')
|
291
|
+
ensure
|
292
|
+
Mustache.template_path, Mustache.template_extension = old_path, old_extension
|
293
|
+
end
|
289
294
|
end
|
290
295
|
|
291
296
|
def test_doesnt_execute_what_it_doesnt_need_to
|
@@ -739,4 +744,20 @@ FROM
|
|
739
744
|
DUMMY1
|
740
745
|
template
|
741
746
|
end
|
747
|
+
|
748
|
+
def test_cast_to_hash_in_context
|
749
|
+
hashlike = Object.new
|
750
|
+
def hashlike.title
|
751
|
+
'title'
|
752
|
+
end
|
753
|
+
def hashlike.to_hash
|
754
|
+
{ title: 'title' }
|
755
|
+
end
|
756
|
+
|
757
|
+
template = '%%{{title}}%%'
|
758
|
+
|
759
|
+
assert_equal '%%title%%', Mustache.render(template, hashlike)
|
760
|
+
|
761
|
+
end
|
762
|
+
|
742
763
|
end
|
data/test/spec_test.rb
CHANGED
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: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wanstrath
|
@@ -11,90 +11,113 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-02-23 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
|
-
- -
|
20
|
+
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '1.6'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '1.6'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
|
-
- -
|
34
|
+
- - ~>
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '10.3'
|
37
37
|
type: :development
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '10.3'
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
45
|
name: minitest
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
|
-
- -
|
48
|
+
- - ~>
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '5.4'
|
51
51
|
type: :development
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- -
|
55
|
+
- - ~>
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: '5.4'
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: benchmark-ips
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
58
72
|
- !ruby/object:Gem::Dependency
|
59
73
|
name: rdoc
|
60
74
|
requirement: !ruby/object:Gem::Requirement
|
61
75
|
requirements:
|
62
|
-
- -
|
76
|
+
- - ~>
|
63
77
|
- !ruby/object:Gem::Version
|
64
78
|
version: '4.1'
|
65
79
|
type: :development
|
66
80
|
prerelease: false
|
67
81
|
version_requirements: !ruby/object:Gem::Requirement
|
68
82
|
requirements:
|
69
|
-
- -
|
83
|
+
- - ~>
|
70
84
|
- !ruby/object:Gem::Version
|
71
85
|
version: '4.1'
|
72
86
|
- !ruby/object:Gem::Dependency
|
73
87
|
name: ronn
|
74
88
|
requirement: !ruby/object:Gem::Requirement
|
75
89
|
requirements:
|
76
|
-
- -
|
90
|
+
- - ~>
|
77
91
|
- !ruby/object:Gem::Version
|
78
92
|
version: '0.7'
|
79
93
|
type: :development
|
80
94
|
prerelease: false
|
81
95
|
version_requirements: !ruby/object:Gem::Requirement
|
82
96
|
requirements:
|
83
|
-
- -
|
97
|
+
- - ~>
|
84
98
|
- !ruby/object:Gem::Version
|
85
99
|
version: '0.7'
|
86
|
-
description:
|
87
|
-
|
100
|
+
description: ! 'Inspired by ctemplate, Mustache is a framework-agnostic way to render
|
101
|
+
|
88
102
|
logic-free views.
|
89
103
|
|
104
|
+
|
90
105
|
As ctemplates says, "It emphasizes separating logic from presentation:
|
106
|
+
|
91
107
|
it is impossible to embed application logic in this template
|
108
|
+
|
92
109
|
language.
|
93
110
|
|
111
|
+
|
94
112
|
Think of Mustache as a replacement for your views. Instead of views
|
113
|
+
|
95
114
|
consisting of ERB or HAML with random helpers and arbitrary logic,
|
115
|
+
|
96
116
|
your views are broken into two parts: a Ruby class and an HTML
|
117
|
+
|
97
118
|
template.
|
119
|
+
|
120
|
+
'
|
98
121
|
email: rokusu@gmail.com
|
99
122
|
executables:
|
100
123
|
- mustache
|
@@ -113,6 +136,7 @@ files:
|
|
113
136
|
- lib/mustache/parser.rb
|
114
137
|
- lib/mustache/settings.rb
|
115
138
|
- lib/mustache/template.rb
|
139
|
+
- lib/mustache/utils.rb
|
116
140
|
- lib/mustache/version.rb
|
117
141
|
- man/mustache.1
|
118
142
|
- man/mustache.1.html
|
@@ -181,19 +205,18 @@ require_paths:
|
|
181
205
|
- lib
|
182
206
|
required_ruby_version: !ruby/object:Gem::Requirement
|
183
207
|
requirements:
|
184
|
-
- -
|
208
|
+
- - ~>
|
185
209
|
- !ruby/object:Gem::Version
|
186
210
|
version: '2.0'
|
187
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
212
|
requirements:
|
189
|
-
- -
|
213
|
+
- - ! '>='
|
190
214
|
- !ruby/object:Gem::Version
|
191
215
|
version: '0'
|
192
216
|
requirements: []
|
193
217
|
rubyforge_project:
|
194
|
-
rubygems_version: 2.
|
218
|
+
rubygems_version: 2.4.5
|
195
219
|
signing_key:
|
196
220
|
specification_version: 4
|
197
221
|
summary: Mustache is a framework-agnostic way to render logic-free views.
|
198
222
|
test_files: []
|
199
|
-
has_rdoc:
|