haml 5.0.4 → 5.2.2

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.
@@ -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