temple 0.1.8 → 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/README.md +7 -19
- data/lib/temple/engine.rb +32 -21
- data/lib/temple/erb/engine.rb +1 -1
- data/lib/temple/erb/parser.rb +2 -2
- data/lib/temple/filters/debugger.rb +1 -1
- data/lib/temple/filters/dynamic_inliner.rb +1 -1
- data/lib/temple/filters/escape_html.rb +16 -13
- data/lib/temple/filters/multi_flattener.rb +2 -2
- data/lib/temple/filters/static_merger.rb +1 -1
- data/lib/temple/generators.rb +10 -6
- data/lib/temple/html/dispatcher.rb +17 -0
- data/lib/temple/html/fast.rb +44 -19
- data/lib/temple/html/pretty.rb +19 -15
- data/lib/temple/mixins.rb +174 -23
- data/lib/temple/templates/rails.rb +34 -0
- data/lib/temple/templates/tilt.rb +30 -0
- data/lib/temple/templates.rb +13 -0
- data/lib/temple/utils.rb +54 -1
- data/lib/temple/version.rb +1 -1
- data/lib/temple.rb +2 -2
- data/temple.gemspec +10 -10
- data/test/filters/test_dynamic_inliner.rb +10 -10
- data/test/filters/test_escape_html.rb +10 -11
- data/test/filters/test_multi_flattener.rb +2 -2
- data/test/filters/test_static_merger.rb +3 -3
- data/test/html/test_fast.rb +26 -21
- data/test/html/test_pretty.rb +2 -2
- data/test/test_engine.rb +144 -0
- data/test/test_erb.rb +3 -2
- data/test/test_generator.rb +7 -7
- data/test/test_utils.rb +38 -0
- metadata +12 -5
- data/lib/temple/erb/template.rb +0 -7
- data/lib/temple/template.rb +0 -35
data/README.md
CHANGED
@@ -115,7 +115,7 @@ mind working across processes, it's not a problem at all.
|
|
115
115
|
Compilers
|
116
116
|
---------
|
117
117
|
|
118
|
-
A *compiler* is simply an object which responds a method called #
|
118
|
+
A *compiler* is simply an object which responds a method called #call which
|
119
119
|
takes one argument and returns a value. It's illegal for a compiler to mutate
|
120
120
|
the argument, and it should be possible to use the same instance several times
|
121
121
|
(although not by several threads at the same time).
|
@@ -128,7 +128,7 @@ class. Temple then assumes the initializer takes an optional option hash:
|
|
128
128
|
@options = options
|
129
129
|
end
|
130
130
|
|
131
|
-
def
|
131
|
+
def call(exp)
|
132
132
|
# do stuff
|
133
133
|
end
|
134
134
|
end
|
@@ -195,7 +195,7 @@ When you have a chain of a parsers, some filters and a generator you can finally
|
|
195
195
|
end
|
196
196
|
|
197
197
|
engine = MyEngine.new(:strict => "For MyParser")
|
198
|
-
engine.
|
198
|
+
engine.call(something)
|
199
199
|
|
200
200
|
And then?
|
201
201
|
---------
|
@@ -203,25 +203,13 @@ And then?
|
|
203
203
|
You've ran the template through the parser, some filters and in the end a
|
204
204
|
generator. What happens next?
|
205
205
|
|
206
|
-
Temple
|
207
|
-
|
208
|
-
template engines. This gives you a wide range of features and your engine can
|
209
|
-
be used right away in many projects.
|
206
|
+
Temple provides helpers to create template classes for [Tilt](http://github.com/rtomayko/tilt) and
|
207
|
+
Rails.
|
210
208
|
|
211
209
|
require 'tilt'
|
212
210
|
|
213
|
-
class MyTemplate
|
214
|
-
|
215
|
-
@src = MyEngine.new(options).compile(data)
|
216
|
-
end
|
217
|
-
|
218
|
-
def template_source
|
219
|
-
@src
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Register your file extension:
|
224
|
-
Tilt.register 'ext', MyTemplate
|
211
|
+
# Create template class MyTemplate and register your file extension
|
212
|
+
MyTemplate = Temple::Templates::Tilt(MyEngine, :register_as => 'ext')
|
225
213
|
|
226
214
|
Tilt.new('example.ext').render # => Render a file
|
227
215
|
MyTemplate.new { "String" }.render # => Render a string
|
data/lib/temple/engine.rb
CHANGED
@@ -18,41 +18,52 @@ module Temple
|
|
18
18
|
# generator :ArrayBuffer, :buffer
|
19
19
|
# end
|
20
20
|
#
|
21
|
+
# class SpecialEngine < MyEngine
|
22
|
+
# append MyCodeOptimizer
|
23
|
+
# replace Temple::Generators::ArrayBuffer, Temple::Generators::RailsOutputBuffer
|
24
|
+
# end
|
25
|
+
#
|
21
26
|
# engine = MyEngine.new(:strict => "For MyParser")
|
22
|
-
# engine.
|
27
|
+
# engine.call(something)
|
23
28
|
#
|
24
29
|
class Engine
|
25
30
|
include Mixins::Options
|
31
|
+
include Mixins::EngineDSL
|
32
|
+
extend Mixins::EngineDSL
|
26
33
|
|
27
|
-
|
28
|
-
@chain ||= []
|
29
|
-
end
|
34
|
+
attr_reader :chain
|
30
35
|
|
31
|
-
def self.
|
32
|
-
|
33
|
-
chain << proc do |opts|
|
34
|
-
filter.new(default_options.merge(Hash[*opts.select {|k,v| options.include?(k) }.flatten]), &block)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Shortcut for <tt>use Temple::Filters::parser</tt>
|
39
|
-
def self.filter(filter, *options, &block)
|
40
|
-
use(Temple::Filters.const_get(filter), *options, &block)
|
36
|
+
def self.chain
|
37
|
+
@chain ||= superclass.respond_to?(:chain) ? superclass.chain.dup : []
|
41
38
|
end
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
def initialize(o = {})
|
41
|
+
super
|
42
|
+
@chain = self.class.chain.dup
|
43
|
+
yield(self) if block_given?
|
44
|
+
[*options[:chain]].compact.each {|block| block.call(self) }
|
45
|
+
@chain = build_chain
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
49
|
-
chain.inject(input) {|m, e| e.
|
48
|
+
def call(input)
|
49
|
+
chain.inject(input) {|m, e| e.call(m) }
|
50
50
|
end
|
51
51
|
|
52
52
|
protected
|
53
53
|
|
54
|
-
def
|
55
|
-
|
54
|
+
def build_chain
|
55
|
+
chain.map do |e|
|
56
|
+
name, filter, option_filter, local_options = e
|
57
|
+
case filter
|
58
|
+
when Class
|
59
|
+
filtered_options = Hash[*option_filter.select {|k| options.include?(k) }.map {|k| [k, options[k]] }.flatten]
|
60
|
+
filter.new(Utils::ImmutableHash.new(local_options, filtered_options))
|
61
|
+
when UnboundMethod
|
62
|
+
filter.bind(self)
|
63
|
+
else
|
64
|
+
filter
|
65
|
+
end
|
66
|
+
end
|
56
67
|
end
|
57
68
|
end
|
58
69
|
end
|
data/lib/temple/erb/engine.rb
CHANGED
@@ -2,8 +2,8 @@ module Temple
|
|
2
2
|
module ERB
|
3
3
|
class Engine < Temple::Engine
|
4
4
|
use Temple::ERB::Parser, :auto_escape
|
5
|
-
use Temple::ERB::Trimming, :trim_mode
|
6
5
|
filter :EscapeHTML, :use_html_safe
|
6
|
+
use Temple::ERB::Trimming, :trim_mode
|
7
7
|
filter :MultiFlattener
|
8
8
|
filter :StaticMerger
|
9
9
|
filter :DynamicInliner
|
data/lib/temple/erb/parser.rb
CHANGED
@@ -10,7 +10,7 @@ module Temple
|
|
10
10
|
'%%>' => '%>',
|
11
11
|
}.freeze
|
12
12
|
|
13
|
-
def
|
13
|
+
def call(input)
|
14
14
|
result = [:multi]
|
15
15
|
pos = 0
|
16
16
|
input.scan(ERB_PATTERN) do |escaped, indicator, code|
|
@@ -25,7 +25,7 @@ module Temple
|
|
25
25
|
when '#'
|
26
26
|
code.count("\n").times { result << [:newline] }
|
27
27
|
when /=/
|
28
|
-
result <<
|
28
|
+
result << [:escape, indicator.length <= 1 && options[:auto_escape], [:dynamic, code]]
|
29
29
|
else
|
30
30
|
result << [:block, code]
|
31
31
|
end
|
@@ -53,7 +53,7 @@ module Temple
|
|
53
53
|
# If we found a single exp last time, let's add it.
|
54
54
|
res.concat(prev) if state == :single
|
55
55
|
# Compile the current exp (unless it's the noop)
|
56
|
-
res << compile
|
56
|
+
res << compile(exp) unless head == :noop
|
57
57
|
# Now we're looking for more!
|
58
58
|
state = :looking
|
59
59
|
end
|
@@ -1,29 +1,32 @@
|
|
1
1
|
module Temple
|
2
2
|
module Filters
|
3
3
|
class EscapeHTML < Filter
|
4
|
-
|
4
|
+
include Temple::HTML::Dispatcher
|
5
|
+
|
6
|
+
temple_dispatch :html
|
5
7
|
|
6
8
|
# Activate the usage of html_safe? if it is available (for Rails 3 for example)
|
7
9
|
default_options[:use_html_safe] = ''.respond_to?(:html_safe?)
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def on_escape_dynamic(value)
|
14
|
-
[:dynamic, "Temple::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((#{value}))"]
|
11
|
+
def initialize(opts = {})
|
12
|
+
super
|
13
|
+
@escape = false
|
15
14
|
end
|
16
15
|
|
17
|
-
def
|
18
|
-
|
16
|
+
def on_escape(flag, exp)
|
17
|
+
old = @escape
|
18
|
+
@escape = flag
|
19
|
+
compile(exp)
|
20
|
+
ensure
|
21
|
+
@escape = old
|
19
22
|
end
|
20
23
|
|
21
|
-
def
|
22
|
-
[:
|
24
|
+
def on_static(value)
|
25
|
+
[:static, @escape ? (options[:use_html_safe] ? escape_html_safe(value) : escape_html(value)) : value]
|
23
26
|
end
|
24
27
|
|
25
|
-
def
|
26
|
-
[:
|
28
|
+
def on_dynamic(value)
|
29
|
+
[:dynamic, @escape ? "Temple::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((#{value}))" : value]
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
@@ -3,11 +3,11 @@ module Temple
|
|
3
3
|
class MultiFlattener < Filter
|
4
4
|
def on_multi(*exps)
|
5
5
|
# If the multi contains a single element, just return the element
|
6
|
-
return compile
|
6
|
+
return compile(exps.first) if exps.length == 1
|
7
7
|
result = [:multi]
|
8
8
|
|
9
9
|
exps.each do |exp|
|
10
|
-
exp = compile
|
10
|
+
exp = compile(exp)
|
11
11
|
if exp.first == :multi
|
12
12
|
result.concat(exp[1..-1])
|
13
13
|
else
|
data/lib/temple/generators.rb
CHANGED
@@ -73,17 +73,21 @@ module Temple
|
|
73
73
|
|
74
74
|
default_options[:buffer] = '_buf'
|
75
75
|
|
76
|
-
def
|
77
|
-
[preamble, compile
|
76
|
+
def call(exp)
|
77
|
+
[preamble, compile(exp), postamble].join(' ; ')
|
78
78
|
end
|
79
79
|
|
80
|
-
def compile
|
80
|
+
def compile(exp)
|
81
81
|
type, *args = exp
|
82
|
-
|
82
|
+
if respond_to?("on_#{type}")
|
83
|
+
send("on_#{type}", *args)
|
84
|
+
else
|
85
|
+
raise "Generator supports only core expressions - found #{exp.inspect}"
|
86
|
+
end
|
83
87
|
end
|
84
88
|
|
85
89
|
def on_multi(*exp)
|
86
|
-
exp.map { |e| compile
|
90
|
+
exp.map { |e| compile(e) }.join(' ; ')
|
87
91
|
end
|
88
92
|
|
89
93
|
def on_newline
|
@@ -91,7 +95,7 @@ module Temple
|
|
91
95
|
end
|
92
96
|
|
93
97
|
def on_capture(name, block)
|
94
|
-
options[:capture_generator].new(:buffer => name).
|
98
|
+
options[:capture_generator].new(:buffer => name).call(block)
|
95
99
|
end
|
96
100
|
|
97
101
|
protected
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Temple
|
2
|
+
module HTML
|
3
|
+
module Dispatcher
|
4
|
+
def on_html_staticattrs(*attrs)
|
5
|
+
[:html, :staticattrs, *attrs.map {|k,v| [k, compile(v)] }]
|
6
|
+
end
|
7
|
+
|
8
|
+
def on_html_comment(content)
|
9
|
+
[:html, :comment, compile(content)]
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_html_tag(name, attrs, closed, content)
|
13
|
+
[:html, :tag, name, compile(attrs), closed, compile(content)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/temple/html/fast.rb
CHANGED
@@ -39,18 +39,11 @@ module Temple
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def html?
|
42
|
-
html5
|
43
|
-
end
|
44
|
-
|
45
|
-
def html5?
|
46
|
-
options[:format] == :html5
|
47
|
-
end
|
48
|
-
|
49
|
-
def html4?
|
50
|
-
options[:format] == :html4
|
42
|
+
options[:format] == :html5 || options[:format] == :html4
|
51
43
|
end
|
52
44
|
|
53
45
|
def on_html_doctype(type)
|
46
|
+
type = type.to_s
|
54
47
|
trailing_newlines = type[/(\A|[^\r])(\n+)\Z/, 2].to_s
|
55
48
|
text = type.downcase.strip
|
56
49
|
|
@@ -76,16 +69,16 @@ module Temple
|
|
76
69
|
def on_html_comment(content)
|
77
70
|
[:multi,
|
78
71
|
[:static, '<!--'],
|
79
|
-
compile
|
72
|
+
compile(content),
|
80
73
|
[:static, '-->']]
|
81
74
|
end
|
82
75
|
|
83
76
|
def on_html_tag(name, attrs, closed, content)
|
84
77
|
closed ||= options[:autoclose].include?(name)
|
85
78
|
raise "Closed tag #{name} has content" if closed && !empty_exp?(content)
|
86
|
-
result = [:multi, [:static, "<#{name}"], compile
|
79
|
+
result = [:multi, [:static, "<#{name}"], compile(attrs)]
|
87
80
|
result << [:static, ' /'] if closed && xhtml?
|
88
|
-
result << [:static, '>'] << compile
|
81
|
+
result << [:static, '>'] << compile(content)
|
89
82
|
result << [:static, "</#{name}>"] if !closed
|
90
83
|
result
|
91
84
|
end
|
@@ -104,13 +97,45 @@ module Temple
|
|
104
97
|
end
|
105
98
|
end
|
106
99
|
result.sort.inject([:multi]) do |list, (name, value)|
|
107
|
-
list <<
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
100
|
+
list << compile_attribute(name, value)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def compile_attribute(name, value)
|
107
|
+
if empty_exp?(value)
|
108
|
+
[:multi]
|
109
|
+
elsif contains_static?(value)
|
110
|
+
attribute(name, value)
|
111
|
+
else
|
112
|
+
tmp = tmp_var(:htmlattr)
|
113
|
+
[:multi,
|
114
|
+
[:capture, tmp, value],
|
115
|
+
[:block, "unless #{tmp}.empty?"],
|
116
|
+
attribute(name, [:dynamic, tmp]),
|
117
|
+
[:block, 'end']]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def attribute(name, value)
|
122
|
+
[:multi,
|
123
|
+
[:static, ' '],
|
124
|
+
[:static, name],
|
125
|
+
[:static, '='],
|
126
|
+
[:static, options[:attr_wrapper]],
|
127
|
+
value,
|
128
|
+
[:static, options[:attr_wrapper]]]
|
129
|
+
end
|
130
|
+
|
131
|
+
def contains_static?(exp)
|
132
|
+
case exp[0]
|
133
|
+
when :multi
|
134
|
+
exp[1..-1].any? {|e| contains_static?(e) }
|
135
|
+
when :static
|
136
|
+
true
|
137
|
+
else
|
138
|
+
false
|
114
139
|
end
|
115
140
|
end
|
116
141
|
end
|
data/lib/temple/html/pretty.rb
CHANGED
@@ -3,39 +3,44 @@ module Temple
|
|
3
3
|
class Pretty < Fast
|
4
4
|
set_default_options :indent => ' ',
|
5
5
|
:pretty => true,
|
6
|
-
:indent_tags => %w(base body dd div dl
|
6
|
+
:indent_tags => %w(base body dd div dl dt fieldset form head h1 h2 h3
|
7
7
|
h4 h5 h6 hr html img input li link meta ol p script
|
8
8
|
style table tbody td tfoot th thead title tr ul).freeze,
|
9
9
|
:pre_tags => %w(pre textarea).freeze
|
10
10
|
|
11
11
|
def initialize(opts = {})
|
12
12
|
super
|
13
|
-
@last =
|
13
|
+
@last = :noindent
|
14
14
|
@indent = 0
|
15
15
|
@pretty = options[:pretty]
|
16
|
+
@pre_tags = Regexp.new(options[:pre_tags].map {|t| "<#{t}" }.join('|'))
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
[:multi, preamble, compile
|
19
|
+
def call(exp)
|
20
|
+
@pretty ? [:multi, preamble, compile(exp)] : super
|
20
21
|
end
|
21
22
|
|
22
23
|
def on_static(content)
|
23
|
-
@
|
24
|
-
|
24
|
+
if @pretty
|
25
|
+
content = Utils.indent(content, indent, @pre_tags)
|
26
|
+
@last = content.sub!(/\r?\n\s*$/, ' ') ? nil : :noindent
|
27
|
+
end
|
28
|
+
[:static, content]
|
25
29
|
end
|
26
30
|
|
27
31
|
def on_dynamic(content)
|
28
|
-
@last =
|
32
|
+
@last = :noindent
|
29
33
|
[:dynamic, @pretty ? "Temple::Utils.indent((#{content}), #{indent.inspect}, _temple_pre_tags)" : content]
|
30
34
|
end
|
31
35
|
|
32
36
|
def on_html_doctype(type)
|
33
|
-
@last =
|
37
|
+
@last = nil
|
34
38
|
super
|
35
39
|
end
|
36
40
|
|
37
41
|
def on_html_comment(content)
|
38
42
|
return super unless @pretty
|
43
|
+
@last = nil
|
39
44
|
[:multi, [:static, indent], super]
|
40
45
|
end
|
41
46
|
|
@@ -46,18 +51,16 @@ module Temple
|
|
46
51
|
raise "Closed tag #{name} has content" if closed && !empty_exp?(content)
|
47
52
|
|
48
53
|
@pretty = false
|
49
|
-
result = [:multi, [:static, "#{tag_indent(name)}<#{name}"], compile
|
54
|
+
result = [:multi, [:static, "#{tag_indent(name)}<#{name}"], compile(attrs)]
|
50
55
|
result << [:static, ' /'] if closed && xhtml?
|
51
56
|
result << [:static, '>']
|
52
57
|
|
53
|
-
@last = name
|
54
58
|
@pretty = !options[:pre_tags].include?(name)
|
55
59
|
@indent += 1
|
56
|
-
result << compile
|
60
|
+
result << compile(content)
|
57
61
|
@indent -= 1
|
58
62
|
|
59
63
|
result << [:static, "#{tag_indent(name)}</#{name}>"] if !closed
|
60
|
-
@last = name
|
61
64
|
@pretty = true
|
62
65
|
result
|
63
66
|
end
|
@@ -65,8 +68,7 @@ module Temple
|
|
65
68
|
protected
|
66
69
|
|
67
70
|
def preamble
|
68
|
-
|
69
|
-
[:block, "_temple_pre_tags = /#{regexp}/"]
|
71
|
+
[:block, "_temple_pre_tags = /#{@pre_tags.source}/"]
|
70
72
|
end
|
71
73
|
|
72
74
|
# Return indentation if not in pre tag
|
@@ -76,7 +78,9 @@ module Temple
|
|
76
78
|
|
77
79
|
# Return indentation before tag
|
78
80
|
def tag_indent(name)
|
79
|
-
@last && (options[:indent_tags].include?(@last) || options[:indent_tags].include?(name)) ? indent : ''
|
81
|
+
result = @last != :noindent && (options[:indent_tags].include?(@last) || options[:indent_tags].include?(name)) ? indent : ''
|
82
|
+
@last = name
|
83
|
+
result
|
80
84
|
end
|
81
85
|
end
|
82
86
|
end
|