haml 5.0.4 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +36 -0
- data/.gitignore +1 -0
- data/.yardopts +1 -2
- data/CHANGELOG.md +51 -1
- data/Gemfile +2 -5
- data/MIT-LICENSE +2 -2
- data/README.md +22 -17
- data/REFERENCE.md +46 -12
- data/Rakefile +2 -13
- data/benchmark.rb +13 -9
- data/haml.gemspec +10 -3
- data/lib/haml/attribute_builder.rb +59 -3
- data/lib/haml/attribute_compiler.rb +46 -32
- data/lib/haml/attribute_parser.rb +3 -1
- data/lib/haml/buffer.rb +1 -54
- data/lib/haml/compiler.rb +23 -24
- data/lib/haml/engine.rb +12 -3
- data/lib/haml/error.rb +25 -24
- data/lib/haml/escapable.rb +39 -11
- data/lib/haml/exec.rb +5 -6
- data/lib/haml/filters.rb +12 -11
- data/lib/haml/generator.rb +2 -1
- data/lib/haml/helpers/action_view_extensions.rb +1 -0
- data/lib/haml/helpers/action_view_mods.rb +4 -1
- data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
- data/lib/haml/helpers/safe_erubi_template.rb +1 -0
- data/lib/haml/helpers/safe_erubis_template.rb +1 -0
- data/lib/haml/helpers/xss_mods.rb +7 -3
- data/lib/haml/helpers.rb +15 -13
- data/lib/haml/options.rb +36 -36
- data/lib/haml/parser.rb +52 -22
- data/lib/haml/plugin.rb +25 -5
- data/lib/haml/railtie.rb +9 -4
- data/lib/haml/sass_rails_filter.rb +1 -0
- data/lib/haml/template/options.rb +1 -0
- data/lib/haml/template.rb +1 -0
- data/lib/haml/temple_engine.rb +13 -10
- data/lib/haml/temple_line_counter.rb +1 -0
- data/lib/haml/util.rb +6 -6
- data/lib/haml/version.rb +2 -1
- data/lib/haml.rb +1 -0
- metadata +29 -10
- data/.travis.yml +0 -54
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'haml/attribute_parser'
|
3
4
|
|
4
5
|
module Haml
|
@@ -6,27 +7,7 @@ module Haml
|
|
6
7
|
# @param type [Symbol] :static or :dynamic
|
7
8
|
# @param key [String]
|
8
9
|
# @param value [String] Actual string value for :static type, value's Ruby literal for :dynamic type.
|
9
|
-
|
10
|
-
# @return [String] A Ruby literal of value.
|
11
|
-
def to_literal
|
12
|
-
case type
|
13
|
-
when :static
|
14
|
-
Haml::Util.inspect_obj(value)
|
15
|
-
when :dynamic
|
16
|
-
value
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns a script to render attributes on runtime.
|
22
|
-
#
|
23
|
-
# @param attributes [Hash]
|
24
|
-
# @param object_ref [String,:nil]
|
25
|
-
# @param dynamic_attributes [DynamicAttributes]
|
26
|
-
# @return [String] Attributes rendering code
|
27
|
-
def self.runtime_build(attributes, object_ref, dynamic_attributes)
|
28
|
-
"_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{dynamic_attributes.to_literal})"
|
29
|
-
end
|
10
|
+
AttributeValue = Struct.new(:type, :key, :value)
|
30
11
|
|
31
12
|
# @param options [Haml::Options]
|
32
13
|
def initialize(options)
|
@@ -40,16 +21,16 @@ module Haml
|
|
40
21
|
#
|
41
22
|
# @param attributes [Hash]
|
42
23
|
# @param object_ref [String,:nil]
|
43
|
-
# @param dynamic_attributes [DynamicAttributes]
|
24
|
+
# @param dynamic_attributes [Haml::Parser::DynamicAttributes]
|
44
25
|
# @return [Array] Temple expression
|
45
26
|
def compile(attributes, object_ref, dynamic_attributes)
|
46
27
|
if object_ref != :nil || !AttributeParser.available?
|
47
|
-
return [:dynamic,
|
28
|
+
return [:dynamic, compile_runtime_build(attributes, object_ref, dynamic_attributes)]
|
48
29
|
end
|
49
30
|
|
50
31
|
parsed_hashes = [dynamic_attributes.new, dynamic_attributes.old].compact.map do |attribute_hash|
|
51
32
|
unless (hash = AttributeParser.parse(attribute_hash))
|
52
|
-
return [:dynamic,
|
33
|
+
return [:dynamic, compile_runtime_build(attributes, object_ref, dynamic_attributes)]
|
53
34
|
end
|
54
35
|
hash
|
55
36
|
end
|
@@ -63,6 +44,17 @@ module Haml
|
|
63
44
|
|
64
45
|
private
|
65
46
|
|
47
|
+
# Returns a script to render attributes on runtime.
|
48
|
+
#
|
49
|
+
# @param attributes [Hash]
|
50
|
+
# @param object_ref [String,:nil]
|
51
|
+
# @param dynamic_attributes [Haml::Parser::DynamicAttributes]
|
52
|
+
# @return [String] Attributes rendering code
|
53
|
+
def compile_runtime_build(attributes, object_ref, dynamic_attributes)
|
54
|
+
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs].map(&method(:to_literal)).join(', ')
|
55
|
+
"::Haml::AttributeBuilder.build(#{to_literal(attributes)}, #{object_ref}, #{arguments}, #{dynamic_attributes.to_literal})"
|
56
|
+
end
|
57
|
+
|
66
58
|
# Build array of grouped values whose sort order may go back and forth, which is also sorted with key name.
|
67
59
|
# This method needs to group values with the same start because it can be changed in `Haml::AttributeBuidler#build_data_keys`.
|
68
60
|
# @param values [Array<Haml::AttributeCompiler::AttributeValue>]
|
@@ -115,7 +107,8 @@ module Haml
|
|
115
107
|
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
116
108
|
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
117
109
|
end.join(', ')
|
118
|
-
[
|
110
|
+
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs].map(&method(:to_literal)).join(', ')
|
111
|
+
[:dynamic, "::Haml::AttributeBuilder.build({ #{hash_content} }, nil, #{arguments})"]
|
119
112
|
end
|
120
113
|
|
121
114
|
# Renders attribute values statically.
|
@@ -129,7 +122,7 @@ module Haml
|
|
129
122
|
|
130
123
|
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
|
131
124
|
code = "::Haml::AttributeBuilder.build_attributes"\
|
132
|
-
"(#{arguments.map
|
125
|
+
"(#{arguments.map(&method(:to_literal)).join(', ')}, { #{hash_content} })"
|
133
126
|
[:static, eval(code).to_s]
|
134
127
|
end
|
135
128
|
|
@@ -138,16 +131,16 @@ module Haml
|
|
138
131
|
# @return [String]
|
139
132
|
def merged_value(key, values)
|
140
133
|
if values.size == 1
|
141
|
-
values.first
|
134
|
+
attr_literal(values.first)
|
142
135
|
else
|
143
|
-
"::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(
|
136
|
+
"::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&method(:attr_literal)).join(', ')})"
|
144
137
|
end
|
145
138
|
end
|
146
139
|
|
147
140
|
# @param str [String]
|
148
141
|
# @return [String]
|
149
142
|
def frozen_string(str)
|
150
|
-
"#{
|
143
|
+
"#{to_literal(str)}.freeze"
|
151
144
|
end
|
152
145
|
|
153
146
|
# Compiles attribute values for one key to Temple expression that generates ` key='value'`.
|
@@ -156,7 +149,7 @@ module Haml
|
|
156
149
|
# @param values [Array<AttributeValue>]
|
157
150
|
# @return [Array] Temple expression
|
158
151
|
def compile_attribute(key, values)
|
159
|
-
if values.all? { |v| Temple::StaticAnalyzer.static?(v
|
152
|
+
if values.all? { |v| Temple::StaticAnalyzer.static?(attr_literal(v)) }
|
160
153
|
return static_build(values)
|
161
154
|
end
|
162
155
|
|
@@ -180,7 +173,7 @@ module Haml
|
|
180
173
|
['false, nil', [:multi]],
|
181
174
|
[:else, [:multi,
|
182
175
|
[:static, " #{id_or_class}=#{@attr_wrapper}"],
|
183
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
176
|
+
[:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
|
184
177
|
[:static, @attr_wrapper]],
|
185
178
|
]
|
186
179
|
],
|
@@ -200,7 +193,7 @@ module Haml
|
|
200
193
|
['false, nil', [:multi]],
|
201
194
|
[:else, [:multi,
|
202
195
|
[:static, " #{key}=#{@attr_wrapper}"],
|
203
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
196
|
+
[:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
|
204
197
|
[:static, @attr_wrapper]],
|
205
198
|
]
|
206
199
|
],
|
@@ -219,5 +212,26 @@ module Haml
|
|
219
212
|
@unique_name ||= 0
|
220
213
|
"_haml_attribute_compiler#{@unique_name += 1}"
|
221
214
|
end
|
215
|
+
|
216
|
+
# @param [Haml::AttributeCompiler::AttributeValue] attr
|
217
|
+
def attr_literal(attr)
|
218
|
+
case attr.type
|
219
|
+
when :static
|
220
|
+
to_literal(attr.value)
|
221
|
+
when :dynamic
|
222
|
+
attr.value
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# For haml/haml#972
|
227
|
+
# @param [Object] value
|
228
|
+
def to_literal(value)
|
229
|
+
case value
|
230
|
+
when true, false
|
231
|
+
value.to_s
|
232
|
+
else
|
233
|
+
Haml::Util.inspect_obj(value)
|
234
|
+
end
|
235
|
+
end
|
222
236
|
end
|
223
237
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
begin
|
3
4
|
require 'ripper'
|
4
5
|
rescue LoadError
|
5
6
|
end
|
7
|
+
require 'temple/static_analyzer'
|
6
8
|
|
7
9
|
module Haml
|
8
10
|
# Haml::AttriubuteParser parses Hash literal to { String (key name) => String (value literal) }.
|
@@ -14,7 +16,7 @@ module Haml
|
|
14
16
|
TYPE = 1
|
15
17
|
TEXT = 2
|
16
18
|
|
17
|
-
IGNORED_TYPES = %i[on_sp on_ignored_nl]
|
19
|
+
IGNORED_TYPES = %i[on_sp on_ignored_nl].freeze
|
18
20
|
|
19
21
|
class << self
|
20
22
|
# @return [Boolean] - return true if AttributeParser.parse can be used.
|
data/lib/haml/buffer.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Haml
|
3
4
|
# This class is used only internally. It holds the buffer of HTML that
|
4
5
|
# is eventually output as the resulting document.
|
@@ -129,16 +130,6 @@ module Haml
|
|
129
130
|
@real_tabs += tab_change
|
130
131
|
end
|
131
132
|
|
132
|
-
def attributes(class_id, obj_ref, *attributes_hashes)
|
133
|
-
attributes = class_id
|
134
|
-
attributes_hashes.each do |old|
|
135
|
-
AttributeBuilder.merge_attributes!(attributes, Hash[old.map {|k, v| [k.to_s, v]}])
|
136
|
-
end
|
137
|
-
AttributeBuilder.merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref
|
138
|
-
AttributeBuilder.build_attributes(
|
139
|
-
html?, @options[:attr_wrapper], @options[:escape_attrs], @options[:hyphenate_data_attrs], attributes)
|
140
|
-
end
|
141
|
-
|
142
133
|
# Remove the whitespace from the right side of the buffer string.
|
143
134
|
# Doesn't do anything if we're at the beginning of a capture_haml block.
|
144
135
|
def rstrip!
|
@@ -187,49 +178,5 @@ module Haml
|
|
187
178
|
tabs = [count + @tabulation, 0].max
|
188
179
|
@@tab_cache[tabs] ||= ' ' * tabs
|
189
180
|
end
|
190
|
-
|
191
|
-
# Takes an array of objects and uses the class and id of the first
|
192
|
-
# one to create an attributes hash.
|
193
|
-
# The second object, if present, is used as a prefix,
|
194
|
-
# just like you can do with `dom_id()` and `dom_class()` in Rails
|
195
|
-
def parse_object_ref(ref)
|
196
|
-
prefix = ref[1]
|
197
|
-
ref = ref[0]
|
198
|
-
# Let's make sure the value isn't nil. If it is, return the default Hash.
|
199
|
-
return {} if ref.nil?
|
200
|
-
class_name =
|
201
|
-
if ref.respond_to?(:haml_object_ref)
|
202
|
-
ref.haml_object_ref
|
203
|
-
else
|
204
|
-
underscore(ref.class)
|
205
|
-
end
|
206
|
-
ref_id =
|
207
|
-
if ref.respond_to?(:to_key)
|
208
|
-
key = ref.to_key
|
209
|
-
key.join('_') unless key.nil?
|
210
|
-
else
|
211
|
-
ref.id
|
212
|
-
end
|
213
|
-
id = "#{class_name}_#{ref_id || 'new'}"
|
214
|
-
if prefix
|
215
|
-
class_name = "#{ prefix }_#{ class_name}"
|
216
|
-
id = "#{ prefix }_#{ id }"
|
217
|
-
end
|
218
|
-
|
219
|
-
{ 'id'.freeze => id, 'class'.freeze => class_name }
|
220
|
-
end
|
221
|
-
|
222
|
-
# Changes a word from camel case to underscores.
|
223
|
-
# Based on the method of the same name in Rails' Inflector,
|
224
|
-
# but copied here so it'll run properly without Rails.
|
225
|
-
def underscore(camel_cased_word)
|
226
|
-
word = camel_cased_word.to_s.dup
|
227
|
-
word.gsub!(/::/, '_')
|
228
|
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
229
|
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
230
|
-
word.tr!('-', '_')
|
231
|
-
word.downcase!
|
232
|
-
word
|
233
|
-
end
|
234
181
|
end
|
235
182
|
end
|
data/lib/haml/compiler.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'haml/attribute_builder'
|
3
4
|
require 'haml/attribute_compiler'
|
4
5
|
require 'haml/temple_line_counter'
|
@@ -167,7 +168,7 @@ module Haml
|
|
167
168
|
end
|
168
169
|
|
169
170
|
def compile_filter
|
170
|
-
unless filter = @filters[@node.value[:name]]
|
171
|
+
unless (filter = @filters[@node.value[:name]])
|
171
172
|
name = @node.value[:name]
|
172
173
|
if ["maruku", "textile"].include?(name)
|
173
174
|
raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
|
@@ -187,30 +188,28 @@ module Haml
|
|
187
188
|
|
188
189
|
if @options.html5?
|
189
190
|
'<!DOCTYPE html>'
|
190
|
-
|
191
|
-
if @
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
else
|
197
|
-
case @node.value[:type]
|
198
|
-
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
199
|
-
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
200
|
-
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
201
|
-
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
|
202
|
-
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
203
|
-
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
elsif @options.html4?
|
191
|
+
elsif @options.xhtml?
|
192
|
+
if @node.value[:version] == "1.1"
|
193
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
|
194
|
+
elsif @node.value[:version] == "5"
|
195
|
+
'<!DOCTYPE html>'
|
196
|
+
else
|
208
197
|
case @node.value[:type]
|
209
|
-
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD
|
210
|
-
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD
|
211
|
-
|
198
|
+
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
199
|
+
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
200
|
+
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
201
|
+
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
|
202
|
+
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
203
|
+
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
212
204
|
end
|
213
205
|
end
|
206
|
+
|
207
|
+
elsif @options.html4?
|
208
|
+
case @node.value[:type]
|
209
|
+
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
|
210
|
+
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
|
211
|
+
else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
212
|
+
end
|
214
213
|
end
|
215
214
|
end
|
216
215
|
|
@@ -315,7 +314,7 @@ module Haml
|
|
315
314
|
|
316
315
|
case last.first
|
317
316
|
when :text
|
318
|
-
last[1].rstrip
|
317
|
+
last[1] = last[1].rstrip
|
319
318
|
if last[1].empty?
|
320
319
|
@to_merge.slice! index
|
321
320
|
rstrip_buffer! index
|
data/lib/haml/engine.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'forwardable'
|
3
4
|
|
4
5
|
require 'haml/parser'
|
@@ -51,6 +52,9 @@ module Haml
|
|
51
52
|
# see {file:REFERENCE.md#options the Haml options documentation}
|
52
53
|
# @raise [Haml::Error] if there's a Haml syntax error in the template
|
53
54
|
def initialize(template, options = {})
|
55
|
+
# Reflect changes of `Haml::Options.defaults` to `Haml::TempleEngine` options, but `#initialize_encoding`
|
56
|
+
# should be run against the arguemnt `options[:encoding]` for backward compatibility with old `Haml::Engine`.
|
57
|
+
options = Options.defaults.dup.tap { |o| o.delete(:encoding) }.merge!(options)
|
54
58
|
@options = Options.new(options)
|
55
59
|
|
56
60
|
@template = check_haml_encoding(template) do |msg, line|
|
@@ -166,8 +170,13 @@ module Haml
|
|
166
170
|
end
|
167
171
|
|
168
172
|
begin
|
169
|
-
|
170
|
-
|
173
|
+
str = @temple_engine.precompiled_with_ambles(local_names)
|
174
|
+
eval(
|
175
|
+
"Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {}; #{str}}\n",
|
176
|
+
scope,
|
177
|
+
@options.filename,
|
178
|
+
@options.line
|
179
|
+
)
|
171
180
|
rescue ::SyntaxError => e
|
172
181
|
raise SyntaxError, e.message
|
173
182
|
end
|
data/lib/haml/error.rb
CHANGED
@@ -1,32 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Haml
|
3
4
|
# An exception raised by Haml code.
|
4
5
|
class Error < StandardError
|
5
6
|
|
6
7
|
MESSAGES = {
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
8
|
+
bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
|
9
|
+
cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
|
10
|
+
cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
|
11
|
+
deeper_indenting: "The line was indented %d levels deeper than the previous line.",
|
12
|
+
filter_not_defined: 'Filter "%s" is not defined.',
|
13
|
+
gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
|
14
|
+
illegal_element: "Illegal element: classes and ids must have values.",
|
15
|
+
illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
|
16
|
+
illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
|
17
|
+
illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
|
18
|
+
illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
|
19
|
+
illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
|
20
|
+
inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
|
21
|
+
indenting_at_start: "Indenting at the beginning of the document is illegal.",
|
22
|
+
install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
|
23
|
+
invalid_attribute_list: 'Invalid attribute list: %s.',
|
24
|
+
invalid_filter_name: 'Invalid filter name ":%s".',
|
25
|
+
invalid_tag: 'Invalid tag: "%s".',
|
26
|
+
missing_if: 'Got "%s" with no preceding "if"',
|
27
|
+
no_ruby_code: "There's no Ruby code for %s to evaluate.",
|
28
|
+
self_closing_content: "Self-closing tags can't have content.",
|
29
|
+
unbalanced_brackets: 'Unbalanced brackets.',
|
30
|
+
no_end: <<-END
|
30
31
|
You don't need to use "- end" in Haml. Un-indent to close a block:
|
31
32
|
- if foo?
|
32
33
|
%strong Foo!
|
@@ -34,7 +35,7 @@ You don't need to use "- end" in Haml. Un-indent to close a block:
|
|
34
35
|
Not foo.
|
35
36
|
%p This line is un-indented, so it isn't part of the "if" block
|
36
37
|
END
|
37
|
-
}
|
38
|
+
}.freeze
|
38
39
|
|
39
40
|
def self.message(key, *args)
|
40
41
|
string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
|
data/lib/haml/escapable.rb
CHANGED
@@ -1,32 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Haml
|
3
4
|
# Like Temple::Filters::Escapable, but with support for escaping by
|
4
5
|
# Haml::Herlpers.html_escape and Haml::Herlpers.escape_once.
|
5
6
|
class Escapable < Temple::Filter
|
7
|
+
# Special value of `flag` to ignore html_safe?
|
8
|
+
EscapeSafeBuffer = Struct.new(:value)
|
9
|
+
|
6
10
|
def initialize(*)
|
7
11
|
super
|
8
|
-
@escape_code = "::Haml::Helpers.html_escape((%s))"
|
9
|
-
@escaper = eval("proc {|v| #{@escape_code % 'v'} }")
|
10
|
-
@once_escape_code = "::Haml::Helpers.escape_once((%s))"
|
11
|
-
@once_escaper = eval("proc {|v| #{@once_escape_code % 'v'} }")
|
12
12
|
@escape = false
|
13
|
+
@escape_safe_buffer = false
|
13
14
|
end
|
14
15
|
|
15
16
|
def on_escape(flag, exp)
|
16
|
-
|
17
|
-
@
|
17
|
+
old_escape, old_escape_safe_buffer = @escape, @escape_safe_buffer
|
18
|
+
@escape_safe_buffer = flag.is_a?(EscapeSafeBuffer)
|
19
|
+
@escape = @escape_safe_buffer ? flag.value : flag
|
18
20
|
compile(exp)
|
19
21
|
ensure
|
20
|
-
@escape =
|
22
|
+
@escape, @escape_safe_buffer = old_escape, old_escape_safe_buffer
|
21
23
|
end
|
22
24
|
|
23
25
|
# The same as Haml::AttributeBuilder.build_attributes
|
24
26
|
def on_static(value)
|
25
27
|
[:static,
|
26
28
|
if @escape == :once
|
27
|
-
|
29
|
+
escape_once(value)
|
28
30
|
elsif @escape
|
29
|
-
|
31
|
+
escape(value)
|
30
32
|
else
|
31
33
|
value
|
32
34
|
end
|
@@ -37,13 +39,39 @@ module Haml
|
|
37
39
|
def on_dynamic(value)
|
38
40
|
[:dynamic,
|
39
41
|
if @escape == :once
|
40
|
-
|
42
|
+
escape_once_code(value)
|
41
43
|
elsif @escape
|
42
|
-
|
44
|
+
escape_code(value)
|
43
45
|
else
|
44
46
|
"(#{value}).to_s"
|
45
47
|
end
|
46
48
|
]
|
47
49
|
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def escape_once(value)
|
54
|
+
if @escape_safe_buffer
|
55
|
+
::Haml::Helpers.escape_once_without_haml_xss(value)
|
56
|
+
else
|
57
|
+
::Haml::Helpers.escape_once(value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def escape(value)
|
62
|
+
if @escape_safe_buffer
|
63
|
+
::Haml::Helpers.html_escape_without_haml_xss(value)
|
64
|
+
else
|
65
|
+
::Haml::Helpers.html_escape(value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def escape_once_code(value)
|
70
|
+
"::Haml::Helpers.escape_once#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
|
71
|
+
end
|
72
|
+
|
73
|
+
def escape_code(value)
|
74
|
+
"::Haml::Helpers.html_escape#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
|
75
|
+
end
|
48
76
|
end
|
49
77
|
end
|
data/lib/haml/exec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'optparse'
|
3
4
|
require 'rbconfig'
|
4
5
|
require 'pp'
|
@@ -120,7 +121,7 @@ module Haml
|
|
120
121
|
@options[:input], @options[:output] = input, output
|
121
122
|
end
|
122
123
|
|
123
|
-
COLORS = {
|
124
|
+
COLORS = {red: 31, green: 32, yellow: 33}.freeze
|
124
125
|
|
125
126
|
# Prints a status message about performing the given action,
|
126
127
|
# colored using the given color (via terminal escapes) if possible.
|
@@ -337,11 +338,9 @@ END
|
|
337
338
|
end
|
338
339
|
|
339
340
|
def validate_ruby(code)
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
$!
|
344
|
-
end
|
341
|
+
eval("BEGIN {return nil}; #{code}", binding, @options[:filename] || "")
|
342
|
+
rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
|
343
|
+
$!
|
345
344
|
end
|
346
345
|
end
|
347
346
|
end
|
data/lib/haml/filters.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "tilt"
|
3
4
|
|
4
5
|
module Haml
|
@@ -119,7 +120,7 @@ module Haml
|
|
119
120
|
# @param text [String] The source text for the filter to process
|
120
121
|
# @return [String] The filtered result
|
121
122
|
# @raise [Haml::Error] if it's not overridden
|
122
|
-
def render(
|
123
|
+
def render(_text)
|
123
124
|
raise Error.new("#{self.inspect}#render not defined!")
|
124
125
|
end
|
125
126
|
|
@@ -130,7 +131,7 @@ module Haml
|
|
130
131
|
# @param text [String] The source text for the filter to process
|
131
132
|
# @return [String] The filtered result
|
132
133
|
# @raise [Haml::Error] if it or \{#render} isn't overridden
|
133
|
-
def render_with_options(text,
|
134
|
+
def render_with_options(text, _options)
|
134
135
|
render(text)
|
135
136
|
end
|
136
137
|
|
@@ -164,7 +165,11 @@ module Haml
|
|
164
165
|
if contains_interpolation?(text)
|
165
166
|
return if options[:suppress_eval]
|
166
167
|
|
167
|
-
|
168
|
+
escape = options[:escape_filter_interpolations]
|
169
|
+
# `escape_filter_interpolations` defaults to `escape_html` if unset.
|
170
|
+
escape = options[:escape_html] if escape.nil?
|
171
|
+
|
172
|
+
text = unescape_interpolation(text, escape).gsub(/(\\+)n/) do |s|
|
168
173
|
escapes = $1.size
|
169
174
|
next s if escapes % 2 == 0
|
170
175
|
"#{'\\' * (escapes - 1)}\n"
|
@@ -182,9 +187,8 @@ RUBY
|
|
182
187
|
return
|
183
188
|
end
|
184
189
|
|
185
|
-
rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text, compiler.options), compiler.options[:preserve])
|
186
|
-
rendered.rstrip
|
187
|
-
push_text("#{rendered}\n")
|
190
|
+
rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text.to_s, compiler.options), compiler.options[:preserve])
|
191
|
+
push_text("#{rendered.rstrip}\n")
|
188
192
|
end
|
189
193
|
end
|
190
194
|
end
|
@@ -247,10 +251,7 @@ RUBY
|
|
247
251
|
|
248
252
|
# @see Base#render
|
249
253
|
def render(text)
|
250
|
-
|
251
|
-
text.rstrip!
|
252
|
-
text.gsub!("\n", "\n ")
|
253
|
-
"<![CDATA[#{text}\n]]>"
|
254
|
+
"<![CDATA[#{"\n#{text.rstrip}".gsub("\n", "\n ")}\n]]>"
|
254
255
|
end
|
255
256
|
end
|
256
257
|
|
data/lib/haml/generator.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Haml
|
3
4
|
module Helpers
|
4
5
|
module ActionViewMods
|
@@ -52,10 +53,12 @@ module ActionView
|
|
52
53
|
end
|
53
54
|
|
54
55
|
module TagHelper
|
56
|
+
DEFAULT_PRESERVE_OPTIONS = %w(textarea pre code).freeze
|
57
|
+
|
55
58
|
def content_tag_with_haml(name, *args, &block)
|
56
59
|
return content_tag_without_haml(name, *args, &block) unless is_haml?
|
57
60
|
|
58
|
-
preserve = haml_buffer.options.fetch(:preserve,
|
61
|
+
preserve = haml_buffer.options.fetch(:preserve, DEFAULT_PRESERVE_OPTIONS).include?(name.to_s)
|
59
62
|
|
60
63
|
if block_given? && block_is_haml?(block) && preserve
|
61
64
|
return content_tag_without_haml(name, *args) do
|