temple 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +14 -0
- data/README.md +66 -50
- data/lib/temple/erb/parser.rb +15 -14
- data/lib/temple/erb/trimming.rb +9 -14
- data/lib/temple/generators.rb +3 -8
- data/lib/temple/html/attribute_merger.rb +5 -2
- data/lib/temple/html/fast.rb +7 -9
- data/lib/temple/html/pretty.rb +6 -5
- data/lib/temple/mixins/dispatcher.rb +96 -24
- data/lib/temple/mixins/engine_dsl.rb +15 -11
- data/lib/temple/version.rb +1 -1
- data/test/html/test_attribute_merger.rb +41 -0
- data/test/html/test_pretty.rb +1 -1
- data/test/mixins/test_dispatcher.rb +26 -0
- data/test/test_engine.rb +1 -1
- metadata +54 -45
data/CHANGES
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
master
|
2
2
|
|
3
|
+
0.3.5
|
4
|
+
|
5
|
+
* Temple::HTML::Pretty improved
|
6
|
+
* :sort_attrs option (default: true) added to HTML::AttributeMerger;
|
7
|
+
if set to false, the attributes will appear in the insertion order
|
8
|
+
* Temple::Mixins::EngineDSL api changed ("wildcard" is deprecated, use "use" instead)
|
9
|
+
* Temple::Mixins::CompiledDispatcher supports arbitrary levels now
|
10
|
+
* Don't use gsub! on incoming strings (#57)
|
11
|
+
* Fix newlines in erb parser (#46)
|
12
|
+
|
13
|
+
0.3.4
|
14
|
+
|
15
|
+
* Bugfix release (0.3.3 was yanked)
|
16
|
+
|
3
17
|
0.3.3
|
4
18
|
|
5
19
|
* Support for rails 3.1 streaming
|
data/README.md
CHANGED
@@ -35,23 +35,27 @@ Temple is built on a theory that every template consists of three elements:
|
|
35
35
|
The goal of a template engine is to take the template and eventually compile
|
36
36
|
it into *the core abstraction*:
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
```ruby
|
39
|
+
[:multi,
|
40
|
+
[:static, "Hello "],
|
41
|
+
[:dynamic, "@user.name"],
|
42
|
+
[:static, "!\n"],
|
43
|
+
[:code, "if @user.birthday == Date.today"],
|
44
|
+
[:static, "Happy birthday!"],
|
45
|
+
[:code, "end"]]
|
46
|
+
```
|
45
47
|
|
46
48
|
Then you can apply some optimizations, feed it to Temple and it generates fast
|
47
49
|
Ruby code for you:
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
```ruby
|
52
|
+
_buf = []
|
53
|
+
_buf << ("Hello #{@user.name}!\n")
|
54
|
+
if @user.birthday == Date.today
|
55
|
+
_buf << "Happy birthday!"
|
56
|
+
end
|
57
|
+
_buf.join
|
58
|
+
```
|
55
59
|
|
56
60
|
S-expression
|
57
61
|
------------
|
@@ -67,13 +71,15 @@ manipulated by computers.
|
|
67
71
|
|
68
72
|
Some examples:
|
69
73
|
|
70
|
-
|
74
|
+
```ruby
|
75
|
+
[:static, "Hello World!"]
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
77
|
+
[:multi,
|
78
|
+
[:static, "Hello "],
|
79
|
+
[:dynamic, "@world"]]
|
75
80
|
|
76
|
-
|
81
|
+
[:html, :tag, "em", [:html, :attrs], [:static, "Hey hey"]]
|
82
|
+
```
|
77
83
|
|
78
84
|
*NOTE:* SexpProcessor, a library written by Ryan Davis, includes a `Sexp`
|
79
85
|
class. While you can use this class (since it's a subclass of Array), it's not
|
@@ -89,11 +95,13 @@ do one thing at every step.
|
|
89
95
|
|
90
96
|
So what's an abstraction? An abstraction is when you introduce a new types:
|
91
97
|
|
92
|
-
|
93
|
-
|
98
|
+
```ruby
|
99
|
+
# Instead of:
|
100
|
+
[:static, "<strong>Use the force</strong>"]
|
94
101
|
|
95
|
-
|
96
|
-
|
102
|
+
# You use:
|
103
|
+
[:html, :tag, "strong", [:html, :attrs], [:static, "Use the force"]]
|
104
|
+
```
|
97
105
|
|
98
106
|
### Why are abstractions so important?
|
99
107
|
|
@@ -126,15 +134,17 @@ the argument, and it should be possible to use the same instance several times
|
|
126
134
|
While a compiler can be any object, you very often want to structure it as a
|
127
135
|
class. Temple then assumes the initializer takes an optional option hash:
|
128
136
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
137
|
+
```ruby
|
138
|
+
class MyCompiler
|
139
|
+
def initialize(options = {})
|
140
|
+
@options = options
|
141
|
+
end
|
133
142
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
143
|
+
def call(exp)
|
144
|
+
# do stuff
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
138
148
|
|
139
149
|
### Parsers
|
140
150
|
|
@@ -181,24 +191,26 @@ Engines
|
|
181
191
|
|
182
192
|
When you have a chain of a parsers, some filters and a generator you can finally create your *engine*. Temple provides {Temple::Engine} which makes this very easy:
|
183
193
|
|
184
|
-
|
185
|
-
|
186
|
-
|
194
|
+
```ruby
|
195
|
+
class MyEngine < Temple::Engine
|
196
|
+
# First run MyParser, passing the :strict option
|
197
|
+
use MyParser, :strict
|
187
198
|
|
188
|
-
|
189
|
-
|
199
|
+
# Then a custom filter
|
200
|
+
use MyFilter
|
190
201
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
202
|
+
# Then some general optimizations filters
|
203
|
+
filter :MultiFlattener
|
204
|
+
filter :StaticMerger
|
205
|
+
filter :DynamicInliner
|
195
206
|
|
196
|
-
|
197
|
-
|
198
|
-
|
207
|
+
# Finally the generator
|
208
|
+
generator :ArrayBuffer, :buffer
|
209
|
+
end
|
199
210
|
|
200
|
-
|
201
|
-
|
211
|
+
engine = MyEngine.new(:strict => "For MyParser")
|
212
|
+
engine.call(something)
|
213
|
+
```
|
202
214
|
|
203
215
|
And then?
|
204
216
|
---------
|
@@ -209,13 +221,15 @@ generator. What happens next?
|
|
209
221
|
Temple provides helpers to create template classes for [Tilt](http://github.com/rtomayko/tilt) and
|
210
222
|
Rails.
|
211
223
|
|
212
|
-
|
224
|
+
```ruby
|
225
|
+
require 'tilt'
|
213
226
|
|
214
|
-
|
215
|
-
|
227
|
+
# Create template class MyTemplate and register your file extension
|
228
|
+
MyTemplate = Temple::Templates::Tilt(MyEngine, :register_as => 'ext')
|
216
229
|
|
217
|
-
|
218
|
-
|
230
|
+
Tilt.new('example.ext').render # => Render a file
|
231
|
+
MyTemplate.new { "String" }.render # => Render a string
|
232
|
+
```
|
219
233
|
|
220
234
|
Installation
|
221
235
|
------------
|
@@ -223,7 +237,9 @@ Installation
|
|
223
237
|
You need Ruby 1.8.7 or Ruby 1.9.2 to work with Temple. Temple is published as a Ruby Gem which can be installed
|
224
238
|
as following:
|
225
239
|
|
226
|
-
|
240
|
+
```bash
|
241
|
+
$ gem install temple
|
242
|
+
```
|
227
243
|
|
228
244
|
Engines using Temple
|
229
245
|
--------------------
|
data/lib/temple/erb/parser.rb
CHANGED
@@ -6,27 +6,28 @@ module Temple
|
|
6
6
|
class Parser
|
7
7
|
include Mixins::Options
|
8
8
|
|
9
|
-
ERB_PATTERN = /(
|
10
|
-
|
11
|
-
ESCAPED = {
|
12
|
-
'<%%' => '<%',
|
13
|
-
'%%>' => '%>',
|
14
|
-
}.freeze
|
9
|
+
ERB_PATTERN = /(\n|<%%|%%>)|<%(==?|\#)?(.*?)?-?%>/m
|
15
10
|
|
16
11
|
def call(input)
|
17
12
|
result = [:multi]
|
18
13
|
pos = 0
|
19
|
-
input.scan(ERB_PATTERN) do |
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
14
|
+
input.scan(ERB_PATTERN) do |token, indicator, code|
|
15
|
+
text = input[pos...$~.begin(0)]
|
16
|
+
pos = $~.end(0)
|
17
|
+
if token
|
18
|
+
case token
|
19
|
+
when "\n"
|
20
|
+
result << [:static, "#{text}\n"] << [:newline]
|
21
|
+
when '<%%', '%%>'
|
22
|
+
result << [:static, text] unless text.empty?
|
23
|
+
token.slice!(1)
|
24
|
+
result << [:static, token]
|
25
|
+
end
|
26
26
|
else
|
27
|
+
result << [:static, text] unless text.empty?
|
27
28
|
case indicator
|
28
29
|
when '#'
|
29
|
-
code.count("\n")
|
30
|
+
result << [:code, "\n" * code.count("\n")]
|
30
31
|
when /=/
|
31
32
|
result << [:escape, indicator.size <= 1, [:dynamic, code]]
|
32
33
|
else
|
data/lib/temple/erb/trimming.rb
CHANGED
@@ -10,18 +10,17 @@ module Temple
|
|
10
10
|
def on_multi(*exps)
|
11
11
|
case options[:trim_mode]
|
12
12
|
when '>'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
i = 0
|
14
|
+
while i < exps.size
|
15
|
+
exps.delete_at(i + 1) if code?(exps[i]) && exps[i+1] == [:static, "\n"]
|
16
|
+
i += 1
|
17
17
|
end
|
18
18
|
when '<>'
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
19
|
+
i = 0
|
20
|
+
while i < exps.size
|
21
|
+
exps.delete_at(i + 1) if code?(exps[i]) && exps[i+1] == [:static, "\n"] &&
|
22
|
+
(!exps[i-1] || (exps[i-1] == [:newline]))
|
23
|
+
i += 1
|
25
24
|
end
|
26
25
|
end
|
27
26
|
[:multi, *exps]
|
@@ -32,10 +31,6 @@ module Temple
|
|
32
31
|
def code?(exp)
|
33
32
|
exp[0] == :escape || exp[0] == :code
|
34
33
|
end
|
35
|
-
|
36
|
-
def static?(exp)
|
37
|
-
exp[0] == :static
|
38
|
-
end
|
39
34
|
end
|
40
35
|
end
|
41
36
|
end
|
data/lib/temple/generators.rb
CHANGED
@@ -11,6 +11,7 @@ module Temple
|
|
11
11
|
#
|
12
12
|
# @api public
|
13
13
|
class Generator
|
14
|
+
include Mixins::CompiledDispatcher
|
14
15
|
include Mixins::Options
|
15
16
|
|
16
17
|
default_options[:buffer] = '_buf'
|
@@ -19,14 +20,8 @@ module Temple
|
|
19
20
|
[preamble, compile(exp), postamble].join('; ')
|
20
21
|
end
|
21
22
|
|
22
|
-
def
|
23
|
-
|
24
|
-
method = "on_#{type}"
|
25
|
-
if respond_to?(method)
|
26
|
-
send(method, *args)
|
27
|
-
else
|
28
|
-
raise InvalidExpression, "Generator supports only core expressions - found #{exp.inspect}"
|
29
|
-
end
|
23
|
+
def on(*exp)
|
24
|
+
raise InvalidExpression, "Generator supports only core expressions - found #{exp.inspect}"
|
30
25
|
end
|
31
26
|
|
32
27
|
def on_multi(*exp)
|
@@ -3,13 +3,14 @@ module Temple
|
|
3
3
|
# @api public
|
4
4
|
class AttributeMerger < Filter
|
5
5
|
default_options[:attr_delimiter] = {'id' => '_', 'class' => ' '}
|
6
|
+
default_options[:sort_attrs] = true
|
6
7
|
|
7
8
|
def on_html_attrs(*attrs)
|
9
|
+
names = []
|
8
10
|
result = {}
|
9
11
|
attrs.each do |attr|
|
10
12
|
raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
|
11
13
|
name, value = attr[2].to_s, attr[3]
|
12
|
-
next if empty_exp?(value)
|
13
14
|
if result[name]
|
14
15
|
delimiter = options[:attr_delimiter][name]
|
15
16
|
raise "Multiple #{name} attributes specified" unless delimiter
|
@@ -37,9 +38,11 @@ module Temple
|
|
37
38
|
end
|
38
39
|
else
|
39
40
|
result[name] = attr
|
41
|
+
names << name
|
40
42
|
end
|
41
43
|
end
|
42
|
-
[:
|
44
|
+
result = options[:sort_attrs] ? result.sort : names.map {|k| [k, result[k]] }
|
45
|
+
[:multi, *result.map {|name,attr| compile(attr) }]
|
43
46
|
end
|
44
47
|
|
45
48
|
def on_html_attr(name, value)
|
data/lib/temple/html/fast.rb
CHANGED
@@ -43,21 +43,19 @@ module Temple
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def on_html_doctype(type)
|
46
|
-
type = type.to_s
|
47
|
-
trailing_newlines = type[/(\A|[^\r])(\n+)\Z/, 2].to_s
|
48
|
-
text = type.downcase.strip
|
46
|
+
type = type.to_s.downcase
|
49
47
|
|
50
|
-
if
|
48
|
+
if type =~ /^xml(\s+(.+))?$/
|
51
49
|
raise 'Invalid xml directive in html mode' if html?
|
52
|
-
|
53
|
-
str = "<?xml version=#{
|
50
|
+
w = options[:attr_wrapper]
|
51
|
+
str = "<?xml version=#{w}1.0#{w} encoding=#{w}#{$2 || 'utf-8'}#{w} ?>"
|
54
52
|
elsif html?
|
55
|
-
str = HTML_DOCTYPES[
|
53
|
+
str = HTML_DOCTYPES[type] || raise("Invalid html doctype #{type}")
|
56
54
|
else
|
57
|
-
str = XHTML_DOCTYPES[
|
55
|
+
str = XHTML_DOCTYPES[type] || raise("Invalid xhtml doctype #{type}")
|
58
56
|
end
|
59
57
|
|
60
|
-
[:static, str
|
58
|
+
[:static, str]
|
61
59
|
end
|
62
60
|
|
63
61
|
def on_html_comment(content)
|
data/lib/temple/html/pretty.rb
CHANGED
@@ -6,7 +6,7 @@ module Temple
|
|
6
6
|
:pretty => true,
|
7
7
|
:indent_tags => %w(article aside audio base body datalist dd div dl dt
|
8
8
|
fieldset figure footer form head h1 h2 h3 h4 h5 h6
|
9
|
-
header hgroup hr html
|
9
|
+
header hgroup hr html input li link meta nav ol p
|
10
10
|
rp rt ruby section script style table tbody td tfoot
|
11
11
|
th thead title tr ul video).freeze,
|
12
12
|
:pre_tags => %w(code pre textarea).freeze
|
@@ -25,7 +25,7 @@ module Temple
|
|
25
25
|
|
26
26
|
def on_static(content)
|
27
27
|
if @pretty
|
28
|
-
content.gsub
|
28
|
+
content = content.gsub("\n", indent) if @pre_tags !~ content
|
29
29
|
@last = content.sub!(/\r?\n\s*$/, ' ') ? nil : :noindent
|
30
30
|
end
|
31
31
|
[:static, content]
|
@@ -38,7 +38,7 @@ module Temple
|
|
38
38
|
gsub_code = if ''.respond_to?(:html_safe?)
|
39
39
|
"#{tmp} = #{tmp}.html_safe? ? #{tmp}.gsub(\"\\n\", #{indent.inspect}).html_safe : #{tmp}.gsub(\"\\n\", #{indent.inspect})"
|
40
40
|
else
|
41
|
-
"#{tmp}.gsub
|
41
|
+
"#{tmp} = #{tmp}.gsub(\"\\n\", #{indent.inspect})"
|
42
42
|
end
|
43
43
|
[:multi,
|
44
44
|
[:code, "#{tmp} = (#{code}).to_s"],
|
@@ -56,8 +56,9 @@ module Temple
|
|
56
56
|
|
57
57
|
def on_html_comment(content)
|
58
58
|
return super unless @pretty
|
59
|
+
result = [:multi, [:static, tag_indent(nil)], super]
|
59
60
|
@last = nil
|
60
|
-
|
61
|
+
result
|
61
62
|
end
|
62
63
|
|
63
64
|
def on_html_tag(name, attrs, content = nil)
|
@@ -76,8 +77,8 @@ module Temple
|
|
76
77
|
result << compile(content)
|
77
78
|
@indent -= 1
|
78
79
|
end
|
80
|
+
result << [:static, "#{content && !empty_exp?(content) ? tag_indent(name) : ''}</#{name}>"] unless closed
|
79
81
|
|
80
|
-
result << [:static, "#{tag_indent(name)}</#{name}>"] if !closed
|
81
82
|
@pretty = true
|
82
83
|
result
|
83
84
|
end
|
@@ -51,38 +51,19 @@ module Temple
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def case_statement(types)
|
55
|
-
code = "type, *args = args\ncase type\n"
|
56
|
-
types.each do |name, method|
|
57
|
-
code << "when #{name.to_sym.inspect}\n" <<
|
58
|
-
(Hash === method ? case_statement(method) : "#{method}(*args)\n")
|
59
|
-
end
|
60
|
-
code << "else\nexp\nend\n"
|
61
|
-
end
|
62
|
-
|
63
54
|
def dispatcher(exp)
|
64
55
|
replace_dispatcher(exp)
|
65
56
|
end
|
66
57
|
|
67
58
|
def replace_dispatcher(exp)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
method_types = $1.split('_')
|
72
|
-
(0...method_types.size).inject(types) do |tmp, i|
|
73
|
-
raise "Invalid temple dispatcher #{method}" unless Hash === tmp
|
74
|
-
if i == method_types.size - 1
|
75
|
-
tmp[method_types[i]] = method
|
76
|
-
else
|
77
|
-
tmp[method_types[i]] ||= {}
|
78
|
-
end
|
79
|
-
end
|
59
|
+
tree = DispatchNode.new
|
60
|
+
dispatched_methods.each do |method|
|
61
|
+
method.split('_')[1..-1].inject(tree) {|node, type| node[type.to_sym] }.method = method
|
80
62
|
end
|
81
63
|
self.class.class_eval %{
|
82
64
|
def dispatcher(exp)
|
83
65
|
if self.class == #{self.class}
|
84
|
-
|
85
|
-
#{case_statement(types)}
|
66
|
+
#{tree.compile}
|
86
67
|
else
|
87
68
|
replace_dispatcher(exp)
|
88
69
|
end
|
@@ -90,9 +71,100 @@ module Temple
|
|
90
71
|
}
|
91
72
|
dispatcher(exp)
|
92
73
|
end
|
74
|
+
|
75
|
+
def dispatched_methods
|
76
|
+
re = /^on(_[a-zA-Z0-9]+)*$/
|
77
|
+
self.methods.map(&:to_s).select(&re.method(:=~))
|
78
|
+
end
|
79
|
+
|
80
|
+
# @api private
|
81
|
+
class DispatchNode < Hash
|
82
|
+
attr_accessor :method
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
super { |hsh,key| hsh[key] = DispatchNode.new }
|
86
|
+
@method = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def compile(level = 0, parent = nil)
|
90
|
+
if empty?
|
91
|
+
if method
|
92
|
+
(' ' * level) + "#{method}(*exp[#{level}..-1])"
|
93
|
+
elsif !parent
|
94
|
+
'exp'
|
95
|
+
else
|
96
|
+
raise 'Invalid dispatcher node'
|
97
|
+
end
|
98
|
+
else
|
99
|
+
code = [(' ' * level) + "case(exp[#{level}])"]
|
100
|
+
each do |key, child|
|
101
|
+
code << (' ' * level) + "when #{key.inspect}"
|
102
|
+
code << child.compile(level + 1, parent || method)
|
103
|
+
end
|
104
|
+
if method || !parent
|
105
|
+
code << (' ' * level) + "else"
|
106
|
+
if method
|
107
|
+
code << (' ' * level) + " #{method}(*exp[#{level}..-1])"
|
108
|
+
else
|
109
|
+
code << (' ' * level) + " exp"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
code << (' ' * level) + "end"
|
113
|
+
code.join("\n")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
93
117
|
end
|
94
118
|
|
95
|
-
# @api
|
119
|
+
# @api public
|
120
|
+
#
|
121
|
+
# Implements a compatible call-method
|
122
|
+
# based on the including classe's methods.
|
123
|
+
#
|
124
|
+
# It uses every method starting with
|
125
|
+
# "on" and uses the rest of the method
|
126
|
+
# name as prefix of the expression it
|
127
|
+
# will receive. So, if a dispatcher
|
128
|
+
# has a method named "on_x", this method
|
129
|
+
# will be called with arg0,..,argN
|
130
|
+
# whenever an expression like [:x, arg0,..,argN ]
|
131
|
+
# is encountered.
|
132
|
+
#
|
133
|
+
# This works with longer prefixes, too.
|
134
|
+
# For example a method named "on_y_z"
|
135
|
+
# will be called whenever an expression
|
136
|
+
# like [:y, :z, .. ] is found. Furthermore,
|
137
|
+
# if additionally a method named "on_y"
|
138
|
+
# is present, it will be called when an
|
139
|
+
# expression starts with :y but then does
|
140
|
+
# not contain with :z. This way a
|
141
|
+
# dispatcher can implement namespaces.
|
142
|
+
#
|
143
|
+
# @note
|
144
|
+
# Processing does not reach into unknown
|
145
|
+
# expression types by default.
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# class MyAwesomeDispatch
|
149
|
+
# include Temple::Mixins::Dispatcher
|
150
|
+
# def on_awesome(thing) # keep awesome things
|
151
|
+
# return [:awesome, thing]
|
152
|
+
# end
|
153
|
+
# def on_boring(thing) # make boring things awesome
|
154
|
+
# return [:awesome, thing+" with bacon"]
|
155
|
+
# end
|
156
|
+
# def on(type,*args) # unknown stuff is boring too
|
157
|
+
# return [:awesome, 'just bacon']
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
# filter = MyAwesomeDispatch.new
|
161
|
+
# # Boring things are converted:
|
162
|
+
# filter.call([:boring, 'egg']) #=> [:awesome, 'egg with bacon']
|
163
|
+
# # Unknown things too:
|
164
|
+
# filter.call([:foo]) #=> [:awesome, 'just bacon']
|
165
|
+
# # Known but not boring things won't be touched:
|
166
|
+
# filter.call([:awesome, 'chuck norris']) #=>[:awesome, 'chuck norris']
|
167
|
+
#
|
96
168
|
module Dispatcher
|
97
169
|
include CompiledDispatcher
|
98
170
|
include CoreDispatcher
|
@@ -28,6 +28,16 @@ module Temple
|
|
28
28
|
|
29
29
|
alias use append
|
30
30
|
|
31
|
+
# DEPRECATED!
|
32
|
+
#
|
33
|
+
# wildcard(:FilterName) { FilterClass.new(options) }
|
34
|
+
#
|
35
|
+
# is replaced by
|
36
|
+
#
|
37
|
+
# use(:FilterName) { FilterClass.new(options) }
|
38
|
+
#
|
39
|
+
alias wildcard use
|
40
|
+
|
31
41
|
def before(name, *args, &block)
|
32
42
|
name = Class === name ? name.name.to_sym : name
|
33
43
|
raise(ArgumentError, 'First argument must be Class or Symbol') unless Symbol === name
|
@@ -78,18 +88,12 @@ module Temple
|
|
78
88
|
chain_modified!
|
79
89
|
end
|
80
90
|
|
81
|
-
def filter(name, *options
|
82
|
-
use(name, Temple::Filters.const_get(name), *options
|
83
|
-
end
|
84
|
-
|
85
|
-
def generator(name, *options, &block)
|
86
|
-
use(name, Temple::Generators.const_get(name), *options, &block)
|
91
|
+
def filter(name, *options)
|
92
|
+
use(name, Temple::Filters.const_get(name), *options)
|
87
93
|
end
|
88
94
|
|
89
|
-
def
|
90
|
-
|
91
|
-
chain << [name, define_chain_method("WILDCARD #{name}", block)]
|
92
|
-
chain_modified!
|
95
|
+
def generator(name, *options)
|
96
|
+
use(name, Temple::Generators.const_get(name), *options)
|
93
97
|
end
|
94
98
|
|
95
99
|
private
|
@@ -125,7 +129,7 @@ module Temple
|
|
125
129
|
# The proc is converted to a method of the engine class.
|
126
130
|
# The proc can then access the option hash of the engine.
|
127
131
|
raise(ArgumentError, 'Too many arguments') unless args.empty?
|
128
|
-
raise(ArgumentError, 'Proc or blocks must have arity 1')
|
132
|
+
raise(ArgumentError, 'Proc or blocks must have arity 0 or 1') if filter.arity > 1
|
129
133
|
[name, define_chain_method("FILTER #{name}", filter)]
|
130
134
|
when Class
|
131
135
|
# Class argument (e.g Filter class)
|
data/lib/temple/version.rb
CHANGED
@@ -3,6 +3,7 @@ require 'helper'
|
|
3
3
|
describe Temple::HTML::AttributeMerger do
|
4
4
|
before do
|
5
5
|
@merger = Temple::HTML::AttributeMerger.new
|
6
|
+
@ordered_merger = Temple::HTML::AttributeMerger.new :sort_attrs => false
|
6
7
|
end
|
7
8
|
|
8
9
|
it 'should pass static attributes through' do
|
@@ -16,6 +17,46 @@ describe Temple::HTML::AttributeMerger do
|
|
16
17
|
[:content]]
|
17
18
|
end
|
18
19
|
|
20
|
+
it 'should sort html attributes by name by default, when :sort_attrs is true' do
|
21
|
+
@merger.call([:html, :tag,
|
22
|
+
'meta',
|
23
|
+
[:html, :attrs, [:html, :attr, 'c', [:static, '1']],
|
24
|
+
[:html, :attr, 'd', [:static, '2']],
|
25
|
+
[:html, :attr, 'a', [:static, '3']],
|
26
|
+
[:html, :attr, 'b', [:static, '4']]]
|
27
|
+
]).should.equal [:html, :tag, 'meta',
|
28
|
+
[:multi,
|
29
|
+
[:html, :attr, 'a', [:static, '3']],
|
30
|
+
[:html, :attr, 'b', [:static, '4']],
|
31
|
+
[:html, :attr, 'c', [:static, '1']],
|
32
|
+
[:html, :attr, 'd', [:static, '2']]]]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should preserve the order of html attributes when :sort_attrs is false' do
|
36
|
+
@ordered_merger.call([:html, :tag,
|
37
|
+
'meta',
|
38
|
+
[:html, :attrs, [:html, :attr, 'c', [:static, '1']],
|
39
|
+
[:html, :attr, 'd', [:static, '2']],
|
40
|
+
[:html, :attr, 'a', [:static, '3']],
|
41
|
+
[:html, :attr, 'b', [:static, '4']]]
|
42
|
+
]).should.equal [:html, :tag, 'meta',
|
43
|
+
[:multi,
|
44
|
+
[:html, :attr, 'c', [:static, '1']],
|
45
|
+
[:html, :attr, 'd', [:static, '2']],
|
46
|
+
[:html, :attr, 'a', [:static, '3']],
|
47
|
+
[:html, :attr, 'b', [:static, '4']]]]
|
48
|
+
|
49
|
+
# Use case:
|
50
|
+
@ordered_merger.call([:html, :tag,
|
51
|
+
'meta',
|
52
|
+
[:html, :attrs, [:html, :attr, 'http-equiv', [:static, 'Content-Type']],
|
53
|
+
[:html, :attr, 'content', [:static, '']]]
|
54
|
+
]).should.equal [:html, :tag, 'meta',
|
55
|
+
[:multi,
|
56
|
+
[:html, :attr, 'http-equiv', [:static, 'Content-Type']],
|
57
|
+
[:html, :attr, 'content', [:static, '']]]]
|
58
|
+
end
|
59
|
+
|
19
60
|
it 'should check for empty dynamic attribute' do
|
20
61
|
@merger.call([:html, :tag,
|
21
62
|
'div',
|
data/test/html/test_pretty.rb
CHANGED
@@ -22,7 +22,7 @@ describe Temple::HTML::Pretty do
|
|
22
22
|
[:static, "text"],
|
23
23
|
[:multi,
|
24
24
|
[:code, "_temple_html_pretty2 = (code).to_s"],
|
25
|
-
[:code, 'if _temple_html_pretty1 !~ _temple_html_pretty2; _temple_html_pretty2.gsub
|
25
|
+
[:code, 'if _temple_html_pretty1 !~ _temple_html_pretty2; _temple_html_pretty2 = _temple_html_pretty2.gsub("\n", "\n "); end'],
|
26
26
|
[:dynamic, "_temple_html_pretty2"]]],
|
27
27
|
[:static, "</p>"]],
|
28
28
|
[:static, "\n</div>"]]]
|
@@ -7,9 +7,23 @@ class FilterWithDispatcherMixin
|
|
7
7
|
[:on_test, arg]
|
8
8
|
end
|
9
9
|
|
10
|
+
def on_test_check(arg)
|
11
|
+
[:on_check, arg]
|
12
|
+
end
|
13
|
+
|
10
14
|
def on_second_test(arg)
|
11
15
|
[:on_second_test, arg]
|
12
16
|
end
|
17
|
+
|
18
|
+
def on_seventh_level_level_level_level_level_test(arg)
|
19
|
+
[:on_seventh_test, arg]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FilterWithDispatcherMixinAndOn < FilterWithDispatcherMixin
|
24
|
+
def on(*args)
|
25
|
+
[:on_zero, *args]
|
26
|
+
end
|
13
27
|
end
|
14
28
|
|
15
29
|
describe Temple::Mixins::Dispatcher do
|
@@ -28,4 +42,16 @@ describe Temple::Mixins::Dispatcher do
|
|
28
42
|
it 'should dispatch second level' do
|
29
43
|
@filter.call([:second, :test, 42]).should.equal [:on_second_test, 42]
|
30
44
|
end
|
45
|
+
|
46
|
+
it 'should dispatch second level if prefixed' do
|
47
|
+
@filter.call([:test, :check, 42]).should.equal [:on_check, 42]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should dispatch seventh level' do
|
51
|
+
@filter.call([:seventh, :level, :level, :level, :level, :level, :test, 42]).should == [:on_seventh_test, 42]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should dispatch zero level' do
|
55
|
+
FilterWithDispatcherMixinAndOn.new.call([:foo,42]).should == [:on_zero, :foo, 42]
|
56
|
+
end
|
31
57
|
end
|
data/test/test_engine.rb
CHANGED
metadata
CHANGED
@@ -1,58 +1,63 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: temple
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.4
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.3.5
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Magnus Holm
|
9
9
|
- Daniel Mendler
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
|
14
|
+
date: 2012-01-06 00:00:00 +01:00
|
15
|
+
default_executable:
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
16
18
|
name: tilt
|
17
|
-
|
19
|
+
prerelease: false
|
20
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
21
|
none: false
|
19
|
-
requirements:
|
20
|
-
- -
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: "0"
|
23
26
|
type: :development
|
24
|
-
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
27
|
+
version_requirements: *id001
|
28
|
+
- !ruby/object:Gem::Dependency
|
27
29
|
name: bacon
|
28
|
-
|
30
|
+
prerelease: false
|
31
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
32
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: "0"
|
34
37
|
type: :development
|
35
|
-
|
36
|
-
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
+
version_requirements: *id002
|
39
|
+
- !ruby/object:Gem::Dependency
|
38
40
|
name: rake
|
39
|
-
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
43
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version:
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
45
48
|
type: :development
|
46
|
-
|
47
|
-
version_requirements: *70118806200640
|
49
|
+
version_requirements: *id003
|
48
50
|
description:
|
49
|
-
email:
|
51
|
+
email:
|
50
52
|
- judofyr@gmail.com
|
51
53
|
- mail@daniel-mendler.de
|
52
54
|
executables: []
|
55
|
+
|
53
56
|
extensions: []
|
57
|
+
|
54
58
|
extra_rdoc_files: []
|
55
|
-
|
59
|
+
|
60
|
+
files:
|
56
61
|
- .gitignore
|
57
62
|
- .travis.yml
|
58
63
|
- .yardopts
|
@@ -114,31 +119,35 @@ files:
|
|
114
119
|
- test/test_grammar.rb
|
115
120
|
- test/test_hash.rb
|
116
121
|
- test/test_utils.rb
|
122
|
+
has_rdoc: true
|
117
123
|
homepage: http://dojo.rubyforge.org/
|
118
124
|
licenses: []
|
125
|
+
|
119
126
|
post_install_message:
|
120
127
|
rdoc_options: []
|
121
|
-
|
128
|
+
|
129
|
+
require_paths:
|
122
130
|
- lib
|
123
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
132
|
none: false
|
125
|
-
requirements:
|
126
|
-
- -
|
127
|
-
- !ruby/object:Gem::Version
|
128
|
-
version:
|
129
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: "0"
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
138
|
none: false
|
131
|
-
requirements:
|
132
|
-
- -
|
133
|
-
- !ruby/object:Gem::Version
|
134
|
-
version:
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: "0"
|
135
143
|
requirements: []
|
144
|
+
|
136
145
|
rubyforge_project:
|
137
|
-
rubygems_version: 1.
|
146
|
+
rubygems_version: 1.6.2
|
138
147
|
signing_key:
|
139
148
|
specification_version: 3
|
140
149
|
summary: Template compilation framework in Ruby
|
141
|
-
test_files:
|
150
|
+
test_files:
|
142
151
|
- test/filters/test_control_flow.rb
|
143
152
|
- test/filters/test_dynamic_inliner.rb
|
144
153
|
- test/filters/test_eraser.rb
|