temple 0.4.1 → 0.5.4
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/.travis.yml +1 -0
- data/CHANGES +34 -0
- data/lib/temple.rb +5 -2
- data/lib/temple/engine.rb +3 -2
- data/lib/temple/erb/parser.rb +1 -3
- data/lib/temple/erb/trimming.rb +2 -0
- data/lib/temple/exceptions.rb +13 -0
- data/lib/temple/filters/control_flow.rb +2 -3
- data/lib/temple/filters/eraser.rb +1 -1
- data/lib/temple/filters/escapable.rb +3 -2
- data/lib/temple/filters/validator.rb +1 -1
- data/lib/temple/generators.rb +8 -6
- data/lib/temple/grammar.rb +1 -1
- data/lib/temple/hash.rb +56 -0
- data/lib/temple/html/attribute_merger.rb +28 -29
- data/lib/temple/html/attribute_remover.rb +11 -4
- data/lib/temple/html/attribute_sorter.rb +9 -5
- data/lib/temple/html/fast.rb +7 -7
- data/lib/temple/html/filter.rb +13 -0
- data/lib/temple/html/pretty.rb +8 -8
- data/lib/temple/mixins/engine_dsl.rb +3 -3
- data/lib/temple/mixins/grammar_dsl.rb +1 -1
- data/lib/temple/mixins/options.rb +64 -7
- data/lib/temple/mixins/template.rb +8 -22
- data/lib/temple/parser.rb +8 -0
- data/lib/temple/templates/rails.rb +13 -7
- data/lib/temple/templates/tilt.rb +17 -8
- data/lib/temple/utils.rb +3 -17
- data/lib/temple/version.rb +1 -1
- data/temple.gemspec +1 -1
- data/test/filters/test_control_flow.rb +4 -6
- data/test/html/test_attribute_merger.rb +8 -14
- data/test/html/test_attribute_remover.rb +5 -6
- data/test/test_engine.rb +0 -8
- data/test/test_erb.rb +2 -2
- data/test/test_filter.rb +4 -1
- metadata +12 -25
data/.travis.yml
CHANGED
data/CHANGES
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
0.5.4
|
2
|
+
|
3
|
+
* HTML::AttributeMerger fixed, it didn't remove first empty attribute values
|
4
|
+
* Add HTML::AttributeRemover back, :remove_empty_attrs must be an Array of Strings now
|
5
|
+
of the attributes to be removed if empty
|
6
|
+
* Simplify [:case] expression grammar
|
7
|
+
* Ignore parameter :outvar by sinatra since sinatra assumes also that the buffer is a String,
|
8
|
+
they should set :buffer and :generator explicitly if they need the access
|
9
|
+
|
10
|
+
0.5.3
|
11
|
+
|
12
|
+
* Only print an message if invalid options are passed to Temple filters or engines
|
13
|
+
since many libraries seem to use Slim and Temple in an incorrect way
|
14
|
+
|
15
|
+
0.5.2
|
16
|
+
|
17
|
+
* Fix the :outvar problem really
|
18
|
+
|
19
|
+
0.5.1
|
20
|
+
|
21
|
+
* Support Sinatra :outvar option in Tilt template
|
22
|
+
|
23
|
+
0.5.0
|
24
|
+
|
25
|
+
* Added exception Temple::FilterError which should be thrown by filters
|
26
|
+
* Added Temple::Parser as default base class for parsers
|
27
|
+
* escape_html doesn't escape / anymore
|
28
|
+
* HTML::AttributeSorter uses stable sorting now
|
29
|
+
* HTML::AttributeRemover removed (Was too Slim specific)
|
30
|
+
* Engine option :chain removed
|
31
|
+
* Option validation implemented (Use define_options in your filters)
|
32
|
+
* Deprecated options implemented (Use deprecated_options in your filters)
|
33
|
+
* ThreadOptions added, Method #with_options
|
34
|
+
|
1
35
|
0.4.1
|
2
36
|
|
3
37
|
* Generators: produce optimized code
|
data/lib/temple.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'temple/version'
|
2
2
|
|
3
3
|
module Temple
|
4
|
-
autoload :InvalidExpression, 'temple/
|
4
|
+
autoload :InvalidExpression, 'temple/exceptions'
|
5
|
+
autoload :FilterError, 'temple/exceptions'
|
5
6
|
autoload :Generator, 'temple/generators'
|
6
7
|
autoload :Generators, 'temple/generators'
|
8
|
+
autoload :Parser, 'temple/parser'
|
7
9
|
autoload :Engine, 'temple/engine'
|
8
10
|
autoload :Utils, 'temple/utils'
|
9
11
|
autoload :Filter, 'temple/filter'
|
@@ -11,6 +13,7 @@ module Temple
|
|
11
13
|
autoload :Grammar, 'temple/grammar'
|
12
14
|
autoload :ImmutableHash, 'temple/hash'
|
13
15
|
autoload :MutableHash, 'temple/hash'
|
16
|
+
autoload :OptionHash, 'temple/hash'
|
14
17
|
|
15
18
|
module Mixins
|
16
19
|
autoload :Dispatcher, 'temple/mixins/dispatcher'
|
@@ -45,7 +48,7 @@ module Temple
|
|
45
48
|
autoload :Fast, 'temple/html/fast'
|
46
49
|
autoload :Pretty, 'temple/html/pretty'
|
47
50
|
autoload :AttributeMerger, 'temple/html/attribute_merger'
|
48
|
-
autoload :AttributeSorter, 'temple/html/attribute_sorter'
|
49
51
|
autoload :AttributeRemover, 'temple/html/attribute_remover'
|
52
|
+
autoload :AttributeSorter, 'temple/html/attribute_sorter'
|
50
53
|
end
|
51
54
|
end
|
data/lib/temple/engine.rb
CHANGED
@@ -33,16 +33,17 @@ module Temple
|
|
33
33
|
include Mixins::EngineDSL
|
34
34
|
extend Mixins::EngineDSL
|
35
35
|
|
36
|
+
define_options :file, :streaming, :buffer
|
37
|
+
|
36
38
|
attr_reader :chain
|
37
39
|
|
38
40
|
def self.chain
|
39
41
|
@chain ||= superclass.respond_to?(:chain) ? superclass.chain.dup : []
|
40
42
|
end
|
41
43
|
|
42
|
-
def initialize(
|
44
|
+
def initialize(opts = {})
|
43
45
|
super
|
44
46
|
@chain = self.class.chain.dup
|
45
|
-
[*options[:chain]].compact.each {|block| block.call(self) }
|
46
47
|
end
|
47
48
|
|
48
49
|
def call(input)
|
data/lib/temple/erb/parser.rb
CHANGED
data/lib/temple/erb/trimming.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Temple
|
2
|
+
# Exception raised if invalid temple expression is found
|
3
|
+
#
|
4
|
+
# @api public
|
5
|
+
class InvalidExpression < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Exception raised if something bad happens in a Temple filter
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class FilterError < RuntimeError
|
12
|
+
end
|
13
|
+
end
|
@@ -20,9 +20,8 @@ module Temple
|
|
20
20
|
def on_case(arg, *cases)
|
21
21
|
result = [:multi, [:code, arg ? "case (#{arg})" : 'case']]
|
22
22
|
cases.map do |c|
|
23
|
-
condition,
|
24
|
-
result << [:code, condition == :else ? 'else' : "when #{condition}"]
|
25
|
-
exps.each {|e| result << compile(e) }
|
23
|
+
condition, exp = c
|
24
|
+
result << [:code, condition == :else ? 'else' : "when #{condition}"] << compile(exp)
|
26
25
|
end
|
27
26
|
result << [:code, 'end']
|
28
27
|
result
|
@@ -8,8 +8,9 @@ module Temple
|
|
8
8
|
# @api public
|
9
9
|
class Escapable < Filter
|
10
10
|
# Activate the usage of html_safe? if it is available (for Rails 3 for example)
|
11
|
-
|
12
|
-
|
11
|
+
define_options :escape_code,
|
12
|
+
:use_html_safe => ''.respond_to?(:html_safe?),
|
13
|
+
:disable_escape => false
|
13
14
|
|
14
15
|
def initialize(opts = {})
|
15
16
|
super
|
data/lib/temple/generators.rb
CHANGED
@@ -14,7 +14,8 @@ module Temple
|
|
14
14
|
include Mixins::CompiledDispatcher
|
15
15
|
include Mixins::Options
|
16
16
|
|
17
|
-
|
17
|
+
define_options :capture_generator,
|
18
|
+
:buffer => '_buf'
|
18
19
|
|
19
20
|
def call(exp)
|
20
21
|
[preamble, compile(exp), postamble].join('; ')
|
@@ -134,10 +135,11 @@ module Temple
|
|
134
135
|
#
|
135
136
|
# @api public
|
136
137
|
class RailsOutputBuffer < StringBuffer
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
138
|
+
define_options :streaming,
|
139
|
+
:buffer_class => 'ActiveSupport::SafeBuffer',
|
140
|
+
:buffer => '@output_buffer',
|
141
|
+
# output_buffer is needed for Rails 3.1 Streaming support
|
142
|
+
:capture_generator => RailsOutputBuffer
|
141
143
|
|
142
144
|
def call(exp)
|
143
145
|
[preamble, compile(exp), postamble].join('; ')
|
@@ -157,5 +159,5 @@ module Temple
|
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
160
|
-
Generator.default_options[:capture_generator] =
|
162
|
+
Generator.default_options[:capture_generator] = Generators::StringBuffer
|
161
163
|
end
|
data/lib/temple/grammar.rb
CHANGED
data/lib/temple/hash.rb
CHANGED
@@ -28,6 +28,12 @@ module Temple
|
|
28
28
|
def values
|
29
29
|
keys.map {|k| self[k] }
|
30
30
|
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
result = {}
|
34
|
+
each {|k, v| result[k] = v }
|
35
|
+
result
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
# Mutable hash class which supports hash merging
|
@@ -45,4 +51,54 @@ module Temple
|
|
45
51
|
@hash.first.update(hash)
|
46
52
|
end
|
47
53
|
end
|
54
|
+
|
55
|
+
class OptionHash < MutableHash
|
56
|
+
def initialize(*hash, &block)
|
57
|
+
super(*hash)
|
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(hash)
|
69
|
+
validate_hash!(hash)
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_keys
|
74
|
+
keys.concat(@valid.keys).uniq
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_valid_keys(*keys)
|
78
|
+
keys.flatten.each { |key| @valid[key] = true }
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_deprecated_keys(*keys)
|
82
|
+
keys.flatten.each { |key| @valid[key] = @deprecated[key] = true }
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_hash!(hash)
|
86
|
+
hash.to_hash.keys.each {|key| validate_key!(key) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate_key!(key)
|
90
|
+
@handler.call(self, key, true) if deprecated_key?(key)
|
91
|
+
@handler.call(self, key, false) unless valid_key?(key)
|
92
|
+
end
|
93
|
+
|
94
|
+
def deprecated_key?(key)
|
95
|
+
@deprecated.include?(key) ||
|
96
|
+
@hash.any? {|h| h.deprecated_key?(key) if h.respond_to?(:deprecated_key?) }
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_key?(key)
|
100
|
+
include?(key) || @valid.include?(key) ||
|
101
|
+
@hash.any? {|h| h.valid_key?(key) if h.respond_to?(:valid_key?) }
|
102
|
+
end
|
103
|
+
end
|
48
104
|
end
|
@@ -3,45 +3,44 @@ 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
|
-
|
6
|
+
define_options :attr_delimiter => {'id' => '_', 'class' => ' '}
|
7
7
|
|
8
8
|
def on_html_attrs(*attrs)
|
9
9
|
names = []
|
10
|
-
|
10
|
+
values = {}
|
11
|
+
|
11
12
|
attrs.each do |attr|
|
12
|
-
raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
|
13
13
|
name, value = attr[2].to_s, attr[3]
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
14
|
+
if values[name]
|
15
|
+
raise(FilterError, "Multiple #{name} attributes specified") unless options[:attr_delimiter][name]
|
16
|
+
values[name] << value
|
17
|
+
else
|
18
|
+
values[name] = [value]
|
19
|
+
names << name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attrs = names.map do |name|
|
24
|
+
value = values[name]
|
25
|
+
if (delimiter = options[:attr_delimiter][name]) && value.size > 1
|
26
|
+
exp = [:multi]
|
27
|
+
if value.all? {|v| contains_nonempty_static?(v) }
|
28
|
+
exp << value.first
|
29
|
+
value[1..-1].each {|v| exp << [:static, delimiter] << v }
|
30
|
+
[:html, :attr, name, exp]
|
28
31
|
else
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
[:capture, tmp, value],
|
34
|
-
[:if, "!#{tmp}.empty?",
|
35
|
-
[:multi,
|
36
|
-
[:static, delimiter],
|
37
|
-
[:dynamic, tmp]]]]]
|
32
|
+
captures = unique_name
|
33
|
+
exp << [:code, "#{captures} = []"]
|
34
|
+
value.each_with_index {|v, i| exp << [:capture, "#{captures}[#{i}]", v] }
|
35
|
+
exp << [:dynamic, "#{captures}.reject(&:empty?).join(#{delimiter.inspect})"]
|
38
36
|
end
|
37
|
+
[:html, :attr, name, exp]
|
39
38
|
else
|
40
|
-
|
41
|
-
names << name
|
39
|
+
[:html, :attr, name, value.first]
|
42
40
|
end
|
43
41
|
end
|
44
|
-
|
42
|
+
|
43
|
+
[:html, :attrs, *attrs]
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
@@ -3,17 +3,24 @@ module Temple
|
|
3
3
|
# This filter removes empty attributes
|
4
4
|
# @api public
|
5
5
|
class AttributeRemover < Filter
|
6
|
-
|
6
|
+
define_options :remove_empty_attrs => %w(id class)
|
7
|
+
|
8
|
+
def initialize(opts = {})
|
9
|
+
super
|
10
|
+
raise ArgumentError, "Option :remove_empty_attrs must be an Array of Strings" unless Array === options[:remove_empty_attrs] &&
|
11
|
+
options[:remove_empty_attrs].all? {|a| String === a }
|
12
|
+
end
|
7
13
|
|
8
14
|
def on_html_attrs(*attrs)
|
9
|
-
[:multi, *(
|
10
|
-
attrs.map {|attr| compile(attr) } : attrs)]
|
15
|
+
[:multi, *attrs.map {|attr| compile(attr) }]
|
11
16
|
end
|
12
17
|
|
13
18
|
def on_html_attr(name, value)
|
19
|
+
return super unless options[:remove_empty_attrs].include?(name.to_s)
|
20
|
+
|
14
21
|
if empty_exp?(value)
|
15
22
|
value
|
16
|
-
elsif
|
23
|
+
elsif contains_nonempty_static?(value)
|
17
24
|
[:html, :attr, name, value]
|
18
25
|
else
|
19
26
|
tmp = unique_name
|
@@ -3,14 +3,18 @@ module Temple
|
|
3
3
|
# This filter sorts html attributes.
|
4
4
|
# @api public
|
5
5
|
class AttributeSorter < Filter
|
6
|
-
|
6
|
+
define_options :sort_attrs => true
|
7
|
+
|
8
|
+
def call(exp)
|
9
|
+
options[:sort_attrs] ? super : exp
|
10
|
+
end
|
7
11
|
|
8
12
|
def on_html_attrs(*attrs)
|
9
|
-
|
13
|
+
n = 0 # Use n to make sort stable. This is important because the merger could be executed afterwards.
|
14
|
+
[:html, :attrs, *attrs.sort_by do |attr|
|
10
15
|
raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
|
11
|
-
attr[2].to_s
|
12
|
-
end
|
13
|
-
[:html, :attrs, *attrs]
|
16
|
+
[attr[2].to_s, n += 1]
|
17
|
+
end]
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
data/lib/temple/html/fast.rb
CHANGED
@@ -21,16 +21,16 @@ module Temple
|
|
21
21
|
'transitional' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
|
22
22
|
}.freeze
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
define_options :format => :xhtml,
|
25
|
+
:attr_wrapper => "'",
|
26
|
+
:autoclose => %w[meta img link br hr input area param col base]
|
27
27
|
|
28
28
|
HTML = [:html, :html4, :html5]
|
29
29
|
|
30
30
|
def initialize(opts = {})
|
31
31
|
super
|
32
32
|
unless [:xhtml, *HTML].include?(options[:format])
|
33
|
-
raise "Invalid format #{options[:format].inspect}"
|
33
|
+
raise ArgumentError, "Invalid format #{options[:format].inspect}"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -46,13 +46,13 @@ module Temple
|
|
46
46
|
type = type.to_s.downcase
|
47
47
|
|
48
48
|
if type =~ /^xml(\s+(.+))?$/
|
49
|
-
raise 'Invalid xml directive in html mode' if html?
|
49
|
+
raise(FilterError, 'Invalid xml directive in html mode') if html?
|
50
50
|
w = options[:attr_wrapper]
|
51
51
|
str = "<?xml version=#{w}1.0#{w} encoding=#{w}#{$2 || 'utf-8'}#{w} ?>"
|
52
52
|
elsif html?
|
53
|
-
str = HTML_DOCTYPES[type] || raise("Invalid html doctype #{type}")
|
53
|
+
str = HTML_DOCTYPES[type] || raise(FilterError, "Invalid html doctype #{type}")
|
54
54
|
else
|
55
|
-
str = XHTML_DOCTYPES[type] || raise("Invalid xhtml doctype #{type}")
|
55
|
+
str = XHTML_DOCTYPES[type] || raise(FilterError, "Invalid xhtml doctype #{type}")
|
56
56
|
end
|
57
57
|
|
58
58
|
[:static, str]
|
data/lib/temple/html/filter.rb
CHANGED
@@ -3,6 +3,19 @@ module Temple
|
|
3
3
|
# @api public
|
4
4
|
class Filter < Temple::Filter
|
5
5
|
include Dispatcher
|
6
|
+
|
7
|
+
def contains_nonempty_static?(exp)
|
8
|
+
case exp.first
|
9
|
+
when :multi
|
10
|
+
exp[1..-1].any? {|e| contains_nonempty_static?(e) }
|
11
|
+
when :escape
|
12
|
+
contains_nonempty_static?(exp.last)
|
13
|
+
when :static
|
14
|
+
!exp.last.empty?
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
6
19
|
end
|
7
20
|
end
|
8
21
|
end
|
data/lib/temple/html/pretty.rb
CHANGED
@@ -2,14 +2,14 @@ module Temple
|
|
2
2
|
module HTML
|
3
3
|
# @api public
|
4
4
|
class Pretty < Fast
|
5
|
-
|
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 p
|
10
|
+
rp rt ruby section script style table tbody td tfoot
|
11
|
+
th thead title tr ul video).freeze,
|
12
|
+
:pre_tags => %w(code pre textarea).freeze
|
13
13
|
|
14
14
|
def initialize(opts = {})
|
15
15
|
super
|
@@ -96,11 +96,11 @@ module Temple
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def chain_class_constructor(filter, option_filter)
|
99
|
-
local_options =
|
99
|
+
local_options = option_filter.last.respond_to?(:to_hash) ? option_filter.pop.to_hash : {}
|
100
100
|
raise(ArgumentError, 'Only symbols allowed in option filter') unless option_filter.all? {|o| Symbol === o }
|
101
|
+
define_options(*option_filter) if respond_to?(:define_options)
|
101
102
|
proc do |engine|
|
102
|
-
|
103
|
-
filter.new(ImmutableHash.new(local_options, filtered_options))
|
103
|
+
filter.new({}.update(engine.options).delete_if {|k,v| !option_filter.include?(k) }.update(local_options))
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -2,26 +2,83 @@ module Temple
|
|
2
2
|
module Mixins
|
3
3
|
# @api public
|
4
4
|
module DefaultOptions
|
5
|
-
def set_default_options(
|
6
|
-
default_options.update(
|
5
|
+
def set_default_options(opts)
|
6
|
+
default_options.update(opts)
|
7
7
|
end
|
8
8
|
|
9
9
|
def default_options
|
10
|
-
@default_options ||=
|
11
|
-
|
10
|
+
@default_options ||= OptionHash.new(superclass.respond_to?(:default_options) ?
|
11
|
+
superclass.default_options : nil) do |hash, key, deprecated|
|
12
|
+
unless @option_validator_disabled
|
13
|
+
if deprecated
|
14
|
+
puts "Option #{key.inspect} is deprecated by #{self}"
|
15
|
+
else
|
16
|
+
# TODO: This will raise an exception in the future!
|
17
|
+
# raise ArgumentError, "Option #{key.inspect} is not supported by #{self}"
|
18
|
+
puts "Option #{key.inspect} is not supported by #{self}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_options(*opts)
|
25
|
+
if opts.last.respond_to?(:to_hash)
|
26
|
+
hash = opts.pop.to_hash
|
27
|
+
default_options.add_valid_keys(hash.keys)
|
28
|
+
default_options.update(hash)
|
29
|
+
end
|
30
|
+
default_options.add_valid_keys(opts)
|
31
|
+
end
|
32
|
+
|
33
|
+
def define_deprecated_options(*opts)
|
34
|
+
if opts.last.respond_to?(:to_hash)
|
35
|
+
hash = opts.pop.to_hash
|
36
|
+
default_options.add_deprecated_keys(hash.keys)
|
37
|
+
default_options.update(hash)
|
38
|
+
end
|
39
|
+
default_options.add_deprecated_keys(opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
def disable_option_validator!
|
43
|
+
@option_validator_disabled = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module ThreadOptions
|
48
|
+
def with_options(options)
|
49
|
+
old_options = thread_options
|
50
|
+
Thread.current[thread_options_key] = ImmutableHash.new(options, thread_options)
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
Thread.current[thread_options_key] = old_options
|
54
|
+
end
|
55
|
+
|
56
|
+
def thread_options
|
57
|
+
Thread.current[thread_options_key]
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def thread_options_key
|
63
|
+
@thread_options_key ||= "#{self.name}-thread-options".to_sym
|
12
64
|
end
|
13
65
|
end
|
14
66
|
|
15
67
|
# @api public
|
16
68
|
module Options
|
17
69
|
def self.included(base)
|
18
|
-
base.class_eval
|
70
|
+
base.class_eval do
|
71
|
+
extend DefaultOptions
|
72
|
+
extend ThreadOptions
|
73
|
+
end
|
19
74
|
end
|
20
75
|
|
21
76
|
attr_reader :options
|
22
77
|
|
23
|
-
def initialize(
|
24
|
-
|
78
|
+
def initialize(opts = {})
|
79
|
+
self.class.default_options.validate_hash!(opts)
|
80
|
+
self.class.default_options.validate_hash!(self.class.thread_options) if self.class.thread_options
|
81
|
+
@options = ImmutableHash.new({}.update(self.class.default_options).update(self.class.thread_options || {}).update(opts))
|
25
82
|
end
|
26
83
|
end
|
27
84
|
end
|
@@ -4,39 +4,25 @@ module Temple
|
|
4
4
|
module Template
|
5
5
|
include DefaultOptions
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def init
|
13
|
-
# Overwrite this for class initialization
|
7
|
+
def compile(code, options)
|
8
|
+
engine = options.delete(:engine)
|
9
|
+
raise 'No engine configured' unless engine
|
10
|
+
engine.new(options).call(code)
|
14
11
|
end
|
15
12
|
|
16
|
-
def register_as(
|
13
|
+
def register_as(*names)
|
17
14
|
raise NotImplementedError
|
18
15
|
end
|
19
16
|
|
20
17
|
def create(engine, options)
|
18
|
+
register_as = options.delete(:register_as)
|
21
19
|
template = Class.new(self)
|
20
|
+
template.disable_option_validator!
|
22
21
|
template.default_options[:engine] = engine
|
23
22
|
template.default_options.update(options)
|
24
|
-
template.
|
25
|
-
template.register_as(options[:register_as]) if options[:register_as]
|
23
|
+
template.register_as(*register_as) if register_as
|
26
24
|
template
|
27
25
|
end
|
28
|
-
|
29
|
-
def build_engine(*options)
|
30
|
-
raise 'No engine configured' unless engine
|
31
|
-
options << default_options
|
32
|
-
engine.new(ImmutableHash.new(*options))
|
33
|
-
end
|
34
|
-
|
35
|
-
def chain(&block)
|
36
|
-
chain = (default_options[:chain] ||= [])
|
37
|
-
chain << block if block
|
38
|
-
chain
|
39
|
-
end
|
40
26
|
end
|
41
27
|
end
|
42
28
|
end
|
@@ -14,12 +14,15 @@ module Temple
|
|
14
14
|
extend Mixins::Template
|
15
15
|
|
16
16
|
def compile(template)
|
17
|
-
|
18
|
-
|
17
|
+
# Overwrite option: No streaming support in Rails < 3.1
|
18
|
+
opts = {}.update(self.class.default_options).update(:file => template.identifier, :streaming => false)
|
19
|
+
self.class.compile(template.source, opts)
|
19
20
|
end
|
20
21
|
|
21
|
-
def self.register_as(
|
22
|
-
|
22
|
+
def self.register_as(*names)
|
23
|
+
names.each do |name|
|
24
|
+
ActionView::Template.register_template_handler name.to_sym, self
|
25
|
+
end
|
23
26
|
end
|
24
27
|
end
|
25
28
|
else
|
@@ -27,15 +30,18 @@ module Temple
|
|
27
30
|
extend Mixins::Template
|
28
31
|
|
29
32
|
def call(template)
|
30
|
-
self.class.
|
33
|
+
opts = {}.update(self.class.default_options).update(:file => template.identifier)
|
34
|
+
self.class.compile(template.source, opts)
|
31
35
|
end
|
32
36
|
|
33
37
|
def supports_streaming?
|
34
38
|
self.class.default_options[:streaming]
|
35
39
|
end
|
36
40
|
|
37
|
-
def self.register_as(
|
38
|
-
|
41
|
+
def self.register_as(*names)
|
42
|
+
names.each do |name|
|
43
|
+
ActionView::Template.register_template_handler name.to_sym, new
|
44
|
+
end
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
@@ -5,14 +5,27 @@ module Temple
|
|
5
5
|
class Tilt < ::Tilt::Template
|
6
6
|
extend Mixins::Template
|
7
7
|
|
8
|
+
define_options :mime_type => 'text/html'
|
9
|
+
|
10
|
+
def self.default_mime_type
|
11
|
+
default_options[:mime_type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.default_mime_type=(mime_type)
|
15
|
+
default_options[:mime_type] = mime_type
|
16
|
+
end
|
17
|
+
|
8
18
|
# Prepare Temple template
|
9
19
|
#
|
10
20
|
# Called immediately after template data is loaded.
|
11
21
|
#
|
12
22
|
# @return [void]
|
13
23
|
def prepare
|
14
|
-
|
15
|
-
|
24
|
+
# Overwrite option: No streaming support in Tilt
|
25
|
+
opts = {}.update(self.class.default_options).update(options).update(:file => eval_file, :streaming => false)
|
26
|
+
opts.delete(:mime_type)
|
27
|
+
opts.delete(:outvar) # Sinatra gives us this invalid variable
|
28
|
+
@src = self.class.compile(data, opts)
|
16
29
|
end
|
17
30
|
|
18
31
|
# A string containing the (Ruby) source code for the template.
|
@@ -23,12 +36,8 @@ module Temple
|
|
23
36
|
@src
|
24
37
|
end
|
25
38
|
|
26
|
-
def self.
|
27
|
-
self.
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.register_as(name)
|
31
|
-
::Tilt.register name.to_s, self
|
39
|
+
def self.register_as(*names)
|
40
|
+
::Tilt.register(self, *names.map(&:to_s))
|
32
41
|
end
|
33
42
|
end
|
34
43
|
end
|
data/lib/temple/utils.rb
CHANGED
@@ -18,7 +18,7 @@ module Temple
|
|
18
18
|
# @param html [String] The string to escape
|
19
19
|
# @return [String] The escaped string
|
20
20
|
def escape_html(html)
|
21
|
-
EscapeUtils.escape_html(html.to_s)
|
21
|
+
EscapeUtils.escape_html(html.to_s, false)
|
22
22
|
end
|
23
23
|
elsif RUBY_VERSION > '1.9'
|
24
24
|
# Used by escape_html
|
@@ -28,7 +28,6 @@ module Temple
|
|
28
28
|
'"' => '"',
|
29
29
|
'<' => '<',
|
30
30
|
'>' => '>',
|
31
|
-
'/' => '/',
|
32
31
|
}.freeze
|
33
32
|
|
34
33
|
# Returns an escaped copy of `html`.
|
@@ -36,7 +35,7 @@ module Temple
|
|
36
35
|
# @param html [String] The string to escape
|
37
36
|
# @return [String] The escaped string
|
38
37
|
def escape_html(html)
|
39
|
-
html.to_s.gsub(/[&\"
|
38
|
+
html.to_s.gsub(/[&\"<>]/, ESCAPE_HTML)
|
40
39
|
end
|
41
40
|
else
|
42
41
|
# Returns an escaped copy of `html`.
|
@@ -44,7 +43,7 @@ module Temple
|
|
44
43
|
# @param html [String] The string to escape
|
45
44
|
# @return [String] The escaped string
|
46
45
|
def escape_html(html)
|
47
|
-
html.to_s.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<')
|
46
|
+
html.to_s.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<')
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
@@ -58,19 +57,6 @@ module Temple
|
|
58
57
|
"_#{prefix}#{@unique_name += 1}"
|
59
58
|
end
|
60
59
|
|
61
|
-
def contains_static?(exp)
|
62
|
-
case exp[0]
|
63
|
-
when :multi
|
64
|
-
exp[1..-1].any? {|e| contains_static?(e) }
|
65
|
-
when :escape
|
66
|
-
contains_static?(exp[2])
|
67
|
-
when :static
|
68
|
-
true
|
69
|
-
else
|
70
|
-
false
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
60
|
# Check if expression is empty
|
75
61
|
#
|
76
62
|
# @param exp [Array] Temple expression
|
data/lib/temple/version.rb
CHANGED
data/temple.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
|
10
10
|
s.authors = ['Magnus Holm', 'Daniel Mendler']
|
11
11
|
s.email = ['judofyr@gmail.com', 'mail@daniel-mendler.de']
|
12
|
-
s.homepage = '
|
12
|
+
s.homepage = 'https://github.com/judofyr/temple'
|
13
13
|
s.summary = 'Template compilation framework in Ruby'
|
14
14
|
|
15
15
|
s.require_paths = %w(lib)
|
@@ -56,14 +56,13 @@ describe Temple::Filters::ControlFlow do
|
|
56
56
|
|
57
57
|
it 'should process cond' do
|
58
58
|
@filter.call([:cond,
|
59
|
-
['cond1', [:
|
59
|
+
['cond1', [:exp1]],
|
60
60
|
['cond2', [:exp2]],
|
61
61
|
[:else, [:exp3]],
|
62
62
|
]).should.equal [:multi,
|
63
63
|
[:code, 'case'],
|
64
64
|
[:code, 'when cond1'],
|
65
|
-
[:
|
66
|
-
[:exp12],
|
65
|
+
[:exp1],
|
67
66
|
[:code, 'when cond2'],
|
68
67
|
[:exp2],
|
69
68
|
[:code, 'else'],
|
@@ -74,14 +73,13 @@ describe Temple::Filters::ControlFlow do
|
|
74
73
|
|
75
74
|
it 'should process case' do
|
76
75
|
@filter.call([:case, 'var',
|
77
|
-
['Array', [:
|
76
|
+
['Array', [:exp1]],
|
78
77
|
['String', [:exp2]],
|
79
78
|
[:else, [:exp3]],
|
80
79
|
]).should.equal [:multi,
|
81
80
|
[:code, 'case (var)'],
|
82
81
|
[:code, 'when Array'],
|
83
|
-
[:
|
84
|
-
[:exp12],
|
82
|
+
[:exp1],
|
85
83
|
[:code, 'when String'],
|
86
84
|
[:exp2],
|
87
85
|
[:code, 'else'],
|
@@ -50,13 +50,10 @@ describe Temple::HTML::AttributeMerger do
|
|
50
50
|
[:html, :attrs,
|
51
51
|
[:html, :attr, "id",
|
52
52
|
[:multi,
|
53
|
-
[:
|
54
|
-
[:capture, "_temple_html_attributemerger1",
|
55
|
-
|
56
|
-
[:
|
57
|
-
[:multi,
|
58
|
-
[:static, "_"],
|
59
|
-
[:dynamic, "_temple_html_attributemerger1"]]]]]],
|
53
|
+
[:code, "_temple_html_attributemerger1 = []"],
|
54
|
+
[:capture, "_temple_html_attributemerger1[0]", [:dynamic, "a"]],
|
55
|
+
[:capture, "_temple_html_attributemerger1[1]", [:dynamic, "b"]],
|
56
|
+
[:dynamic, "_temple_html_attributemerger1.reject(&:empty?).join(\"_\")"]]]],
|
60
57
|
[:content]]
|
61
58
|
end
|
62
59
|
|
@@ -69,13 +66,10 @@ describe Temple::HTML::AttributeMerger do
|
|
69
66
|
[:html, :attrs,
|
70
67
|
[:html, :attr, "class",
|
71
68
|
[:multi,
|
72
|
-
[:
|
73
|
-
[:capture, "_temple_html_attributemerger1",
|
74
|
-
|
75
|
-
[:
|
76
|
-
[:multi,
|
77
|
-
[:static, " "],
|
78
|
-
[:dynamic, "_temple_html_attributemerger1"]]]]]],
|
69
|
+
[:code, "_temple_html_attributemerger1 = []"],
|
70
|
+
[:capture, "_temple_html_attributemerger1[0]", [:static, "a"]],
|
71
|
+
[:capture, "_temple_html_attributemerger1[1]", [:dynamic, "b"]],
|
72
|
+
[:dynamic, "_temple_html_attributemerger1.reject(&:empty?).join(\" \")"]]]],
|
79
73
|
[:content]]
|
80
74
|
end
|
81
75
|
end
|
@@ -3,7 +3,6 @@ require 'helper'
|
|
3
3
|
describe Temple::HTML::AttributeRemover do
|
4
4
|
before do
|
5
5
|
@remover = Temple::HTML::AttributeRemover.new
|
6
|
-
@disabled_remover = Temple::HTML::AttributeRemover.new :remove_empty_attrs => false
|
7
6
|
end
|
8
7
|
|
9
8
|
it 'should pass static attributes through' do
|
@@ -17,7 +16,7 @@ describe Temple::HTML::AttributeRemover do
|
|
17
16
|
[:content]]
|
18
17
|
end
|
19
18
|
|
20
|
-
it 'should check for empty dynamic attribute if
|
19
|
+
it 'should check for empty dynamic attribute if it is included in :remove_empty_attrs' do
|
21
20
|
@remover.call([:html, :tag,
|
22
21
|
'div',
|
23
22
|
[:html, :attrs, [:html, :attr, 'class', [:dynamic, 'b']]],
|
@@ -31,14 +30,14 @@ describe Temple::HTML::AttributeRemover do
|
|
31
30
|
[:content]]
|
32
31
|
end
|
33
32
|
|
34
|
-
it 'should not check for empty dynamic attribute if
|
35
|
-
@
|
33
|
+
it 'should not check for empty dynamic attribute if it is not included in :remove_empty_attrs' do
|
34
|
+
@remover.call([:html, :tag,
|
36
35
|
'div',
|
37
|
-
[:html, :attrs, [:html, :attr, '
|
36
|
+
[:html, :attrs, [:html, :attr, 'name', [:dynamic, 'b']]],
|
38
37
|
[:content]
|
39
38
|
]).should.equal [:html, :tag, "div",
|
40
39
|
[:multi,
|
41
|
-
[:html, :attr, "
|
40
|
+
[:html, :attr, "name", [:dynamic, "b"]]],
|
42
41
|
[:content]]
|
43
42
|
end
|
44
43
|
end
|
data/test/test_engine.rb
CHANGED
@@ -167,12 +167,4 @@ describe Temple::Engine do
|
|
167
167
|
inherited_engine.chain.size.should.equal 9
|
168
168
|
TestEngine.chain.size.should.equal 8
|
169
169
|
end
|
170
|
-
|
171
|
-
it 'should support chain option' do
|
172
|
-
engine = TestEngine.new(:chain => proc {|e| e.remove :MyFilter1 })
|
173
|
-
TestEngine.chain.size.should.equal 8
|
174
|
-
engine.chain.size.should.equal 7
|
175
|
-
engine.chain[0].first.should.equal :Parser
|
176
|
-
engine.chain[1].first.should.equal :MyFilter2
|
177
|
-
end
|
178
170
|
end
|
data/test/test_erb.rb
CHANGED
@@ -38,13 +38,13 @@ world}
|
|
38
38
|
it 'should escape automatically' do
|
39
39
|
src = '<%= "<" %>'
|
40
40
|
ans = '<'
|
41
|
-
erb(src
|
41
|
+
erb(src).should.equal ans
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'should support == to disable automatic escape' do
|
45
45
|
src = '<%== "<" %>'
|
46
46
|
ans = '<'
|
47
|
-
erb(src
|
47
|
+
erb(src).should.equal ans
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'should support trim mode' do
|
data/test/test_filter.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class SimpleFilter < Temple::Filter
|
4
|
+
define_options :key
|
5
|
+
|
4
6
|
def on_test(arg)
|
5
7
|
[:on_test, arg]
|
6
8
|
end
|
@@ -10,8 +12,9 @@ describe Temple::Filter do
|
|
10
12
|
it 'should support options' do
|
11
13
|
Temple::Filter.should.respond_to :default_options
|
12
14
|
Temple::Filter.should.respond_to :set_default_options
|
15
|
+
Temple::Filter.should.respond_to :define_options
|
13
16
|
Temple::Filter.new.options.should.be.instance_of Temple::ImmutableHash
|
14
|
-
|
17
|
+
SimpleFilter.new(:key => 3).options[:key].should.equal 3
|
15
18
|
end
|
16
19
|
|
17
20
|
it 'should implement call' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: temple
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4
|
4
|
+
version: 0.5.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-09-
|
13
|
+
date: 2012-09-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: tilt
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirement: &6391160 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,15 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
26
|
-
none: false
|
27
|
-
requirements:
|
28
|
-
- - ! '>='
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: '0'
|
25
|
+
version_requirements: *6391160
|
31
26
|
- !ruby/object:Gem::Dependency
|
32
27
|
name: bacon
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &6390120 !ruby/object:Gem::Requirement
|
34
29
|
none: false
|
35
30
|
requirements:
|
36
31
|
- - ! '>='
|
@@ -38,15 +33,10 @@ dependencies:
|
|
38
33
|
version: '0'
|
39
34
|
type: :development
|
40
35
|
prerelease: false
|
41
|
-
version_requirements:
|
42
|
-
none: false
|
43
|
-
requirements:
|
44
|
-
- - ! '>='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '0'
|
36
|
+
version_requirements: *6390120
|
47
37
|
- !ruby/object:Gem::Dependency
|
48
38
|
name: rake
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirement: &6389580 !ruby/object:Gem::Requirement
|
50
40
|
none: false
|
51
41
|
requirements:
|
52
42
|
- - ! '>='
|
@@ -54,12 +44,7 @@ dependencies:
|
|
54
44
|
version: '0'
|
55
45
|
type: :development
|
56
46
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
none: false
|
59
|
-
requirements:
|
60
|
-
- - ! '>='
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
47
|
+
version_requirements: *6389580
|
63
48
|
description:
|
64
49
|
email:
|
65
50
|
- judofyr@gmail.com
|
@@ -83,6 +68,7 @@ files:
|
|
83
68
|
- lib/temple/erb/parser.rb
|
84
69
|
- lib/temple/erb/template.rb
|
85
70
|
- lib/temple/erb/trimming.rb
|
71
|
+
- lib/temple/exceptions.rb
|
86
72
|
- lib/temple/filter.rb
|
87
73
|
- lib/temple/filters/control_flow.rb
|
88
74
|
- lib/temple/filters/dynamic_inliner.rb
|
@@ -106,6 +92,7 @@ files:
|
|
106
92
|
- lib/temple/mixins/grammar_dsl.rb
|
107
93
|
- lib/temple/mixins/options.rb
|
108
94
|
- lib/temple/mixins/template.rb
|
95
|
+
- lib/temple/parser.rb
|
109
96
|
- lib/temple/templates.rb
|
110
97
|
- lib/temple/templates/rails.rb
|
111
98
|
- lib/temple/templates/tilt.rb
|
@@ -133,7 +120,7 @@ files:
|
|
133
120
|
- test/test_grammar.rb
|
134
121
|
- test/test_hash.rb
|
135
122
|
- test/test_utils.rb
|
136
|
-
homepage:
|
123
|
+
homepage: https://github.com/judofyr/temple
|
137
124
|
licenses: []
|
138
125
|
post_install_message:
|
139
126
|
rdoc_options: []
|
@@ -153,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
140
|
version: '0'
|
154
141
|
requirements: []
|
155
142
|
rubyforge_project:
|
156
|
-
rubygems_version: 1.8.
|
143
|
+
rubygems_version: 1.8.15
|
157
144
|
signing_key:
|
158
145
|
specification_version: 3
|
159
146
|
summary: Template compilation framework in Ruby
|