handlebar 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +40 -1
- data/VERSION +1 -1
- data/handlebar.gemspec +2 -2
- data/lib/handlebar/support.rb +1 -1
- data/lib/handlebar/template.rb +50 -27
- data/test/test_handlebar_template.rb +39 -3
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -1,6 +1,45 @@
|
|
1
1
|
= handlebar
|
2
2
|
|
3
|
-
A simple text
|
3
|
+
A simple text template system for generating output for a variety of uses
|
4
|
+
including plain-text, HTML, and JavaScript.
|
5
|
+
|
6
|
+
== Examples
|
7
|
+
|
8
|
+
The straight-forward usage is substitutions:
|
9
|
+
|
10
|
+
template = Handlebar::Template.new("This {{noun}} is {{adjective}}")
|
11
|
+
|
12
|
+
template.render(:noun => 'shoe', :adjective => 'red')
|
13
|
+
# => "This shoe is red"
|
14
|
+
|
15
|
+
If required, the content can be HTML-escaped automatically:
|
16
|
+
|
17
|
+
template = Handlebar::Template.new(
|
18
|
+
"This {{noun}} is {{adjective}}",
|
19
|
+
:escape => :html
|
20
|
+
)
|
21
|
+
|
22
|
+
template.render(:noun => 'goose', :adjective => '<em>blue</em>')
|
23
|
+
# => "This goose is <em>blue</em>"
|
24
|
+
|
25
|
+
This can also be engaged on a case-by-case basis:
|
26
|
+
|
27
|
+
template = Handlebar::Template.new("This {{&noun}} is {{adjective}}")
|
28
|
+
|
29
|
+
template.render(:noun => '<b>goose</b>', :adjective => '<em>blue</em>')
|
30
|
+
# => "This <b>goose</b> is <em>blue</em>"
|
31
|
+
|
32
|
+
Also available is URI encoding for links:
|
33
|
+
|
34
|
+
template = Handlebar::Template.new(
|
35
|
+
"<a href='/home?user_id={{%user_id}}'>{{&label}}</a>"
|
36
|
+
)
|
37
|
+
|
38
|
+
template.render(:user_id => 'joe&2', :label => 'Joe&2')
|
39
|
+
# => "<a href='/home?user_id=joe%262'>Joe&2</a>"
|
40
|
+
|
41
|
+
A number of other usage cases are described in test/test_handlebar_template.rb
|
42
|
+
as a reference.
|
4
43
|
|
5
44
|
== Copyright
|
6
45
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/handlebar.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{handlebar}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Scott Tadman"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-07-04}
|
13
13
|
s.description = %q{A simple text templating system}
|
14
14
|
s.email = %q{github@tadman.ca}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/handlebar/support.rb
CHANGED
data/lib/handlebar/template.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Handlebar::Template
|
2
2
|
# == Constants ============================================================
|
3
3
|
|
4
|
-
TOKEN_REGEXP = /((?:[^\{]|\{[^\{]|\{\{\{)+)|\{\{\s*([
|
4
|
+
TOKEN_REGEXP = /((?:[^\{]|\{[^\{]|\{\{\{)+)|\{\{\s*([\&\%\$\.\:\*\/\=]|\?\!?)?([^\}]*)\}\}/.freeze
|
5
5
|
TOKEN_TRIGGER = /\{\{/.freeze
|
6
6
|
|
7
7
|
# == Utility Classes ======================================================
|
@@ -66,7 +66,11 @@ class Handlebar::Template
|
|
66
66
|
when Array
|
67
67
|
variables
|
68
68
|
when Hash
|
69
|
-
|
69
|
+
_variables = variables
|
70
|
+
|
71
|
+
Hash.new do |h, k|
|
72
|
+
h[k] = (_variables[k.to_sym] || _variables[k.to_s])
|
73
|
+
end
|
70
74
|
else
|
71
75
|
[ variables ]
|
72
76
|
end
|
@@ -75,21 +79,20 @@ class Handlebar::Template
|
|
75
79
|
# Unless the template options have already been processed, mapping
|
76
80
|
# will need to be performed.
|
77
81
|
unless (templates.is_a?(TemplateHash))
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
]
|
82
|
+
_templates = templates
|
83
|
+
templates = TemplateHash.new do |h, k|
|
84
|
+
v = _templates[k]
|
85
|
+
|
86
|
+
h[k] =
|
87
|
+
case (v)
|
88
|
+
when Handlebar::Template, Proc, Array
|
89
|
+
v
|
90
|
+
when TOKEN_TRIGGER
|
91
|
+
self.class.new(v, :escape => @escape_method)
|
92
|
+
else
|
93
|
+
v.to_s
|
94
|
+
end
|
95
|
+
end
|
93
96
|
end
|
94
97
|
else
|
95
98
|
templates = TemplateHash.new
|
@@ -100,6 +103,11 @@ class Handlebar::Template
|
|
100
103
|
when Array
|
101
104
|
_parents = parents.dup
|
102
105
|
_parent = _parents.shift
|
106
|
+
|
107
|
+
unless (_parent.is_a?(Handlebar::Template))
|
108
|
+
_parent = self.class.new(_parent, :escape => @escape_method)
|
109
|
+
end
|
110
|
+
|
103
111
|
_parent.render(
|
104
112
|
variables,
|
105
113
|
templates.merge(
|
@@ -114,6 +122,21 @@ class Handlebar::Template
|
|
114
122
|
nil => self.to_proc.call(variables, templates)
|
115
123
|
)
|
116
124
|
)
|
125
|
+
when String
|
126
|
+
_parent = parents
|
127
|
+
|
128
|
+
unless (_parent.is_a?(Handlebar::Template))
|
129
|
+
_parent = self.class.new(_parent, :escape => @escape_method)
|
130
|
+
end
|
131
|
+
|
132
|
+
_parent.render(
|
133
|
+
variables,
|
134
|
+
templates.merge(
|
135
|
+
nil => self.to_proc.call(variables, templates)
|
136
|
+
)
|
137
|
+
)
|
138
|
+
else
|
139
|
+
raise ArgumentError, "Invalid options passed in to parents"
|
117
140
|
end
|
118
141
|
else
|
119
142
|
self.to_proc.call(variables, templates)
|
@@ -141,7 +164,7 @@ class Handlebar::Template
|
|
141
164
|
tag = tag.empty? ? nil : tag.to_sym
|
142
165
|
|
143
166
|
case (tag_type)
|
144
|
-
when
|
167
|
+
when '&'
|
145
168
|
# HTML escaped
|
146
169
|
index = stack[-1][2][tag.inspect]
|
147
170
|
|
@@ -149,28 +172,28 @@ class Handlebar::Template
|
|
149
172
|
|
150
173
|
variables and variables[tag] = true
|
151
174
|
|
152
|
-
when
|
175
|
+
when '%'
|
153
176
|
# URI escaped
|
154
177
|
index = stack[-1][2][tag.inspect]
|
155
178
|
|
156
179
|
source and source << "v&&r<<h.uri_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
157
180
|
|
158
181
|
variables and variables[tag] = true
|
159
|
-
when
|
182
|
+
when '$'
|
160
183
|
# JavaScript escaped
|
161
184
|
index = stack[-1][2][tag.inspect]
|
162
185
|
|
163
186
|
source and source << "v&&r<<h.js_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
164
187
|
|
165
188
|
variables and variables[tag] = true
|
166
|
-
when
|
189
|
+
when '.'
|
167
190
|
# CSS escaped
|
168
191
|
index = stack[-1][2][tag.inspect]
|
169
192
|
|
170
193
|
source and source << "v&&r<<h.css_escape(v.is_a?(Array)?v[#{index}]:v[#{tag.inspect}]);"
|
171
194
|
|
172
195
|
variables and variables[tag] = true
|
173
|
-
when
|
196
|
+
when ':'
|
174
197
|
# Defines start of a :section
|
175
198
|
index = stack[-1][2][tag.inspect]
|
176
199
|
|
@@ -181,7 +204,7 @@ class Handlebar::Template
|
|
181
204
|
source and source << "h.iterate(v){|v|;v=h.cast_as_vars(v, s);"
|
182
205
|
|
183
206
|
sections and sections[tag] = true
|
184
|
-
when
|
207
|
+
when '?', '?!'
|
185
208
|
# Defines start of a ?conditional
|
186
209
|
|
187
210
|
stack[-1][2][tag.inspect]
|
@@ -189,14 +212,14 @@ class Handlebar::Template
|
|
189
212
|
# The stack will inherit the variable assignment locations from the
|
190
213
|
# existing stack layer.
|
191
214
|
stack << [ :conditional, tag, stack[-1][2] ]
|
192
|
-
source and source << "if(v&&v.is_a?(Hash)&&v[#{tag.inspect}]);"
|
215
|
+
source and source << "#{tag_type=='?' ? 'if' : 'unless'}(v&&v.is_a?(Hash)&&v[#{tag.inspect}]);"
|
193
216
|
|
194
217
|
variables and variables[tag] = true
|
195
|
-
when
|
218
|
+
when '*'
|
196
219
|
source and source << "_t=t&&t[#{tag.inspect}];r<<(_t.respond_to?(:call)?_t.call(v,t):_t.to_s);"
|
197
220
|
|
198
221
|
templates and templates[tag] = true
|
199
|
-
when
|
222
|
+
when '/'
|
200
223
|
# Closes out a section or conditional
|
201
224
|
closed = stack.pop
|
202
225
|
|
@@ -212,7 +235,7 @@ class Handlebar::Template
|
|
212
235
|
when :base
|
213
236
|
raise ParseError, "Unexpected {{#{tag}}}, too many tags closed"
|
214
237
|
end
|
215
|
-
when
|
238
|
+
when '='
|
216
239
|
# Literal insertion
|
217
240
|
index = stack[-1][2][tag.inspect]
|
218
241
|
|
@@ -33,6 +33,12 @@ class TestHandlebarTemplate < Test::Unit::TestCase
|
|
33
33
|
assert_equal 'false', template.render
|
34
34
|
assert_equal 'true false', template.render(:boolean => true)
|
35
35
|
assert_equal 'false', template.render(:boolean => false)
|
36
|
+
|
37
|
+
template = Handlebar::Template.new('{{?boolean}}true{{/}}{{?!boolean}}false{{/}}')
|
38
|
+
|
39
|
+
assert_equal 'false', template.render
|
40
|
+
assert_equal 'true', template.render(:boolean => true)
|
41
|
+
assert_equal 'false', template.render(:boolean => false)
|
36
42
|
end
|
37
43
|
|
38
44
|
def test_sectioned_templates
|
@@ -71,6 +77,26 @@ class TestHandlebarTemplate < Test::Unit::TestCase
|
|
71
77
|
assert_equal 'child', template.render(nil, { :example => '{{*parent}}', :parent => 'child' }.freeze)
|
72
78
|
end
|
73
79
|
|
80
|
+
def test_dynamic_variables
|
81
|
+
template = Handlebar::Template.new('{{example}}{{text}}', :escape => :html)
|
82
|
+
|
83
|
+
generator = Hash.new do |h, k|
|
84
|
+
h[k] = "<#{k}>"
|
85
|
+
end
|
86
|
+
|
87
|
+
assert_equal '<example><text>', template.render(generator)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_dynamic_templates
|
91
|
+
template = Handlebar::Template.new('<{{*example}}>', :escape => :html)
|
92
|
+
|
93
|
+
generator = Hash.new do |h, k|
|
94
|
+
h[k] = k.to_s.upcase
|
95
|
+
end
|
96
|
+
|
97
|
+
assert_equal '<EXAMPLE>', template.render(nil, generator)
|
98
|
+
end
|
99
|
+
|
74
100
|
def test_missing_templates
|
75
101
|
template = Handlebar::Template.new('{{*example}}', :escape => :html)
|
76
102
|
|
@@ -86,9 +112,9 @@ class TestHandlebarTemplate < Test::Unit::TestCase
|
|
86
112
|
end
|
87
113
|
|
88
114
|
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}}')
|
115
|
+
parent_template = Handlebar::Template.new('{{a}}[{{*}}]{{b}}'.freeze)
|
116
|
+
child_template = Handlebar::Template.new('{{c}}{{*}}'.freeze)
|
117
|
+
final_template = Handlebar::Template.new('{{a}}'.freeze)
|
92
118
|
|
93
119
|
variables = { :a => 'A', :b => 'B', :c => 'C' }
|
94
120
|
|
@@ -96,6 +122,16 @@ class TestHandlebarTemplate < Test::Unit::TestCase
|
|
96
122
|
assert_equal 'CA', final_template.render(variables, nil, child_template)
|
97
123
|
assert_equal 'A[CA]B', final_template.render(variables, nil, [ child_template, parent_template ].freeze)
|
98
124
|
end
|
125
|
+
|
126
|
+
def test_inline_parent_templates
|
127
|
+
template = Handlebar::Template.new('{{a}}')
|
128
|
+
|
129
|
+
variables = { :a => 'A', :b => 'B', :c => 'C' }
|
130
|
+
|
131
|
+
assert_equal 'A', template.render(variables)
|
132
|
+
assert_equal 'CA', template.render(variables, nil, '{{c}}{{*}}'.freeze)
|
133
|
+
assert_equal 'A[CA]B', template.render(variables, nil, %w[ {{c}}{{*}} {{a}}[{{*}}]{{b}} ].freeze)
|
134
|
+
end
|
99
135
|
|
100
136
|
def test_extract_variables
|
101
137
|
template = Handlebar::Template.new('{{a}}{{?b}}{{=c}}{{/b}}{{&d}}{{$e}}{{.f}}{{%g}}{{:h}}{{i}}{{/h}}')
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: handlebar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.2.
|
5
|
+
version: 0.2.1
|
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-
|
13
|
+
date: 2011-07-04 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|