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
@@ -58,12 +58,14 @@ module Temple
|
|
58
58
|
def replace_dispatcher(exp)
|
59
59
|
tree = DispatchNode.new
|
60
60
|
dispatched_methods.each do |method|
|
61
|
-
method.split('_')[1..-1].inject(tree) {|node, type| node[type.to_sym] }.method = method
|
61
|
+
method.split('_'.freeze)[1..-1].inject(tree) {|node, type| node[type.to_sym] }.method = method
|
62
62
|
end
|
63
|
-
self.class.class_eval
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
|
+
def dispatcher(exp)
|
65
|
+
return replace_dispatcher(exp) if self.class != #{self.class}
|
66
|
+
#{tree.compile.gsub("\n", "\n ")}
|
67
|
+
end
|
68
|
+
RUBY
|
67
69
|
dispatcher(exp)
|
68
70
|
end
|
69
71
|
|
@@ -88,10 +90,11 @@ end}
|
|
88
90
|
raise 'Invalid dispatcher node' unless method
|
89
91
|
call_method
|
90
92
|
else
|
91
|
-
code =
|
93
|
+
code = String.new
|
94
|
+
code << "case(exp[#{level}])\n"
|
92
95
|
each do |key, child|
|
93
96
|
code << "when #{key.inspect}\n " <<
|
94
|
-
child.compile(level + 1, call_method).gsub("\n", "\n ") << "\n"
|
97
|
+
child.compile(level + 1, call_method).gsub("\n".freeze, "\n ".freeze) << "\n".freeze
|
95
98
|
end
|
96
99
|
code << "else\n " << (call_method || 'exp') << "\nend"
|
97
100
|
end
|
@@ -17,15 +17,7 @@ module Temple
|
|
17
17
|
|
18
18
|
def remove(name)
|
19
19
|
name = chain_name(name)
|
20
|
-
found
|
21
|
-
chain.reject! do |i|
|
22
|
-
if i.first == name
|
23
|
-
found = true
|
24
|
-
else
|
25
|
-
false
|
26
|
-
end
|
27
|
-
end
|
28
|
-
raise "#{name} not found" unless found
|
20
|
+
raise "#{name} not found" unless chain.reject! {|i| name === i.first }
|
29
21
|
chain_modified!
|
30
22
|
end
|
31
23
|
|
@@ -34,54 +26,31 @@ module Temple
|
|
34
26
|
def before(name, *args, &block)
|
35
27
|
name = chain_name(name)
|
36
28
|
e = chain_element(args, block)
|
37
|
-
|
38
|
-
|
39
|
-
if chain[i].first == name
|
40
|
-
found = true
|
41
|
-
chain.insert(i, e)
|
42
|
-
i += 2
|
43
|
-
else
|
44
|
-
i += 1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
raise "#{name} not found" unless found
|
29
|
+
chain.map! {|f| name === f.first ? [e, f] : [f] }.flatten!(1)
|
30
|
+
raise "#{name} not found" unless chain.include?(e)
|
48
31
|
chain_modified!
|
49
32
|
end
|
50
33
|
|
51
34
|
def after(name, *args, &block)
|
52
35
|
name = chain_name(name)
|
53
36
|
e = chain_element(args, block)
|
54
|
-
|
55
|
-
|
56
|
-
if chain[i].first == name
|
57
|
-
found = true
|
58
|
-
i += 1
|
59
|
-
chain.insert(i, e)
|
60
|
-
end
|
61
|
-
i += 1
|
62
|
-
end
|
63
|
-
raise "#{name} not found" unless found
|
37
|
+
chain.map! {|f| name === f.first ? [f, e] : [f] }.flatten!(1)
|
38
|
+
raise "#{name} not found" unless chain.include?(e)
|
64
39
|
chain_modified!
|
65
40
|
end
|
66
41
|
|
67
42
|
def replace(name, *args, &block)
|
68
43
|
name = chain_name(name)
|
69
44
|
e = chain_element(args, block)
|
70
|
-
|
71
|
-
|
72
|
-
if c.first == name
|
73
|
-
found = true
|
74
|
-
chain[i] = e
|
75
|
-
end
|
76
|
-
end
|
77
|
-
raise "#{name} not found" unless found
|
45
|
+
chain.map! {|f| name === f.first ? e : f }
|
46
|
+
raise "#{name} not found" unless chain.include?(e)
|
78
47
|
chain_modified!
|
79
48
|
end
|
80
49
|
|
81
50
|
# Shortcuts to access namespaces
|
82
|
-
{ :
|
83
|
-
:
|
84
|
-
:
|
51
|
+
{ filter: Temple::Filters,
|
52
|
+
generator: Temple::Generators,
|
53
|
+
html: Temple::HTML }.each do |method, mod|
|
85
54
|
define_method(method) do |name, *options|
|
86
55
|
use(name, mod.const_get(name), *options)
|
87
56
|
end
|
@@ -90,47 +59,51 @@ module Temple
|
|
90
59
|
private
|
91
60
|
|
92
61
|
def chain_name(name)
|
93
|
-
|
94
|
-
|
95
|
-
|
62
|
+
case name
|
63
|
+
when Class
|
64
|
+
name.name.to_sym
|
65
|
+
when Symbol, String
|
66
|
+
name.to_sym
|
67
|
+
when Regexp
|
68
|
+
name
|
69
|
+
else
|
70
|
+
raise(ArgumentError, 'Name argument must be Class, Symbol, String or Regexp')
|
71
|
+
end
|
96
72
|
end
|
97
73
|
|
98
|
-
def chain_class_constructor(filter,
|
99
|
-
|
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)
|
74
|
+
def chain_class_constructor(filter, local_options)
|
75
|
+
define_options(filter.options.valid_keys) if respond_to?(:define_options) && filter.respond_to?(:options)
|
102
76
|
proc do |engine|
|
103
|
-
|
77
|
+
opts = {}.update(engine.options)
|
78
|
+
opts.delete_if {|k,v| !filter.options.valid_key?(k) } if filter.respond_to?(:options)
|
79
|
+
opts.update(local_options) if local_options
|
80
|
+
filter.new(opts)
|
104
81
|
end
|
105
82
|
end
|
106
83
|
|
107
84
|
def chain_proc_constructor(name, filter)
|
108
85
|
raise(ArgumentError, 'Proc or blocks must have arity 0 or 1') if filter.arity > 1
|
109
86
|
method_name = "FILTER #{name}"
|
110
|
-
|
111
|
-
|
112
|
-
|
87
|
+
c = Class === self ? self : singleton_class
|
88
|
+
filter = c.class_eval { define_method(method_name, &filter); instance_method(method_name) }
|
89
|
+
proc do |engine|
|
113
90
|
if filter.arity == 1
|
114
|
-
proc
|
91
|
+
# the proc takes one argument, e.g. use(:Filter) {|exp| exp }
|
92
|
+
filter.bind(engine)
|
115
93
|
else
|
116
|
-
|
117
|
-
|
118
|
-
|
94
|
+
f = filter.bind(engine).call
|
95
|
+
if f.respond_to? :call
|
96
|
+
# the proc returns a callable object, e.g. use(:Filter) { Filter.new }
|
119
97
|
f
|
98
|
+
else
|
99
|
+
raise(ArgumentError, 'Proc or blocks must return a Callable or a Class') unless f.respond_to? :new
|
100
|
+
# the proc returns a class, e.g. use(:Filter) { Filter }
|
101
|
+
f.new(f.respond_to?(:options) ? engine.options.to_hash.select {|k,v| f.options.valid_key?(k) } : engine.options)
|
120
102
|
end
|
121
103
|
end
|
122
|
-
else
|
123
|
-
(class << self; self; end).class_eval { define_method(method_name, &filter) }
|
124
|
-
filter = method(method_name)
|
125
|
-
proc {|engine| filter }
|
126
104
|
end
|
127
105
|
end
|
128
106
|
|
129
|
-
def chain_callable_constructor(filter)
|
130
|
-
raise(ArgumentError, 'Class or callable argument is required') unless filter.respond_to?(:call)
|
131
|
-
proc {|engine| filter }
|
132
|
-
end
|
133
|
-
|
134
107
|
def chain_element(args, block)
|
135
108
|
name = args.shift
|
136
109
|
if Class === name
|
@@ -157,12 +130,14 @@ module Temple
|
|
157
130
|
when Class
|
158
131
|
# Class argument (e.g Filter class)
|
159
132
|
# The options are passed to the classes constructor.
|
160
|
-
|
133
|
+
raise(ArgumentError, 'Too many arguments') if args.size > 1
|
134
|
+
[name, chain_class_constructor(filter, args.first)]
|
161
135
|
else
|
162
136
|
# Other callable argument (e.g. Object of class which implements #call or Method)
|
163
137
|
# The callable has no access to the option hash of the engine.
|
164
138
|
raise(ArgumentError, 'Too many arguments') unless args.empty?
|
165
|
-
|
139
|
+
raise(ArgumentError, 'Class or callable argument is required') unless filter.respond_to?(:call)
|
140
|
+
[name, proc { filter }]
|
166
141
|
end
|
167
142
|
end
|
168
143
|
end
|
@@ -7,6 +7,12 @@ module Temple
|
|
7
7
|
@grammar = grammar
|
8
8
|
end
|
9
9
|
|
10
|
+
def match?(exp)
|
11
|
+
match(exp, [])
|
12
|
+
end
|
13
|
+
alias === match?
|
14
|
+
alias =~ match?
|
15
|
+
|
10
16
|
def |(rule)
|
11
17
|
Or.new(@grammar, self, rule)
|
12
18
|
end
|
@@ -53,10 +59,6 @@ module Temple
|
|
53
59
|
success
|
54
60
|
end
|
55
61
|
|
56
|
-
def match?(exp)
|
57
|
-
match(exp, [])
|
58
|
-
end
|
59
|
-
|
60
62
|
def validate!(exp)
|
61
63
|
unmatched = []
|
62
64
|
unless match(exp, unmatched)
|
@@ -120,14 +122,13 @@ module Temple
|
|
120
122
|
def match?(exp)
|
121
123
|
const_get(:Expression).match?(exp)
|
122
124
|
end
|
125
|
+
alias === match?
|
126
|
+
alias =~ match?
|
123
127
|
|
124
128
|
def validate!(exp)
|
125
129
|
const_get(:Expression).validate!(exp)
|
126
130
|
end
|
127
131
|
|
128
|
-
alias === match?
|
129
|
-
alias =~ match?
|
130
|
-
|
131
132
|
def Value(value)
|
132
133
|
Value.new(self, value)
|
133
134
|
end
|
@@ -142,7 +143,8 @@ module Temple
|
|
142
143
|
start = Or.new(self)
|
143
144
|
curr = [start]
|
144
145
|
rule.each do |elem|
|
145
|
-
|
146
|
+
case elem
|
147
|
+
when /^(.*)(\*|\?|\+)$/
|
146
148
|
elem = Element.new(self, const_get($1))
|
147
149
|
curr.each {|c| c << elem }
|
148
150
|
elem << elem if $2 != '?'
|
@@ -1,42 +1,44 @@
|
|
1
1
|
module Temple
|
2
2
|
module Mixins
|
3
3
|
# @api public
|
4
|
-
module
|
4
|
+
module ClassOptions
|
5
5
|
def set_default_options(opts)
|
6
|
-
|
6
|
+
warn 'set_default_options has been deprecated, use set_options'
|
7
|
+
set_options(opts)
|
7
8
|
end
|
8
9
|
|
9
10
|
def default_options
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
warn 'default_options has been deprecated, use options'
|
12
|
+
options
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_options(opts)
|
16
|
+
options.update(opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def options
|
20
|
+
@options ||= OptionMap.new(superclass.respond_to?(:options) ?
|
21
|
+
superclass.options : nil) do |hash, key, what|
|
22
|
+
warn "#{self}: Option #{key.inspect} is #{what}" unless @option_validator_disabled
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
def define_options(*opts)
|
25
27
|
if opts.last.respond_to?(:to_hash)
|
26
28
|
hash = opts.pop.to_hash
|
27
|
-
|
28
|
-
|
29
|
+
options.add_valid_keys(hash.keys)
|
30
|
+
options.update(hash)
|
29
31
|
end
|
30
|
-
|
32
|
+
options.add_valid_keys(opts)
|
31
33
|
end
|
32
34
|
|
33
35
|
def define_deprecated_options(*opts)
|
34
36
|
if opts.last.respond_to?(:to_hash)
|
35
37
|
hash = opts.pop.to_hash
|
36
|
-
|
37
|
-
|
38
|
+
options.add_deprecated_keys(hash.keys)
|
39
|
+
options.update(hash)
|
38
40
|
end
|
39
|
-
|
41
|
+
options.add_deprecated_keys(opts)
|
40
42
|
end
|
41
43
|
|
42
44
|
def disable_option_validator!
|
@@ -47,7 +49,7 @@ module Temple
|
|
47
49
|
module ThreadOptions
|
48
50
|
def with_options(options)
|
49
51
|
old_options = thread_options
|
50
|
-
Thread.current[thread_options_key] =
|
52
|
+
Thread.current[thread_options_key] = ImmutableMap.new(options, thread_options)
|
51
53
|
yield
|
52
54
|
ensure
|
53
55
|
Thread.current[thread_options_key] = old_options
|
@@ -68,7 +70,7 @@ module Temple
|
|
68
70
|
module Options
|
69
71
|
def self.included(base)
|
70
72
|
base.class_eval do
|
71
|
-
extend
|
73
|
+
extend ClassOptions
|
72
74
|
extend ThreadOptions
|
73
75
|
end
|
74
76
|
end
|
@@ -76,9 +78,9 @@ module Temple
|
|
76
78
|
attr_reader :options
|
77
79
|
|
78
80
|
def initialize(opts = {})
|
79
|
-
self.class.
|
80
|
-
self.class.
|
81
|
-
@options =
|
81
|
+
self.class.options.validate_map!(opts)
|
82
|
+
self.class.options.validate_map!(self.class.thread_options) if self.class.thread_options
|
83
|
+
@options = ImmutableMap.new({}.update(self.class.options).update(self.class.thread_options || {}).update(opts))
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
@@ -2,7 +2,7 @@ module Temple
|
|
2
2
|
module Mixins
|
3
3
|
# @api private
|
4
4
|
module Template
|
5
|
-
include
|
5
|
+
include ClassOptions
|
6
6
|
|
7
7
|
def compile(code, options)
|
8
8
|
engine = options.delete(:engine)
|
@@ -18,8 +18,8 @@ module Temple
|
|
18
18
|
register_as = options.delete(:register_as)
|
19
19
|
template = Class.new(self)
|
20
20
|
template.disable_option_validator!
|
21
|
-
template.
|
22
|
-
template.
|
21
|
+
template.options[:engine] = engine
|
22
|
+
template.options.update(options)
|
23
23
|
template.register_as(*register_as) if register_as
|
24
24
|
template
|
25
25
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
begin
|
2
|
+
require 'ripper'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Temple
|
7
|
+
module StaticAnalyzer
|
8
|
+
STATIC_TOKENS = [
|
9
|
+
:on_tstring_beg, :on_tstring_end, :on_tstring_content,
|
10
|
+
:on_embexpr_beg, :on_embexpr_end,
|
11
|
+
:on_lbracket, :on_rbracket,
|
12
|
+
:on_qwords_beg, :on_words_sep, :on_qwords_sep,
|
13
|
+
:on_lparen, :on_rparen,
|
14
|
+
:on_lbrace, :on_rbrace, :on_label,
|
15
|
+
:on_int, :on_float, :on_imaginary,
|
16
|
+
:on_comma, :on_sp, :on_ignored_nl,
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
DYNAMIC_TOKENS = [
|
20
|
+
:on_ident, :on_period,
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
STATIC_KEYWORDS = [
|
24
|
+
'true', 'false', 'nil',
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
STATIC_OPERATORS = [
|
28
|
+
'=>',
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def available?
|
33
|
+
defined?(Ripper) && Ripper.respond_to?(:lex)
|
34
|
+
end
|
35
|
+
|
36
|
+
def static?(code)
|
37
|
+
return false if code.nil? || code.strip.empty?
|
38
|
+
return false if syntax_error?(code)
|
39
|
+
|
40
|
+
Ripper.lex(code).each do |_, token, str|
|
41
|
+
case token
|
42
|
+
when *STATIC_TOKENS
|
43
|
+
# noop
|
44
|
+
when :on_kw
|
45
|
+
return false unless STATIC_KEYWORDS.include?(str)
|
46
|
+
when :on_op
|
47
|
+
return false unless STATIC_OPERATORS.include?(str)
|
48
|
+
when *DYNAMIC_TOKENS
|
49
|
+
return false
|
50
|
+
else
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def syntax_error?(code)
|
58
|
+
SyntaxChecker.new(code).parse
|
59
|
+
false
|
60
|
+
rescue SyntaxChecker::ParseError
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if defined?(Ripper)
|
66
|
+
class SyntaxChecker < Ripper
|
67
|
+
class ParseError < StandardError; end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def on_parse_error(*)
|
72
|
+
raise ParseError
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,47 +1,28 @@
|
|
1
|
-
unless defined?(ActionView)
|
2
|
-
raise "Rails is not loaded - Temple::Templates::Rails cannot be used"
|
3
|
-
end
|
4
|
-
|
5
|
-
if ::ActionPack::VERSION::MAJOR < 3
|
6
|
-
raise "Temple supports only Rails 3.x and greater, your Rails version is #{::ActionPack::VERSION::STRING}"
|
7
|
-
end
|
8
|
-
|
9
1
|
module Temple
|
10
2
|
module Templates
|
11
|
-
|
12
|
-
|
13
|
-
include ActionView::TemplateHandlers::Compilable
|
14
|
-
extend Mixins::Template
|
3
|
+
class Rails
|
4
|
+
extend Mixins::Template
|
15
5
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def self.register_as(*names)
|
23
|
-
names.each do |name|
|
24
|
-
ActionView::Template.register_template_handler name.to_sym, self
|
25
|
-
end
|
6
|
+
def call(template, source = nil)
|
7
|
+
opts = {}.update(self.class.options).update(file: template.identifier)
|
8
|
+
if ActionView::Base.try(:annotate_rendered_view_with_filenames) && template.format == :html
|
9
|
+
opts[:preamble] = "<!-- BEGIN #{template.short_identifier} -->\n"
|
10
|
+
opts[:postamble] = "<!-- END #{template.short_identifier} -->\n"
|
26
11
|
end
|
12
|
+
self.class.compile((source || template.source), opts)
|
27
13
|
end
|
28
|
-
else
|
29
|
-
class Rails
|
30
|
-
extend Mixins::Template
|
31
14
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
15
|
+
def supports_streaming?
|
16
|
+
self.class.options[:streaming]
|
17
|
+
end
|
36
18
|
|
37
|
-
|
38
|
-
|
19
|
+
def self.register_as(*names)
|
20
|
+
raise 'Rails is not loaded - Temple::Templates::Rails cannot be used' unless defined?(::ActionView)
|
21
|
+
if ::ActiveSupport::VERSION::MAJOR < 5
|
22
|
+
raise "Temple supports only Rails 5 and greater, your Rails version is #{::ActiveSupport::VERSION::STRING}"
|
39
23
|
end
|
40
|
-
|
41
|
-
|
42
|
-
names.each do |name|
|
43
|
-
ActionView::Template.register_template_handler name.to_sym, new
|
44
|
-
end
|
24
|
+
names.each do |name|
|
25
|
+
::ActionView::Template.register_template_handler name.to_sym, new
|
45
26
|
end
|
46
27
|
end
|
47
28
|
end
|
@@ -5,15 +5,7 @@ module Temple
|
|
5
5
|
class Tilt < ::Tilt::Template
|
6
6
|
extend Mixins::Template
|
7
7
|
|
8
|
-
define_options :
|
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
|
8
|
+
define_options mime_type: 'text/html'
|
17
9
|
|
18
10
|
# Prepare Temple template
|
19
11
|
#
|
@@ -21,10 +13,12 @@ module Temple
|
|
21
13
|
#
|
22
14
|
# @return [void]
|
23
15
|
def prepare
|
24
|
-
|
25
|
-
|
26
|
-
opts.
|
27
|
-
|
16
|
+
opts = {}.update(self.class.options).update(options).update(file: eval_file)
|
17
|
+
metadata[:mime_type] = opts.delete(:mime_type)
|
18
|
+
if opts.include?(:outvar)
|
19
|
+
opts[:buffer] = opts.delete(:outvar)
|
20
|
+
opts[:save_buffer] = true
|
21
|
+
end
|
28
22
|
@src = self.class.compile(data, opts)
|
29
23
|
end
|
30
24
|
|
data/lib/temple/utils.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
begin
|
2
|
-
require '
|
2
|
+
require 'cgi/escape'
|
3
3
|
rescue LoadError
|
4
|
-
# Loading EscapeUtils failed
|
5
4
|
end
|
6
5
|
|
7
6
|
module Temple
|
@@ -15,16 +14,17 @@ module Temple
|
|
15
14
|
# @param html [String] The string to escape
|
16
15
|
# @return [String] The escaped string
|
17
16
|
def escape_html_safe(html)
|
18
|
-
|
17
|
+
s = html.to_s
|
18
|
+
s.html_safe? || html.html_safe? ? s : escape_html(s)
|
19
19
|
end
|
20
20
|
|
21
|
-
if defined?(
|
21
|
+
if defined?(CGI.escapeHTML)
|
22
22
|
# Returns an escaped copy of `html`.
|
23
23
|
#
|
24
24
|
# @param html [String] The string to escape
|
25
25
|
# @return [String] The escaped string
|
26
26
|
def escape_html(html)
|
27
|
-
|
27
|
+
CGI.escapeHTML(html.to_s)
|
28
28
|
end
|
29
29
|
else
|
30
30
|
# Used by escape_html
|
@@ -37,30 +37,14 @@ module Temple
|
|
37
37
|
'>' => '>'
|
38
38
|
}.freeze
|
39
39
|
|
40
|
-
|
41
|
-
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
42
|
-
else
|
43
|
-
# On 1.8, there is a kcode = 'u' bug that allows for XSS otherwise
|
44
|
-
# TODO doesn't apply to jruby, so a better condition above might be preferable?
|
45
|
-
ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
|
46
|
-
end
|
40
|
+
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
47
41
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
html.to_s.gsub(ESCAPE_HTML_PATTERN, ESCAPE_HTML)
|
55
|
-
end
|
56
|
-
else
|
57
|
-
# Returns an escaped copy of `html`.
|
58
|
-
#
|
59
|
-
# @param html [String] The string to escape
|
60
|
-
# @return [String] The escaped string
|
61
|
-
def escape_html(html)
|
62
|
-
html.to_s.gsub(ESCAPE_HTML_PATTERN) {|c| ESCAPE_HTML[c] }
|
63
|
-
end
|
42
|
+
# Returns an escaped copy of `html`.
|
43
|
+
#
|
44
|
+
# @param html [String] The string to escape
|
45
|
+
# @return [String] The escaped string
|
46
|
+
def escape_html(html)
|
47
|
+
html.to_s.gsub(ESCAPE_HTML_PATTERN, ESCAPE_HTML)
|
64
48
|
end
|
65
49
|
end
|
66
50
|
|
@@ -70,7 +54,7 @@ module Temple
|
|
70
54
|
# @return [String] Variable name
|
71
55
|
def unique_name(prefix = nil)
|
72
56
|
@unique_name ||= 0
|
73
|
-
prefix ||= (@unique_prefix ||= self.class.name.gsub('::', '_').downcase)
|
57
|
+
prefix ||= (@unique_prefix ||= self.class.name.gsub('::'.freeze, '_'.freeze).downcase)
|
74
58
|
"_#{prefix}#{@unique_name += 1}"
|
75
59
|
end
|
76
60
|
|
@@ -88,5 +72,19 @@ module Temple
|
|
88
72
|
false
|
89
73
|
end
|
90
74
|
end
|
75
|
+
|
76
|
+
def indent_dynamic(text, indent_next, indent, pre_tags = nil)
|
77
|
+
text = text.to_s
|
78
|
+
safe = text.respond_to?(:html_safe?) && text.html_safe?
|
79
|
+
return text if pre_tags && text =~ pre_tags
|
80
|
+
|
81
|
+
level = text.scan(/^\s*/).map(&:size).min
|
82
|
+
text = text.gsub(/(?!\A)^\s{#{level}}/, '') if level > 0
|
83
|
+
|
84
|
+
text = text.sub(/\A\s*\n?/, "\n".freeze) if indent_next
|
85
|
+
text = text.gsub("\n".freeze, indent)
|
86
|
+
|
87
|
+
safe ? text.html_safe : text
|
88
|
+
end
|
91
89
|
end
|
92
90
|
end
|
data/lib/temple/version.rb
CHANGED