haml 5.0.4 → 5.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- class AttributeValue < Struct.new(:type, :key, :value)
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, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
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, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
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
- [:dynamic, "_hamlout.attributes({ #{hash_content} }, nil)"]
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 { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
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.to_literal
134
+ attr_literal(values.first)
142
135
  else
143
- "::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&:to_literal).join(', ')})"
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
- "#{Haml::Util.inspect_obj(str)}.freeze"
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.to_literal) }
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: false
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
- else
191
- if @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
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 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">'
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: false
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
- eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" <<
170
- @temple_engine.precompiled_with_ambles(local_names) << "}\n", scope, @options.filename, @options.line)
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
- :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
8
- :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
9
- :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
10
- :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
11
- :filter_not_defined => 'Filter "%s" is not defined.',
12
- :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
13
- :illegal_element => "Illegal element: classes and ids must have values.",
14
- :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
15
- :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
16
- :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
17
- :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
18
- :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
19
- :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
20
- :indenting_at_start => "Indenting at the beginning of the document is illegal.",
21
- :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
22
- :invalid_attribute_list => 'Invalid attribute list: %s.',
23
- :invalid_filter_name => 'Invalid filter name ":%s".',
24
- :invalid_tag => 'Invalid tag: "%s".',
25
- :missing_if => 'Got "%s" with no preceding "if"',
26
- :no_ruby_code => "There's no Ruby code for %s to evaluate.",
27
- :self_closing_content => "Self-closing tags can't have content.",
28
- :unbalanced_brackets => 'Unbalanced brackets.',
29
- :no_end => <<-END
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}"
@@ -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
- old = @escape
17
- @escape = flag
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 = old
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
- @once_escaper[value]
29
+ escape_once(value)
28
30
  elsif @escape
29
- @escaper[value]
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
- @once_escape_code % value
42
+ escape_once_code(value)
41
43
  elsif @escape
42
- @escape_code % value
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 = { :red => 31, :green => 32, :yellow => 33 }
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
- begin
341
- eval("BEGIN {return nil}; #{code}", binding, @options[:filename] || "")
342
- rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
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: false
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(text)
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, options)
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
- text = unescape_interpolation(text, options[:escape_html]).gsub(/(\\+)n/) do |s|
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
- text = "\n#{text}"
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
 
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # Ruby code generator, which is a limited version of Temple::Generator.
4
5
  # Limit methods since Haml doesn't need most of them.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  module Helpers
4
5
  @@action_view_defined = true
@@ -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, %w[textarea pre code]).include?(name.to_s)
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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActionView
3
4
  module Helpers
4
5
  module CaptureHelper
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'action_view'
3
4
 
4
5
  module Haml
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'action_view'
3
4
 
4
5
  module Haml