handlebar 0.1.0 → 0.2.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/VERSION +1 -0
- data/handlebar.gemspec +62 -0
- data/lib/handlebar/template.rb +227 -106
- data/test/test_handlebar_template.rb +124 -30
- metadata +4 -2
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/handlebar.gemspec
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{handlebar}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Scott Tadman"]
|
12
|
+
s.date = %q{2011-06-30}
|
13
|
+
s.description = %q{A simple text templating system}
|
14
|
+
s.email = %q{github@tadman.ca}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"handlebar.gemspec",
|
28
|
+
"lib/handlebar.rb",
|
29
|
+
"lib/handlebar/support.rb",
|
30
|
+
"lib/handlebar/template.rb",
|
31
|
+
"notes/example.hb",
|
32
|
+
"test/helper.rb",
|
33
|
+
"test/test_handlebar.rb",
|
34
|
+
"test/test_handlebar_template.rb"
|
35
|
+
]
|
36
|
+
s.homepage = %q{http://github.com/twg/handlebar}
|
37
|
+
s.licenses = ["MIT"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.5.3}
|
40
|
+
s.summary = %q{Simple text tempating system}
|
41
|
+
s.test_files = [
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/test_handlebar.rb",
|
44
|
+
"test/test_handlebar_template.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
52
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
55
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
59
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/lib/handlebar/template.rb
CHANGED
@@ -4,6 +4,18 @@ class Handlebar::Template
|
|
4
4
|
TOKEN_REGEXP = /((?:[^\{]|\{[^\{]|\{\{\{)+)|\{\{\s*([\&\%\$\.\:\?\*\/\=])?([^\}]*)\}\}/.freeze
|
5
5
|
TOKEN_TRIGGER = /\{\{/.freeze
|
6
6
|
|
7
|
+
# == Utility Classes ======================================================
|
8
|
+
|
9
|
+
class TemplateHash < Hash; end
|
10
|
+
|
11
|
+
class VariableTracker < Hash
|
12
|
+
def initialize
|
13
|
+
super do |h, k|
|
14
|
+
h[k] = h.length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
7
19
|
# == Exceptions ===========================================================
|
8
20
|
|
9
21
|
class ParseError < Exception ; end
|
@@ -15,8 +27,20 @@ class Handlebar::Template
|
|
15
27
|
|
16
28
|
# == Instance Methods =====================================================
|
17
29
|
|
18
|
-
def initialize(content,
|
19
|
-
|
30
|
+
def initialize(content, options = nil)
|
31
|
+
if (options)
|
32
|
+
if (source = options[:escape])
|
33
|
+
case (source.to_sym)
|
34
|
+
when :html, :html_escape
|
35
|
+
@escape_method = :html_escape
|
36
|
+
when :text, nil
|
37
|
+
# Default, ignored
|
38
|
+
else
|
39
|
+
raise ArgumentError, "Unknown escape source #{source}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
20
44
|
@content =
|
21
45
|
case (content)
|
22
46
|
when IO
|
@@ -25,102 +49,18 @@ class Handlebar::Template
|
|
25
49
|
content.to_s
|
26
50
|
end
|
27
51
|
end
|
28
|
-
|
52
|
+
|
29
53
|
def to_proc
|
30
|
-
@
|
31
|
-
|
32
|
-
variables = nil
|
33
|
-
stack = [ [ :base, nil, 0 ] ]
|
34
|
-
h = Handlebar::Support
|
35
|
-
|
36
|
-
@content.scan(TOKEN_REGEXP).each do |text, tag_type, tag|
|
37
|
-
if (text)
|
38
|
-
text = text.sub(/\{(\{\{+)/, '\1').sub(/\}(\}\}+)/, '\1')
|
39
|
-
|
40
|
-
method << "r<<#{text.inspect};"
|
41
|
-
else
|
42
|
-
tag = tag.strip
|
43
|
-
|
44
|
-
case (tag_type)
|
45
|
-
when ?&
|
46
|
-
# HTML escaped
|
47
|
-
method << "v&&r<<h.html_escape(v[#{tag.to_sym.inspect}].to_s);"
|
48
|
-
when ?%
|
49
|
-
# URI escaped
|
50
|
-
method << "v&&r<<h.uri_escape(v[#{tag.to_sym.inspect}].to_s);"
|
51
|
-
when ?$
|
52
|
-
# JavaScript escaped
|
53
|
-
method << "v&&r<<h.js_escape(v[#{tag.to_sym.inspect}].to_s);"
|
54
|
-
when ?.
|
55
|
-
# CSS escaped
|
56
|
-
method << "v&&r<<h.css_escape(v[#{tag.to_sym.inspect}].to_s);"
|
57
|
-
when ?:
|
58
|
-
# Defines start of a :section
|
59
|
-
variables ||= 's=[];'
|
60
|
-
stack << [ :section, tag, 0 ]
|
61
|
-
method << "if(v);s<<v;v=v.is_a?(Hash)&&v[#{tag.to_sym.inspect}];"
|
62
|
-
method << "h.iterate(v){|v|;v=h.cast_as_vars(v, s);"
|
63
|
-
when ??
|
64
|
-
# Defines start of a ?conditional
|
65
|
-
stack << [ :conditional, tag ]
|
66
|
-
method << "if(v&&v.is_a?(Hash)&&v[#{tag.to_sym.inspect}]);"
|
67
|
-
when ?*
|
68
|
-
method << "_t=t&&t[#{tag.to_sym.inspect}];r<<(_t.respond_to?(:call)?_t.call(v,t):_t.to_s);"
|
69
|
-
when ?/
|
70
|
-
# Closes out a section or conditional
|
71
|
-
closed = stack.pop
|
72
|
-
|
73
|
-
case (closed[0])
|
74
|
-
when :section
|
75
|
-
unless (tag == closed[1] or tag.empty?)
|
76
|
-
raise ParseError, "Template contains unexpected {{#{tag}}}, expected {{#{closed[1]}}}"
|
77
|
-
end
|
54
|
+
@_proc ||= begin
|
55
|
+
source = ''
|
78
56
|
|
79
|
-
|
80
|
-
when :conditional
|
81
|
-
method << "end;"
|
82
|
-
when :base
|
83
|
-
raise ParseError, "Unexpected {{#{tag}}}, too many tags closed"
|
84
|
-
end
|
85
|
-
when ?=
|
86
|
-
# Literal insertion
|
87
|
-
method << "v&&r<<(v.is_a?(Array)?v[#{stack[-1][2]}]:v[#{tag.to_sym.inspect}]).to_s;"
|
88
|
-
|
89
|
-
stack[-1][2] += 1
|
90
|
-
else
|
91
|
-
# Contextual insertion
|
92
|
-
subst = "v.is_a?(Array)?v[#{stack[-1][2]}]:v[#{tag.to_sym.inspect}]"
|
93
|
-
|
94
|
-
if (@context)
|
95
|
-
method << "v&&r<<h.#{@context}_escape(#{subst}.to_s);"
|
96
|
-
else
|
97
|
-
method << "v&&r<<(#{subst}).to_s;"
|
98
|
-
end
|
99
|
-
|
100
|
-
stack[-1][2] += 1
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
unless (stack.length == 1)
|
106
|
-
case (stack[1][0])
|
107
|
-
when :section
|
108
|
-
raise ParseError, "Unclosed {{:#{stack[1][1]}}} in template"
|
109
|
-
when :conditional
|
110
|
-
raise ParseError, "Unclosed {{?#{stack[1][1]}}} in template"
|
111
|
-
else
|
112
|
-
raise ParseError, "Unclosed {{#{stack[1][1]}}} in template"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
c = false
|
117
|
-
source = "lambda{|v,t|raise RecursionError if(c);c=true;#{variables}r='';#{method}c=false;r}"
|
57
|
+
self.compile(:source => source, :escape_method => @escape_method)
|
118
58
|
|
119
59
|
eval(source)
|
120
60
|
end
|
121
61
|
end
|
122
62
|
|
123
|
-
def
|
63
|
+
def render(variables = nil, templates = nil, parents = nil)
|
124
64
|
variables =
|
125
65
|
case (variables)
|
126
66
|
when Array
|
@@ -132,24 +72,205 @@ class Handlebar::Template
|
|
132
72
|
end
|
133
73
|
|
134
74
|
if (templates)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
75
|
+
# Unless the template options have already been processed, mapping
|
76
|
+
# will need to be performed.
|
77
|
+
unless (templates.is_a?(TemplateHash))
|
78
|
+
templates = TemplateHash[
|
79
|
+
templates.collect do |k, v|
|
80
|
+
[
|
81
|
+
k,
|
82
|
+
case (v)
|
83
|
+
when Handlebar::Template, Proc, Array
|
84
|
+
v
|
85
|
+
when TOKEN_TRIGGER
|
86
|
+
self.class.new(v, :escape => @escape_method)
|
87
|
+
else
|
88
|
+
v.to_s
|
89
|
+
end
|
90
|
+
]
|
91
|
+
end
|
92
|
+
]
|
93
|
+
end
|
94
|
+
else
|
95
|
+
templates = TemplateHash.new
|
96
|
+
end
|
97
|
+
|
98
|
+
if (parents)
|
99
|
+
case (parents)
|
100
|
+
when Array
|
101
|
+
_parents = parents.dup
|
102
|
+
_parent = _parents.shift
|
103
|
+
_parent.render(
|
104
|
+
variables,
|
105
|
+
templates.merge(
|
106
|
+
nil => self.to_proc.call(variables, templates)
|
107
|
+
),
|
108
|
+
_parents.empty? ? nil : _parents
|
109
|
+
)
|
110
|
+
when Handlebar::Template, Proc
|
111
|
+
parents.render(
|
112
|
+
variables,
|
113
|
+
templates.merge(
|
114
|
+
nil => self.to_proc.call(variables, templates)
|
115
|
+
)
|
116
|
+
)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
self.to_proc.call(variables, templates)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
alias_method :call, :render
|
123
|
+
|
124
|
+
def compile(options)
|
125
|
+
escape_method = options[:escape_method]
|
126
|
+
sections = options[:sections]
|
127
|
+
templates = options[:templates]
|
128
|
+
variables = options[:variables]
|
129
|
+
source = options[:source]
|
130
|
+
|
131
|
+
stack = [ [ :base, nil, VariableTracker.new ] ]
|
132
|
+
stack_variables = nil
|
133
|
+
|
134
|
+
@content.scan(TOKEN_REGEXP).each do |text, tag_type, tag|
|
135
|
+
if (text)
|
136
|
+
text = text.sub(/\{(\{\{+)/, '\1').sub(/\}(\}\}+)/, '\1')
|
137
|
+
|
138
|
+
source and source << "r<<#{text.inspect};"
|
139
|
+
else
|
140
|
+
tag = tag.strip
|
141
|
+
tag = tag.empty? ? nil : tag.to_sym
|
142
|
+
|
143
|
+
case (tag_type)
|
144
|
+
when ?&
|
145
|
+
# HTML escaped
|
146
|
+
index = stack[-1][2][tag.inspect]
|
147
|
+
|
148
|
+
source and source << "v&&r<<h.html_escape(v[#{tag.inspect}].to_s);"
|
149
|
+
|
150
|
+
variables and variables[tag] = true
|
151
|
+
|
152
|
+
when ?%
|
153
|
+
# URI escaped
|
154
|
+
index = stack[-1][2][tag.inspect]
|
155
|
+
|
156
|
+
source and source << "v&&r<<h.uri_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
157
|
+
|
158
|
+
variables and variables[tag] = true
|
159
|
+
when ?$
|
160
|
+
# JavaScript escaped
|
161
|
+
index = stack[-1][2][tag.inspect]
|
162
|
+
|
163
|
+
source and source << "v&&r<<h.js_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
164
|
+
|
165
|
+
variables and variables[tag] = true
|
166
|
+
when ?.
|
167
|
+
# CSS escaped
|
168
|
+
index = stack[-1][2][tag.inspect]
|
169
|
+
|
170
|
+
source and source << "v&&r<<h.css_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
171
|
+
|
172
|
+
variables and variables[tag] = true
|
173
|
+
when ?:
|
174
|
+
# Defines start of a :section
|
175
|
+
index = stack[-1][2][tag.inspect]
|
176
|
+
|
177
|
+
stack_variables ||= 's=[];'
|
178
|
+
stack << [ :section, tag, VariableTracker.new ]
|
179
|
+
|
180
|
+
source and source << "if(v);s<<v;v=v.is_a?(Array)?v[#{index}]:(v.is_a?(Hash)&&v[#{tag.inspect}]);"
|
181
|
+
source and source << "h.iterate(v){|v|;v=h.cast_as_vars(v, s);"
|
182
|
+
|
183
|
+
sections and sections[tag] = true
|
184
|
+
when ??
|
185
|
+
# Defines start of a ?conditional
|
186
|
+
|
187
|
+
stack[-1][2][tag.inspect]
|
188
|
+
|
189
|
+
# The stack will inherit the variable assignment locations from the
|
190
|
+
# existing stack layer.
|
191
|
+
stack << [ :conditional, tag, stack[-1][2] ]
|
192
|
+
source and source << "if(v&&v.is_a?(Hash)&&v[#{tag.inspect}]);"
|
193
|
+
|
194
|
+
variables and variables[tag] = true
|
195
|
+
when ?*
|
196
|
+
source and source << "_t=t&&t[#{tag.inspect}];r<<(_t.respond_to?(:call)?_t.call(v,t):_t.to_s);"
|
197
|
+
|
198
|
+
templates and templates[tag] = true
|
199
|
+
when ?/
|
200
|
+
# Closes out a section or conditional
|
201
|
+
closed = stack.pop
|
202
|
+
|
203
|
+
case (closed[0])
|
204
|
+
when :section
|
205
|
+
if (tag and tag != closed[1])
|
206
|
+
raise ParseError, "Template contains unexpected {{#{tag}}}, expected {{#{closed[1]}}}"
|
146
207
|
end
|
147
|
-
|
208
|
+
|
209
|
+
source and source << "};v=s.pop;end;"
|
210
|
+
when :conditional
|
211
|
+
source and source << "end;"
|
212
|
+
when :base
|
213
|
+
raise ParseError, "Unexpected {{#{tag}}}, too many tags closed"
|
214
|
+
end
|
215
|
+
when ?=
|
216
|
+
# Literal insertion
|
217
|
+
index = stack[-1][2][tag.inspect]
|
218
|
+
|
219
|
+
source and source << "v&&r<<(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]).to_s;"
|
220
|
+
|
221
|
+
variables and variables[tag] = true
|
222
|
+
else
|
223
|
+
# Contextual insertion
|
224
|
+
index = stack[-1][2][tag.inspect]
|
225
|
+
|
226
|
+
subst = "v.is_a?(Array)?v[#{stack[-1][2][tag.inspect]}]:v[#{tag.inspect}]"
|
227
|
+
|
228
|
+
if (escape_method)
|
229
|
+
source and source << "v&&r<<h.#{escape_method}(#{subst}.to_s);"
|
230
|
+
else
|
231
|
+
source and source << "v&&r<<(#{subst}).to_s;"
|
232
|
+
end
|
233
|
+
|
234
|
+
variables and variables[tag] = true
|
148
235
|
end
|
149
|
-
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
unless (stack.length == 1)
|
240
|
+
case (stack[1][0])
|
241
|
+
when :section
|
242
|
+
raise ParseError, "Unclosed {{:#{stack[1][1]}}} in template"
|
243
|
+
when :conditional
|
244
|
+
raise ParseError, "Unclosed {{?#{stack[1][1]}}} in template"
|
245
|
+
else
|
246
|
+
raise ParseError, "Unclosed {{#{stack[1][1]}}} in template"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
if (source)
|
251
|
+
source.replace("begin;c=false;h=Handlebar::Support;lambda{|v,t|raise RecursionError if(c);c=true;#{stack_variables}r='';#{source}c=false;r};end")
|
150
252
|
end
|
151
253
|
|
152
|
-
|
254
|
+
true
|
255
|
+
end
|
256
|
+
|
257
|
+
def to_yaml(dump)
|
258
|
+
_proc, @_proc = @_proc, nil
|
259
|
+
|
260
|
+
super(dump)
|
261
|
+
|
262
|
+
@_proc = _proc
|
263
|
+
|
264
|
+
dump
|
265
|
+
end
|
266
|
+
|
267
|
+
def marshal_dump
|
268
|
+
[ @content, { :escape => @escape_method } ]
|
269
|
+
end
|
270
|
+
|
271
|
+
def marshal_load(dump)
|
272
|
+
@content, options = dump
|
273
|
+
|
274
|
+
@escape_method = options[:escape]
|
153
275
|
end
|
154
|
-
alias_method :call, :interpret
|
155
276
|
end
|
@@ -1,85 +1,179 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
|
3
5
|
class TestHandlebarTemplate < Test::Unit::TestCase
|
4
6
|
def test_empty_template
|
5
7
|
template = Handlebar::Template.new('')
|
6
8
|
|
7
|
-
assert_equal '', template.
|
9
|
+
assert_equal '', template.render
|
8
10
|
end
|
9
11
|
|
10
12
|
def test_simple_templates
|
11
13
|
template = Handlebar::Template.new('example')
|
12
14
|
|
13
|
-
assert_equal 'example', template.
|
15
|
+
assert_equal 'example', template.render
|
14
16
|
|
15
17
|
template = Handlebar::Template.new('{{{example}}}')
|
16
18
|
|
17
|
-
assert_equal '{{example}}', template.
|
19
|
+
assert_equal '{{example}}', template.render
|
18
20
|
|
19
21
|
template = Handlebar::Template.new('example {{example}} text')
|
20
22
|
|
21
|
-
assert_equal 'example something text', template.
|
23
|
+
assert_equal 'example something text', template.render(:example => 'something')
|
22
24
|
|
23
25
|
template = Handlebar::Template.new('example {{ example }} text')
|
24
26
|
|
25
|
-
assert_equal 'example something text', template.
|
27
|
+
assert_equal 'example something text', template.render(:example => 'something')
|
26
28
|
end
|
27
29
|
|
28
30
|
def test_boolean_templates
|
29
31
|
template = Handlebar::Template.new('{{?boolean}}true {{/}}false')
|
30
32
|
|
31
|
-
assert_equal 'false', template.
|
32
|
-
assert_equal 'true false', template.
|
33
|
-
assert_equal 'false', template.
|
33
|
+
assert_equal 'false', template.render
|
34
|
+
assert_equal 'true false', template.render(:boolean => true)
|
35
|
+
assert_equal 'false', template.render(:boolean => false)
|
34
36
|
end
|
35
37
|
|
36
38
|
def test_sectioned_templates
|
37
39
|
template = Handlebar::Template.new('<head>{{:head}}<{{tag}}>{{/}}</head>')
|
38
40
|
|
39
|
-
assert_equal '<head><meta></head>', template.
|
40
|
-
assert_equal '<head><meta><link></head>', template.
|
41
|
-
assert_equal '<head><meta><link></head>', template.
|
42
|
-
assert_equal '<head></head>', template.
|
43
|
-
assert_equal '<head></head>', template.
|
44
|
-
assert_equal '<head></head>', template.
|
41
|
+
assert_equal '<head><meta></head>', template.render(:head => 'meta')
|
42
|
+
assert_equal '<head><meta><link></head>', template.render(:head => %w[ meta link ])
|
43
|
+
assert_equal '<head><meta><link></head>', template.render(:head => [ { :tag => 'meta' }, { :tag => 'link' } ])
|
44
|
+
assert_equal '<head></head>', template.render
|
45
|
+
assert_equal '<head></head>', template.render(:head => nil)
|
46
|
+
assert_equal '<head></head>', template.render(:head => [ ])
|
45
47
|
|
46
48
|
template = Handlebar::Template.new('<div>{{:link}}<a href="{{href}}" alt="{{alt}}">{{/}}</div>')
|
47
49
|
|
48
|
-
assert_equal '<div><a href="meta" alt=""></div>', template.
|
49
|
-
assert_equal '<div><a href="meta" alt="link"></div>', template.
|
50
|
-
assert_equal '<div><a href="/h" alt=""><a href="" alt="alt"><a href="/" alt="top"></div>', template.
|
51
|
-
assert_equal '<div></div>', template.
|
52
|
-
assert_equal '<div></div>', template.
|
53
|
-
assert_equal '<div></div>', template.
|
50
|
+
assert_equal '<div><a href="meta" alt=""></div>', template.render(:link => 'meta')
|
51
|
+
assert_equal '<div><a href="meta" alt="link"></div>', template.render(:link => [ %w[ meta link ] ])
|
52
|
+
assert_equal '<div><a href="/h" alt=""><a href="" alt="alt"><a href="/" alt="top"></div>', template.render(:link => [ { :href => '/h' }, { :alt => 'alt' }, { :href => '/', :alt => 'top' } ])
|
53
|
+
assert_equal '<div></div>', template.render
|
54
|
+
assert_equal '<div></div>', template.render(:link => nil)
|
55
|
+
assert_equal '<div></div>', template.render(:link => [ ])
|
54
56
|
end
|
55
57
|
|
56
58
|
def test_template_with_context
|
57
|
-
template = Handlebar::Template.new('{{example}}', :html)
|
59
|
+
template = Handlebar::Template.new('{{example}}', :escape => :html)
|
58
60
|
|
59
|
-
assert_equal '<strong>', template.
|
61
|
+
assert_equal '<strong>', template.render('<strong>')
|
60
62
|
|
61
|
-
template = Handlebar::Template.new('{{=example}}', :html)
|
63
|
+
template = Handlebar::Template.new('{{=example}}', :escape => :html)
|
62
64
|
|
63
|
-
assert_equal '<strong>', template.
|
65
|
+
assert_equal '<strong>', template.render('<strong>')
|
64
66
|
end
|
65
67
|
|
66
68
|
def test_recursive_templates
|
67
|
-
template = Handlebar::Template.new('{{*example}}', :html)
|
69
|
+
template = Handlebar::Template.new('{{*example}}', :escape => :html)
|
68
70
|
|
69
|
-
assert_equal 'child', template.
|
71
|
+
assert_equal 'child', template.render(nil, { :example => '{{*parent}}', :parent => 'child' }.freeze)
|
70
72
|
end
|
71
73
|
|
72
74
|
def test_missing_templates
|
73
|
-
template = Handlebar::Template.new('{{*example}}', :html)
|
75
|
+
template = Handlebar::Template.new('{{*example}}', :escape => :html)
|
74
76
|
|
75
|
-
assert_equal '', template.
|
77
|
+
assert_equal '', template.render(nil, { })
|
76
78
|
end
|
77
79
|
|
78
80
|
def test_recursive_circular_templates
|
79
|
-
template = Handlebar::Template.new('{{*reference}}', :html)
|
81
|
+
template = Handlebar::Template.new('{{*reference}}', :escape => :html)
|
80
82
|
|
81
83
|
assert_exception Handlebar::Template::RecursionError do
|
82
|
-
template.
|
84
|
+
template.render(nil, { :reference => '{{*backreference}}', :backreference => '{{*reference}}' }.freeze)
|
83
85
|
end
|
84
86
|
end
|
87
|
+
|
88
|
+
def test_parent_templates
|
89
|
+
parent_template = Handlebar::Template.new('{{a}}[{{*}}]{{b}}')
|
90
|
+
child_template = Handlebar::Template.new('{{c}}{{*}}')
|
91
|
+
final_template = Handlebar::Template.new('{{a}}')
|
92
|
+
|
93
|
+
variables = { :a => 'A', :b => 'B', :c => 'C' }
|
94
|
+
|
95
|
+
assert_equal 'A', final_template.render(variables)
|
96
|
+
assert_equal 'CA', final_template.render(variables, nil, child_template)
|
97
|
+
assert_equal 'A[CA]B', final_template.render(variables, nil, [ child_template, parent_template ].freeze)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_extract_variables
|
101
|
+
template = Handlebar::Template.new('{{a}}{{?b}}{{=c}}{{/b}}{{&d}}{{$e}}{{.f}}{{%g}}{{:h}}{{i}}{{/h}}')
|
102
|
+
|
103
|
+
variables = { }
|
104
|
+
sections = { }
|
105
|
+
templates = { }
|
106
|
+
|
107
|
+
template.compile(
|
108
|
+
:variables => variables,
|
109
|
+
:sections => sections,
|
110
|
+
:templates => templates
|
111
|
+
)
|
112
|
+
|
113
|
+
assert_equal [ :a, :b, :c, :d, :e, :f, :g, :i ], variables.keys.sort_by(&:to_s)
|
114
|
+
assert_equal [ :h ], sections.keys.sort_by(&:to_s)
|
115
|
+
assert_equal [ ], templates.keys.sort_by(&:to_s)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_chain_extract_variables
|
119
|
+
template = Handlebar::Template.new('{{a}}{{?b}}{{=c}}{{/b}}{{&d}}{{$e}}{{.f}}{{%g}}{{:h}}{{i}}{{/h}}')
|
120
|
+
|
121
|
+
variables = { :x => true }
|
122
|
+
sections = { :y => true }
|
123
|
+
templates = { :z => true }
|
124
|
+
|
125
|
+
template.compile(
|
126
|
+
:variables => variables,
|
127
|
+
:sections => sections,
|
128
|
+
:templates => templates
|
129
|
+
)
|
130
|
+
|
131
|
+
assert_equal [ :a, :b, :c, :d, :e, :f, :g, :i, :x ], variables.keys.sort_by(&:to_s)
|
132
|
+
assert_equal [ :h, :y ], sections.keys.sort_by(&:to_s)
|
133
|
+
assert_equal [ :z ], templates.keys.sort_by(&:to_s)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_variable_tracker
|
137
|
+
tracker = Handlebar::Template::VariableTracker.new
|
138
|
+
|
139
|
+
assert_equal true, tracker.empty?
|
140
|
+
assert_equal 0, tracker[:a]
|
141
|
+
assert_equal 1, tracker[:b]
|
142
|
+
assert_equal 2, tracker[:c]
|
143
|
+
assert_equal 0, tracker[:a]
|
144
|
+
assert_equal 2, tracker[:c]
|
145
|
+
assert_equal 3, tracker[:z]
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_clone
|
149
|
+
template = Handlebar::Template.new('<p>{{example}}</p>', :escape => :html)
|
150
|
+
|
151
|
+
cloned = template.clone
|
152
|
+
|
153
|
+
assert_equal '<p><strong></p>', cloned.render('<strong>')
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_serialization_with_yaml
|
157
|
+
template = Handlebar::Template.new('<p>{{example}}</p>', :escape => :html)
|
158
|
+
|
159
|
+
assert_equal '<p><strong></p>', template.render('<strong>')
|
160
|
+
|
161
|
+
serialized_template = YAML.dump(template)
|
162
|
+
|
163
|
+
deserialized_template = YAML.load(serialized_template)
|
164
|
+
|
165
|
+
assert_equal '<p><strong></p>', deserialized_template.render('<strong>')
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_serialization_with_marshal
|
169
|
+
template = Handlebar::Template.new('<p>{{example}}</p>', :escape => :html)
|
170
|
+
|
171
|
+
assert_equal '<p><strong></p>', template.render('<strong>')
|
172
|
+
|
173
|
+
serialized_template = Marshal.dump(template)
|
174
|
+
|
175
|
+
deserialized_template = Marshal.load(serialized_template)
|
176
|
+
|
177
|
+
assert_equal '<p><strong></p>', deserialized_template.render('<strong>')
|
178
|
+
end
|
85
179
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: handlebar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Scott Tadman
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-30 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -51,6 +51,8 @@ files:
|
|
51
51
|
- LICENSE.txt
|
52
52
|
- README.rdoc
|
53
53
|
- Rakefile
|
54
|
+
- VERSION
|
55
|
+
- handlebar.gemspec
|
54
56
|
- lib/handlebar.rb
|
55
57
|
- lib/handlebar/support.rb
|
56
58
|
- lib/handlebar/template.rb
|