temple 0.6.7 → 0.10.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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +34 -0
- data/.gitignore +1 -0
- data/CHANGES +106 -1
- data/EXPRESSIONS.md +3 -2
- data/Gemfile +0 -1
- data/README.md +14 -10
- data/Rakefile +4 -11
- data/lib/temple/engine.rb +7 -3
- data/lib/temple/erb/engine.rb +5 -3
- data/lib/temple/erb/parser.rb +1 -1
- data/lib/temple/erb/trimming.rb +11 -26
- data/lib/temple/filters/ambles.rb +21 -0
- data/lib/temple/filters/encoding.rb +1 -1
- data/lib/temple/filters/eraser.rb +1 -1
- data/lib/temple/filters/escapable.rb +2 -2
- data/lib/temple/filters/remove_bom.rb +2 -9
- data/lib/temple/filters/static_analyzer.rb +30 -0
- data/lib/temple/filters/string_splitter.rb +141 -0
- data/lib/temple/filters/validator.rb +1 -1
- data/lib/temple/generator.rb +32 -6
- data/lib/temple/generators/array.rb +2 -2
- data/lib/temple/generators/array_buffer.rb +6 -5
- data/lib/temple/generators/erb.rb +1 -5
- data/lib/temple/generators/rails_output_buffer.rb +7 -8
- data/lib/temple/generators/string_buffer.rb +2 -2
- data/lib/temple/html/attribute_merger.rb +6 -11
- data/lib/temple/html/attribute_remover.rb +1 -1
- data/lib/temple/html/attribute_sorter.rb +1 -1
- data/lib/temple/html/fast.rb +49 -44
- data/lib/temple/html/pretty.rb +34 -43
- data/lib/temple/html/safe.rb +23 -0
- data/lib/temple/map.rb +105 -0
- data/lib/temple/mixins/dispatcher.rb +10 -7
- data/lib/temple/mixins/engine_dsl.rb +42 -67
- data/lib/temple/mixins/grammar_dsl.rb +10 -8
- data/lib/temple/mixins/options.rb +26 -24
- data/lib/temple/mixins/template.rb +3 -3
- data/lib/temple/static_analyzer.rb +77 -0
- data/lib/temple/templates/rails.rb +17 -36
- data/lib/temple/templates/tilt.rb +7 -13
- data/lib/temple/utils.rb +27 -29
- data/lib/temple/version.rb +1 -1
- data/lib/temple.rb +8 -4
- data/spec/engine_spec.rb +189 -0
- data/{test/test_erb.rb → spec/erb_spec.rb} +12 -13
- data/spec/filter_spec.rb +29 -0
- data/{test/filters/test_code_merger.rb → spec/filters/code_merger_spec.rb} +7 -7
- data/{test/filters/test_control_flow.rb → spec/filters/control_flow_spec.rb} +13 -13
- data/{test/filters/test_dynamic_inliner.rb → spec/filters/dynamic_inliner_spec.rb} +18 -18
- data/{test/filters/test_eraser.rb → spec/filters/eraser_spec.rb} +13 -13
- data/{test/filters/test_escapable.rb → spec/filters/escapable_spec.rb} +15 -13
- data/{test/filters/test_multi_flattener.rb → spec/filters/multi_flattener_spec.rb} +4 -4
- data/spec/filters/static_analyzer_spec.rb +35 -0
- data/{test/filters/test_static_merger.rb → spec/filters/static_merger_spec.rb} +7 -7
- data/spec/filters/string_splitter_spec.rb +50 -0
- data/spec/generator_spec.rb +158 -0
- data/spec/grammar_spec.rb +47 -0
- data/{test/html/test_attribute_merger.rb → spec/html/attribute_merger_spec.rb} +11 -11
- data/{test/html/test_attribute_remover.rb → spec/html/attribute_remover_spec.rb} +7 -7
- data/{test/html/test_attribute_sorter.rb → spec/html/attribute_sorter_spec.rb} +8 -8
- data/{test/html/test_fast.rb → spec/html/fast_spec.rb} +23 -23
- data/{test/html/test_pretty.rb → spec/html/pretty_spec.rb} +9 -15
- data/spec/map_spec.rb +39 -0
- data/{test/mixins/test_dispatcher.rb → spec/mixins/dispatcher_spec.rb} +12 -12
- data/{test/mixins/test_grammar_dsl.rb → spec/mixins/grammar_dsl_spec.rb} +19 -19
- data/{test/helper.rb → spec/spec_helper.rb} +9 -15
- data/spec/static_analyzer_spec.rb +39 -0
- data/spec/utils_spec.rb +39 -0
- data/temple.gemspec +4 -2
- metadata +62 -63
- data/.travis.yml +0 -13
- data/lib/temple/hash.rb +0 -104
- data/test/test_engine.rb +0 -170
- data/test/test_filter.rb +0 -29
- data/test/test_generator.rb +0 -136
- data/test/test_grammar.rb +0 -47
- data/test/test_hash.rb +0 -39
- data/test/test_utils.rb +0 -39
data/lib/temple/generator.rb
CHANGED
@@ -5,14 +5,40 @@ module Temple
|
|
5
5
|
#
|
6
6
|
# @api public
|
7
7
|
class Generator
|
8
|
+
include Utils
|
8
9
|
include Mixins::CompiledDispatcher
|
9
10
|
include Mixins::Options
|
10
11
|
|
11
|
-
define_options :
|
12
|
-
:
|
12
|
+
define_options :save_buffer,
|
13
|
+
capture_generator: 'StringBuffer',
|
14
|
+
buffer: '_buf',
|
15
|
+
freeze_static: true
|
13
16
|
|
14
17
|
def call(exp)
|
15
|
-
[preamble, compile(exp), postamble].join('; ')
|
18
|
+
[preamble, compile(exp), postamble].flatten.compact.join('; ')
|
19
|
+
end
|
20
|
+
|
21
|
+
def preamble
|
22
|
+
[save_buffer, create_buffer]
|
23
|
+
end
|
24
|
+
|
25
|
+
def postamble
|
26
|
+
[return_buffer, restore_buffer]
|
27
|
+
end
|
28
|
+
|
29
|
+
def save_buffer
|
30
|
+
"begin; #{@original_buffer = unique_name} = #{buffer} if defined?(#{buffer})" if options[:save_buffer]
|
31
|
+
end
|
32
|
+
|
33
|
+
def restore_buffer
|
34
|
+
"ensure; #{buffer} = #{@original_buffer}; end" if options[:save_buffer]
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_buffer
|
38
|
+
end
|
39
|
+
|
40
|
+
def return_buffer
|
41
|
+
'nil'
|
16
42
|
end
|
17
43
|
|
18
44
|
def on(*exp)
|
@@ -20,7 +46,7 @@ module Temple
|
|
20
46
|
end
|
21
47
|
|
22
48
|
def on_multi(*exp)
|
23
|
-
exp.map {|e| compile(e) }.join('; ')
|
49
|
+
exp.map {|e| compile(e) }.join('; '.freeze)
|
24
50
|
end
|
25
51
|
|
26
52
|
def on_newline
|
@@ -28,11 +54,11 @@ module Temple
|
|
28
54
|
end
|
29
55
|
|
30
56
|
def on_capture(name, exp)
|
31
|
-
capture_generator.new(:
|
57
|
+
capture_generator.new(buffer: name).call(exp)
|
32
58
|
end
|
33
59
|
|
34
60
|
def on_static(text)
|
35
|
-
concat(text.inspect)
|
61
|
+
concat(options[:freeze_static] ? "#{text.inspect}.freeze" : text.inspect)
|
36
62
|
end
|
37
63
|
|
38
64
|
def on_dynamic(code)
|
@@ -5,23 +5,24 @@ module Temple
|
|
5
5
|
# _buf = []
|
6
6
|
# _buf << "static"
|
7
7
|
# _buf << dynamic
|
8
|
-
# _buf.join
|
8
|
+
# _buf.join("")
|
9
9
|
#
|
10
10
|
# @api public
|
11
11
|
class ArrayBuffer < Array
|
12
12
|
def call(exp)
|
13
13
|
case exp.first
|
14
14
|
when :static
|
15
|
-
"#{buffer} = #{exp.last.inspect}"
|
15
|
+
[save_buffer, "#{buffer} = #{exp.last.inspect}", restore_buffer].compact.join('; ')
|
16
16
|
when :dynamic
|
17
|
-
"#{buffer} = (#{exp.last}).to_s"
|
17
|
+
[save_buffer, "#{buffer} = (#{exp.last}).to_s", restore_buffer].compact.join('; ')
|
18
18
|
else
|
19
19
|
super
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def return_buffer
|
24
|
+
freeze = options[:freeze_static] ? '.freeze' : ''
|
25
|
+
"#{buffer} = #{buffer}.join(\"\"#{freeze})"
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -9,7 +9,7 @@ module Temple
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def on_multi(*exp)
|
12
|
-
exp.map {|e| compile(e) }.join
|
12
|
+
exp.map {|e| compile(e) }.join('')
|
13
13
|
end
|
14
14
|
|
15
15
|
def on_capture(name, exp)
|
@@ -20,10 +20,6 @@ module Temple
|
|
20
20
|
text
|
21
21
|
end
|
22
22
|
|
23
|
-
def on_newline
|
24
|
-
"<%\n%>"
|
25
|
-
end
|
26
|
-
|
27
23
|
def on_dynamic(code)
|
28
24
|
"<%= #{code} %>"
|
29
25
|
end
|
@@ -9,18 +9,17 @@ module Temple
|
|
9
9
|
#
|
10
10
|
# @api public
|
11
11
|
class RailsOutputBuffer < StringBuffer
|
12
|
-
define_options :streaming,
|
13
|
-
:
|
14
|
-
:
|
15
|
-
|
16
|
-
:capture_generator => RailsOutputBuffer
|
12
|
+
define_options :streaming, # ignored
|
13
|
+
buffer_class: 'ActionView::OutputBuffer',
|
14
|
+
buffer: '@output_buffer',
|
15
|
+
capture_generator: RailsOutputBuffer
|
17
16
|
|
18
17
|
def call(exp)
|
19
|
-
[preamble, compile(exp), postamble].join('; ')
|
18
|
+
[preamble, compile(exp), postamble].flatten.compact.join('; '.freeze)
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
23
|
-
if
|
21
|
+
def create_buffer
|
22
|
+
if buffer == '@output_buffer'
|
24
23
|
"#{buffer} = output_buffer || #{options[:buffer_class]}.new"
|
25
24
|
else
|
26
25
|
"#{buffer} = #{options[:buffer_class]}.new"
|
@@ -3,43 +3,38 @@ module Temple
|
|
3
3
|
# This filter merges html attributes (e.g. used for id and class)
|
4
4
|
# @api public
|
5
5
|
class AttributeMerger < Filter
|
6
|
-
define_options :
|
6
|
+
define_options merge_attrs: {'id' => '_', 'class' => ' '}
|
7
7
|
|
8
8
|
def on_html_attrs(*attrs)
|
9
|
-
names = []
|
10
9
|
values = {}
|
11
10
|
|
12
|
-
attrs.each do |
|
13
|
-
name
|
11
|
+
attrs.each do |_, _, name, value|
|
12
|
+
name = name.to_s
|
14
13
|
if values[name]
|
15
14
|
raise(FilterError, "Multiple #{name} attributes specified") unless options[:merge_attrs][name]
|
16
15
|
values[name] << value
|
17
16
|
else
|
18
17
|
values[name] = [value]
|
19
|
-
names << name
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
|
-
attrs =
|
24
|
-
value = values[name]
|
21
|
+
attrs = values.map do |name, value|
|
25
22
|
if (delimiter = options[:merge_attrs][name]) && value.size > 1
|
26
23
|
exp = [:multi]
|
27
24
|
if value.all? {|v| contains_nonempty_static?(v) }
|
28
25
|
exp << value.first
|
29
26
|
value[1..-1].each {|v| exp << [:static, delimiter] << v }
|
30
|
-
[:html, :attr, name, exp]
|
31
27
|
else
|
32
28
|
captures = unique_name
|
33
29
|
exp << [:code, "#{captures} = []"]
|
34
30
|
value.each_with_index {|v, i| exp << [:capture, "#{captures}[#{i}]", v] }
|
35
31
|
exp << [:dynamic, "#{captures}.reject(&:empty?).join(#{delimiter.inspect})"]
|
36
32
|
end
|
37
|
-
[:html, :attr, name, exp]
|
38
33
|
else
|
39
|
-
|
34
|
+
exp = value.first
|
40
35
|
end
|
36
|
+
[:html, :attr, name, exp]
|
41
37
|
end
|
42
|
-
|
43
38
|
[:html, :attrs, *attrs]
|
44
39
|
end
|
45
40
|
end
|
data/lib/temple/html/fast.rb
CHANGED
@@ -2,39 +2,50 @@ module Temple
|
|
2
2
|
module HTML
|
3
3
|
# @api public
|
4
4
|
class Fast < Filter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
DOCTYPES = {
|
6
|
+
xml: {
|
7
|
+
'1.1' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
|
8
|
+
'5' => '<!DOCTYPE html>',
|
9
|
+
'html' => '<!DOCTYPE html>',
|
10
|
+
'strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
|
11
|
+
'frameset' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
|
12
|
+
'mobile' => '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">',
|
13
|
+
'basic' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
|
14
|
+
'transitional' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
|
15
|
+
'svg' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
|
16
|
+
},
|
17
|
+
html: {
|
18
|
+
'5' => '<!DOCTYPE html>',
|
19
|
+
'html' => '<!DOCTYPE html>',
|
20
|
+
'strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
|
21
|
+
'frameset' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
|
22
|
+
'transitional' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
23
|
+
}
|
24
|
+
}
|
25
|
+
DOCTYPES[:xhtml] = DOCTYPES[:xml]
|
26
|
+
DOCTYPES.freeze
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
'html' => '<!DOCTYPE html>',
|
19
|
-
'strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
|
20
|
-
'frameset' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
|
21
|
-
'transitional' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
|
22
|
-
}.freeze
|
28
|
+
# See http://www.w3.org/html/wg/drafts/html/master/single-page.html#void-elements
|
29
|
+
HTML_VOID_ELEMENTS = %w[area base br col embed hr img input keygen link menuitem meta param source track wbr]
|
23
30
|
|
24
|
-
define_options :
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
|
29
|
-
HTML = [:html, :html4, :html5]
|
31
|
+
define_options format: :xhtml,
|
32
|
+
attr_quote: '"',
|
33
|
+
autoclose: HTML_VOID_ELEMENTS,
|
34
|
+
js_wrapper: nil
|
30
35
|
|
31
36
|
def initialize(opts = {})
|
32
37
|
super
|
33
|
-
|
34
|
-
|
38
|
+
@format = options[:format]
|
39
|
+
unless [:xhtml, :html, :xml].include?(@format)
|
40
|
+
if @format == :html4 || @format == :html5
|
41
|
+
warn "Format #{@format.inspect} is deprecated, use :html"
|
42
|
+
@format = :html
|
43
|
+
else
|
44
|
+
raise ArgumentError, "Invalid format #{@format.inspect}"
|
45
|
+
end
|
35
46
|
end
|
36
47
|
wrapper = options[:js_wrapper]
|
37
|
-
wrapper = xhtml
|
48
|
+
wrapper = @format == :xml || @format == :xhtml ? :cdata : :comment if wrapper == :guess
|
38
49
|
@js_wrapper =
|
39
50
|
case wrapper
|
40
51
|
when :comment
|
@@ -51,25 +62,15 @@ module Temple
|
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
def xhtml?
|
55
|
-
options[:format] == :xhtml
|
56
|
-
end
|
57
|
-
|
58
|
-
def html?
|
59
|
-
HTML.include?(options[:format])
|
60
|
-
end
|
61
|
-
|
62
65
|
def on_html_doctype(type)
|
63
66
|
type = type.to_s.downcase
|
64
67
|
|
65
68
|
if type =~ /^xml(\s+(.+))?$/
|
66
|
-
raise(FilterError, 'Invalid xml directive in html mode') if html
|
69
|
+
raise(FilterError, 'Invalid xml directive in html mode') if @format == :html
|
67
70
|
w = options[:attr_quote]
|
68
71
|
str = "<?xml version=#{w}1.0#{w} encoding=#{w}#{$2 || 'utf-8'}#{w} ?>"
|
69
|
-
elsif html?
|
70
|
-
str = HTML_DOCTYPES[type] || raise(FilterError, "Invalid html doctype #{type}")
|
71
72
|
else
|
72
|
-
str =
|
73
|
+
str = DOCTYPES[@format][type] || raise(FilterError, "Invalid doctype #{type}")
|
73
74
|
end
|
74
75
|
|
75
76
|
[:static, str]
|
@@ -91,9 +92,9 @@ module Temple
|
|
91
92
|
|
92
93
|
def on_html_tag(name, attrs, content = nil)
|
93
94
|
name = name.to_s
|
94
|
-
closed = !content || (empty_exp?(content) && options[:autoclose].include?(name))
|
95
|
+
closed = !content || (empty_exp?(content) && (@format == :xml || options[:autoclose].include?(name)))
|
95
96
|
result = [:multi, [:static, "<#{name}"], compile(attrs)]
|
96
|
-
result << [:static, (closed &&
|
97
|
+
result << [:static, (closed && @format != :html ? ' /' : '') + '>']
|
97
98
|
result << compile(content) if content
|
98
99
|
result << [:static, "</#{name}>"] if !closed
|
99
100
|
result
|
@@ -104,10 +105,14 @@ module Temple
|
|
104
105
|
end
|
105
106
|
|
106
107
|
def on_html_attr(name, value)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
if @format == :html && empty_exp?(value)
|
109
|
+
[:static, " #{name}"]
|
110
|
+
else
|
111
|
+
[:multi,
|
112
|
+
[:static, " #{name}=#{options[:attr_quote]}"],
|
113
|
+
compile(value),
|
114
|
+
[:static, options[:attr_quote]]]
|
115
|
+
end
|
111
116
|
end
|
112
117
|
|
113
118
|
def on_html_js(content)
|
data/lib/temple/html/pretty.rb
CHANGED
@@ -2,21 +2,21 @@ module Temple
|
|
2
2
|
module HTML
|
3
3
|
# @api public
|
4
4
|
class Pretty < Fast
|
5
|
-
define_options :
|
6
|
-
:
|
7
|
-
:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
:
|
5
|
+
define_options indent: ' ',
|
6
|
+
pretty: true,
|
7
|
+
indent_tags: %w(article aside audio base body datalist dd div dl dt
|
8
|
+
fieldset figure footer form head h1 h2 h3 h4 h5 h6
|
9
|
+
header hgroup hr html li link meta nav ol option p
|
10
|
+
rp rt ruby section script style table tbody td tfoot
|
11
|
+
th thead tr ul video doctype).freeze,
|
12
|
+
pre_tags: %w(code pre textarea).freeze
|
13
13
|
|
14
14
|
def initialize(opts = {})
|
15
15
|
super
|
16
|
-
@
|
16
|
+
@indent_next = nil
|
17
17
|
@indent = 0
|
18
18
|
@pretty = options[:pretty]
|
19
|
-
@pre_tags = Regexp.
|
19
|
+
@pre_tags = @format != :xml && Regexp.union(options[:pre_tags].map {|t| "<#{t}" })
|
20
20
|
end
|
21
21
|
|
22
22
|
def call(exp)
|
@@ -24,36 +24,19 @@ module Temple
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def on_static(content)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
@last = :static
|
27
|
+
return [:static, content] unless @pretty
|
28
|
+
unless @pre_tags && @pre_tags =~ content
|
29
|
+
content = content.sub(/\A\s*\n?/, "\n".freeze) if @indent_next
|
30
|
+
content = content.gsub("\n".freeze, indent)
|
33
31
|
end
|
32
|
+
@indent_next = false
|
34
33
|
[:static, content]
|
35
34
|
end
|
36
35
|
|
37
36
|
def on_dynamic(code)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
indent_code << "#{tmp} = #{tmp}.sub(/\\A\\s*\\n?/, \"\\n\"); " if options[:indent_tags].include?(@last)
|
42
|
-
indent_code << "#{tmp} = #{tmp}.gsub(\"\n\", #{indent.inspect}); "
|
43
|
-
if ''.respond_to?(:html_safe)
|
44
|
-
safe = unique_name
|
45
|
-
# we have to first save if the string was html_safe
|
46
|
-
# otherwise the gsub operation will lose that knowledge
|
47
|
-
indent_code = "#{safe} = #{tmp}.html_safe?; #{indent_code}#{tmp} = #{tmp}.html_safe if #{safe}; "
|
48
|
-
end
|
49
|
-
@last = :dynamic
|
50
|
-
[:multi,
|
51
|
-
[:code, "#{tmp} = (#{code}).to_s"],
|
52
|
-
[:code, "if #{@pre_tags_name} !~ #{tmp}; #{indent_code}end"],
|
53
|
-
[:dynamic, tmp]]
|
54
|
-
else
|
55
|
-
[:dynamic, code]
|
56
|
-
end
|
37
|
+
return [:dynamic, code] unless @pretty
|
38
|
+
indent_next, @indent_next = @indent_next, false
|
39
|
+
[:dynamic, "::Temple::Utils.indent_dynamic((#{code}), #{indent_next.inspect}, #{indent.inspect}#{@pre_tags ? ', ' + @pre_tags_name : ''})"]
|
57
40
|
end
|
58
41
|
|
59
42
|
def on_html_doctype(type)
|
@@ -64,7 +47,7 @@ module Temple
|
|
64
47
|
def on_html_comment(content)
|
65
48
|
return super unless @pretty
|
66
49
|
result = [:multi, [:static, tag_indent('comment')], super]
|
67
|
-
@
|
50
|
+
@indent_next = false
|
68
51
|
result
|
69
52
|
end
|
70
53
|
|
@@ -76,16 +59,18 @@ module Temple
|
|
76
59
|
|
77
60
|
@pretty = false
|
78
61
|
result = [:multi, [:static, "#{tag_indent(name)}<#{name}"], compile(attrs)]
|
79
|
-
result << [:static, (closed &&
|
62
|
+
result << [:static, (closed && @format != :html ? ' /' : '') + '>']
|
80
63
|
|
81
|
-
@pretty = !options[:pre_tags].include?(name)
|
64
|
+
@pretty = !@pre_tags || !options[:pre_tags].include?(name)
|
82
65
|
if content
|
83
66
|
@indent += 1
|
84
67
|
result << compile(content)
|
85
68
|
@indent -= 1
|
86
69
|
end
|
87
|
-
|
88
|
-
|
70
|
+
unless closed
|
71
|
+
indent = tag_indent(name)
|
72
|
+
result << [:static, "#{content && !empty_exp?(content) ? indent : ''}</#{name}>"]
|
73
|
+
end
|
89
74
|
@pretty = true
|
90
75
|
result
|
91
76
|
end
|
@@ -93,6 +78,7 @@ module Temple
|
|
93
78
|
protected
|
94
79
|
|
95
80
|
def preamble
|
81
|
+
return [:multi] unless @pre_tags
|
96
82
|
@pre_tags_name = unique_name
|
97
83
|
[:code, "#{@pre_tags_name} = /#{@pre_tags.source}/"]
|
98
84
|
end
|
@@ -103,9 +89,14 @@ module Temple
|
|
103
89
|
|
104
90
|
# Return indentation before tag
|
105
91
|
def tag_indent(name)
|
106
|
-
|
107
|
-
|
108
|
-
|
92
|
+
if @format == :xml
|
93
|
+
flag = @indent_next != nil
|
94
|
+
@indent_next = true
|
95
|
+
else
|
96
|
+
flag = @indent_next != nil && (@indent_next || options[:indent_tags].include?(name))
|
97
|
+
@indent_next = options[:indent_tags].include?(name)
|
98
|
+
end
|
99
|
+
flag ? indent : ''
|
109
100
|
end
|
110
101
|
end
|
111
102
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Temple
|
2
|
+
module HTML
|
3
|
+
class SafeString < String
|
4
|
+
def html_safe?; true end
|
5
|
+
def html_safe; self end
|
6
|
+
def to_s; self end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Object
|
12
|
+
def html_safe?; false end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Numeric
|
16
|
+
def html_safe?; true end
|
17
|
+
end
|
18
|
+
|
19
|
+
class String
|
20
|
+
def html_safe
|
21
|
+
Temple::HTML::SafeString.new(self)
|
22
|
+
end
|
23
|
+
end
|
data/lib/temple/map.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module Temple
|
2
|
+
# Immutable map class which supports map merging
|
3
|
+
# @api public
|
4
|
+
class ImmutableMap
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(*map)
|
8
|
+
@map = map.compact
|
9
|
+
end
|
10
|
+
|
11
|
+
def include?(key)
|
12
|
+
@map.any? {|h| h.include?(key) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@map.each {|h| return h[key] if h.include?(key) }
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
keys.each {|k| yield(k, self[k]) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def keys
|
25
|
+
@map.inject([]) {|keys, h| keys.concat(h.keys) }.uniq
|
26
|
+
end
|
27
|
+
|
28
|
+
def values
|
29
|
+
keys.map {|k| self[k] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
result = {}
|
34
|
+
each {|k, v| result[k] = v }
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Mutable map class which supports map merging
|
40
|
+
# @api public
|
41
|
+
class MutableMap < ImmutableMap
|
42
|
+
def initialize(*map)
|
43
|
+
super({}, *map)
|
44
|
+
end
|
45
|
+
|
46
|
+
def []=(key, value)
|
47
|
+
@map.first[key] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def update(map)
|
51
|
+
@map.first.update(map)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class OptionMap < MutableMap
|
56
|
+
def initialize(*map, &block)
|
57
|
+
super(*map)
|
58
|
+
@handler = block
|
59
|
+
@valid = {}
|
60
|
+
@deprecated = {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def []=(key, value)
|
64
|
+
validate_key!(key)
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def update(map)
|
69
|
+
validate_map!(map)
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_keys
|
74
|
+
(keys + @valid.keys +
|
75
|
+
@map.map {|h| h.valid_keys if h.respond_to?(:valid_keys) }.compact.flatten).uniq
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_valid_keys(*keys)
|
79
|
+
keys.flatten.each { |key| @valid[key] = true }
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_deprecated_keys(*keys)
|
83
|
+
keys.flatten.each { |key| @valid[key] = @deprecated[key] = true }
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_map!(map)
|
87
|
+
map.to_hash.keys.each {|key| validate_key!(key) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_key!(key)
|
91
|
+
@handler.call(self, key, :deprecated) if deprecated_key?(key)
|
92
|
+
@handler.call(self, key, :invalid) unless valid_key?(key)
|
93
|
+
end
|
94
|
+
|
95
|
+
def deprecated_key?(key)
|
96
|
+
@deprecated.include?(key) ||
|
97
|
+
@map.any? {|h| h.deprecated_key?(key) if h.respond_to?(:deprecated_key?) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def valid_key?(key)
|
101
|
+
include?(key) || @valid.include?(key) ||
|
102
|
+
@map.any? {|h| h.valid_key?(key) if h.respond_to?(:valid_key?) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|