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
  module Haml
3
4
  module Helpers
4
5
  # This module overrides Haml helpers to work properly
@@ -7,12 +8,15 @@ module Haml
7
8
  # to work with Rails' XSS protection methods.
8
9
  module XssMods
9
10
  def self.included(base)
10
- %w[html_escape find_and_preserve preserve list_of surround
11
- precede succeed capture_haml haml_concat haml_internal_concat haml_indent
12
- escape_once].each do |name|
11
+ %w[find_and_preserve preserve list_of surround
12
+ precede succeed capture_haml haml_concat haml_internal_concat haml_indent].each do |name|
13
13
  base.send(:alias_method, "#{name}_without_haml_xss", name)
14
14
  base.send(:alias_method, name, "#{name}_with_haml_xss")
15
15
  end
16
+ # Those two always have _without_haml_xss
17
+ %w[html_escape escape_once].each do |name|
18
+ base.send(:alias_method, name, "#{name}_with_haml_xss")
19
+ end
16
20
  end
17
21
 
18
22
  # Don't escape text that's already safe,
data/lib/haml/helpers.rb CHANGED
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  require 'erb'
3
4
 
4
5
  module Haml
@@ -109,10 +110,7 @@ MESSAGE
109
110
  # @yield The block within which to escape newlines
110
111
  def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
111
112
  return find_and_preserve(capture_haml(&block), input || tags) if block
112
- tags = tags.each_with_object('') do |t, s|
113
- s << '|' unless s.empty?
114
- s << Regexp.escape(t)
115
- end
113
+ tags = tags.map { |tag| Regexp.escape(tag) }.join('|')
116
114
  re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
117
115
  input.to_s.gsub(re) do |s|
118
116
  s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
@@ -200,8 +198,8 @@ MESSAGE
200
198
  # @yield [item] A block which contains Haml code that goes within list items
201
199
  # @yieldparam item An element of `enum`
202
200
  def list_of(enum, opts={}, &block)
203
- opts_attributes = opts.each_with_object('') {|(k, v), s| s << " #{k}='#{v}'"}
204
- enum.each_with_object('') do |i, ret|
201
+ opts_attributes = opts.map { |k, v| " #{k}='#{v}'" }.join
202
+ enum.map do |i|
205
203
  result = capture_haml(i, &block)
206
204
 
207
205
  if result.count("\n") > 1
@@ -211,9 +209,8 @@ MESSAGE
211
209
  result.strip!
212
210
  end
213
211
 
214
- ret << "\n" unless ret.empty?
215
- ret << %Q!<li#{opts_attributes}>#{result}</li>!
216
- end
212
+ %Q!<li#{opts_attributes}>#{result}</li>!
213
+ end.join("\n")
217
214
  end
218
215
 
219
216
  # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
@@ -596,7 +593,7 @@ MESSAGE
596
593
  end
597
594
 
598
595
  # Characters that need to be escaped to HTML entities from user input
599
- HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;' }
596
+ HTML_ESCAPE = {'&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;'}.freeze
600
597
 
601
598
  HTML_ESCAPE_REGEX = /['"><&]/
602
599
 
@@ -610,9 +607,12 @@ MESSAGE
610
607
  # @param text [String] The string to sanitize
611
608
  # @return [String] The sanitized string
612
609
  def html_escape(text)
613
- ERB::Util.html_escape(text)
610
+ CGI.escapeHTML(text.to_s)
614
611
  end
615
612
 
613
+ # Always escape text regardless of html_safe?
614
+ alias_method :html_escape_without_haml_xss, :html_escape
615
+
616
616
  HTML_ESCAPE_ONCE_REGEX = /['"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
617
617
 
618
618
  # Escapes HTML entities in `text`, but without escaping an ampersand
@@ -625,6 +625,9 @@ MESSAGE
625
625
  text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
626
626
  end
627
627
 
628
+ # Always escape text once regardless of html_safe?
629
+ alias_method :escape_once_without_haml_xss, :escape_once
630
+
628
631
  # Returns whether or not the current template is a Haml template.
629
632
  #
630
633
  # This function, unlike other {Haml::Helpers} functions,
@@ -704,4 +707,3 @@ class Object
704
707
  false
705
708
  end
706
709
  end
707
-
data/lib/haml/options.rb CHANGED
@@ -1,47 +1,44 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # This class encapsulates all of the configuration options that Haml
4
5
  # understands. Please see the {file:REFERENCE.md#options Haml Reference} to
5
6
  # learn how to set the options.
6
7
  class Options
7
-
8
8
  @valid_formats = [:html4, :html5, :xhtml]
9
-
10
9
  @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :format,
11
- :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs, :cdata]
10
+ :encoding, :escape_html, :escape_filter_interpolations, :escape_attrs, :hyphenate_data_attrs, :cdata]
12
11
 
13
- # The default option values.
14
- # @return Hash
15
- def self.defaults
16
- @defaults ||= Haml::TempleEngine.options.to_hash.merge(encoding: 'UTF-8')
17
- end
12
+ class << self
13
+ # The default option values.
14
+ # @return Hash
15
+ def defaults
16
+ @defaults ||= Haml::TempleEngine.options.to_hash.merge(encoding: 'UTF-8')
17
+ end
18
18
 
19
- # An array of valid values for the `:format` option.
20
- # @return Array
21
- def self.valid_formats
22
- @valid_formats
23
- end
19
+ # An array of valid values for the `:format` option.
20
+ # @return Array
21
+ attr_reader :valid_formats
24
22
 
25
- # An array of keys that will be used to provide a hash of options to
26
- # {Haml::Buffer}.
27
- # @return Hash
28
- def self.buffer_option_keys
29
- @buffer_option_keys
30
- end
23
+ # An array of keys that will be used to provide a hash of options to
24
+ # {Haml::Buffer}.
25
+ # @return Hash
26
+ attr_reader :buffer_option_keys
31
27
 
32
- # Returns a subset of defaults: those that {Haml::Buffer} cares about.
33
- # @return [{Symbol => Object}] The options hash
34
- def self.buffer_defaults
35
- @buffer_defaults ||= buffer_option_keys.inject({}) do |hash, key|
36
- hash.merge(key => defaults[key])
28
+ # Returns a subset of defaults: those that {Haml::Buffer} cares about.
29
+ # @return [{Symbol => Object}] The options hash
30
+ def buffer_defaults
31
+ @buffer_defaults ||= buffer_option_keys.inject({}) do |hash, key|
32
+ hash.merge(key => defaults[key])
33
+ end
37
34
  end
38
- end
39
35
 
40
- def self.wrap(options)
41
- if options.is_a?(Options)
42
- options
43
- else
44
- Options.new(options)
36
+ def wrap(options)
37
+ if options.is_a?(Options)
38
+ options
39
+ else
40
+ Options.new(options)
41
+ end
45
42
  end
46
43
  end
47
44
 
@@ -85,6 +82,13 @@ module Haml
85
82
  # Defaults to false.
86
83
  attr_accessor :escape_html
87
84
 
85
+ # Sets whether or not to escape HTML-sensitive characters in interpolated strings.
86
+ # See also {file:REFERENCE.md#escaping_html Escaping HTML} and
87
+ # {file:REFERENCE.md#unescaping_html Unescaping HTML}.
88
+ #
89
+ # Defaults to the current value of `escape_html`.
90
+ attr_accessor :escape_filter_interpolations
91
+
88
92
  # The name of the Haml file being parsed.
89
93
  # This is only used as information when exceptions are raised. This is
90
94
  # automatically assigned when working through ActionView, so it's really
@@ -131,7 +135,7 @@ module Haml
131
135
  # formatting errors.
132
136
  #
133
137
  # Defaults to `false`.
134
- attr_reader :remove_whitespace
138
+ attr_accessor :remove_whitespace
135
139
 
136
140
  # Whether or not attribute hashes and Ruby scripts designated by `=` or `~`
137
141
  # should be evaluated. If this is `true`, said scripts are rendered as empty
@@ -167,7 +171,7 @@ module Haml
167
171
  # Key is filter name in String and value is Class to use. Defaults to {}.
168
172
  attr_accessor :filters
169
173
 
170
- def initialize(values = {}, &block)
174
+ def initialize(values = {})
171
175
  defaults.each {|k, v| instance_variable_set :"@#{k}", v}
172
176
  values.each {|k, v| send("#{k}=", v) if defaults.has_key?(k) && !v.nil?}
173
177
  yield if block_given?
@@ -237,10 +241,6 @@ module Haml
237
241
  xhtml? || @cdata
238
242
  end
239
243
 
240
- def remove_whitespace=(value)
241
- @remove_whitespace = value
242
- end
243
-
244
244
  def encoding=(value)
245
245
  return unless value
246
246
  @encoding = value.is_a?(Encoding) ? value.name : value.to_s
data/lib/haml/parser.rb CHANGED
@@ -1,4 +1,6 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
3
+ require 'ripper'
2
4
  require 'strscan'
3
5
 
4
6
  module Haml
@@ -60,7 +62,7 @@ module Haml
60
62
  SILENT_SCRIPT,
61
63
  ESCAPE,
62
64
  FILTER
63
- ]
65
+ ].freeze
64
66
 
65
67
  # The value of the character that designates that a line is part
66
68
  # of a multiline string.
@@ -74,8 +76,8 @@ module Haml
74
76
  #
75
77
  BLOCK_WITH_SPACES = /do\s*\|\s*[^\|]*\s+\|\z/
76
78
 
77
- MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when]
78
- START_BLOCK_KEYWORDS = %w[if begin case unless]
79
+ MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when].freeze
80
+ START_BLOCK_KEYWORDS = %w[if begin case unless].freeze
79
81
  # Try to parse assignments to block starters as best as possible
80
82
  START_BLOCK_KEYWORD_REGEX = /(?:\w+(?:,\s*\w+)*\s*=\s*)?(#{START_BLOCK_KEYWORDS.join('|')})/
81
83
  BLOCK_KEYWORD_REGEX = /^-?\s*(?:(#{MID_BLOCK_KEYWORDS.join('|')})|#{START_BLOCK_KEYWORD_REGEX.source})\b/
@@ -89,6 +91,9 @@ module Haml
89
91
  ID_KEY = 'id'.freeze
90
92
  CLASS_KEY = 'class'.freeze
91
93
 
94
+ # Used for scanning old attributes, substituting the first '{'
95
+ METHOD_CALL_PREFIX = 'a('
96
+
92
97
  def initialize(options)
93
98
  @options = Options.wrap(options)
94
99
  # Record the indent levels of "if" statements to validate the subsequent
@@ -178,7 +183,7 @@ module Haml
178
183
  private
179
184
 
180
185
  # @private
181
- class Line < Struct.new(:whitespace, :text, :full, :index, :parser, :eod)
186
+ Line = Struct.new(:whitespace, :text, :full, :index, :parser, :eod) do
182
187
  alias_method :eod?, :eod
183
188
 
184
189
  # @private
@@ -194,25 +199,26 @@ module Haml
194
199
  end
195
200
 
196
201
  # @private
197
- class ParseNode < Struct.new(:type, :line, :value, :parent, :children)
202
+ ParseNode = Struct.new(:type, :line, :value, :parent, :children) do
198
203
  def initialize(*args)
199
204
  super
200
205
  self.children ||= []
201
206
  end
202
207
 
203
208
  def inspect
204
- %Q[(#{type} #{value.inspect}#{children.each_with_object('') {|c, s| s << "\n#{c.inspect.gsub!(/^/, ' ')}"}})]
209
+ %Q[(#{type} #{value.inspect}#{children.each_with_object(''.dup) {|c, s| s << "\n#{c.inspect.gsub!(/^/, ' ')}"}})].dup
205
210
  end
206
211
  end
207
212
 
208
213
  # @param [String] new - Hash literal including dynamic values.
209
214
  # @param [String] old - Hash literal including dynamic values or Ruby literal of multiple Hashes which MUST be interpreted as method's last arguments.
210
- class DynamicAttributes < Struct.new(:new, :old)
215
+ DynamicAttributes = Struct.new(:new, :old) do
216
+ undef :old=
211
217
  def old=(value)
212
218
  unless value =~ /\A{.*}\z/m
213
219
  raise ArgumentError.new('Old attributes must start with "{" and end with "}"')
214
220
  end
215
- super
221
+ self[:old] = value
216
222
  end
217
223
 
218
224
  # This will be a literal for Haml::Buffer#attributes's last argument, `attributes_hashes`.
@@ -287,7 +293,7 @@ module Haml
287
293
  end
288
294
 
289
295
  def block_keyword(text)
290
- return unless keyword = text.scan(BLOCK_KEYWORD_REGEX)[0]
296
+ return unless (keyword = text.scan(BLOCK_KEYWORD_REGEX)[0])
291
297
  keyword[0] || keyword[1]
292
298
  end
293
299
 
@@ -305,7 +311,7 @@ module Haml
305
311
  return ParseNode.new(:plain, line.index + 1, :text => line.text)
306
312
  end
307
313
 
308
- escape_html = @options.escape_html if escape_html.nil?
314
+ escape_html = @options.escape_html && @options.mime_type != 'text/plain' if escape_html.nil?
309
315
  line.text = unescape_interpolation(line.text, escape_html)
310
316
  script(line, false)
311
317
  end
@@ -534,7 +540,7 @@ module Haml
534
540
 
535
541
  # Post-process case statements to normalize the nesting of "when" clauses
536
542
  return unless node.value[:keyword] == "case"
537
- return unless first = node.children.first
543
+ return unless (first = node.children.first)
538
544
  return unless first.type == :silent_script && first.value[:keyword] == "when"
539
545
  return if first.children.empty?
540
546
  # If the case node has a "when" child with children, it's the
@@ -583,9 +589,9 @@ module Haml
583
589
  scanner = StringScanner.new(text)
584
590
  scanner.scan(/\s+/)
585
591
  until scanner.eos?
586
- return unless key = scanner.scan(LITERAL_VALUE_REGEX)
592
+ return unless (key = scanner.scan(LITERAL_VALUE_REGEX))
587
593
  return unless scanner.scan(/\s*=>\s*/)
588
- return unless value = scanner.scan(LITERAL_VALUE_REGEX)
594
+ return unless (value = scanner.scan(LITERAL_VALUE_REGEX))
589
595
  return unless scanner.scan(/\s*(?:,|$)\s*/)
590
596
  attributes[eval(key).to_s] = eval(value).to_s
591
597
  end
@@ -649,13 +655,18 @@ module Haml
649
655
  # @return [String] rest
650
656
  # @return [Integer] last_line
651
657
  def parse_old_attributes(text)
652
- text = text.dup
653
658
  last_line = @line.index + 1
654
659
 
655
660
  begin
656
- attributes_hash, rest = balance(text, ?{, ?})
661
+ # Old attributes often look like a valid Hash literal, but it sometimes allow code like
662
+ # `{ hash, foo: bar }`, which is compiled to `_hamlout.attributes({}, nil, hash, foo: bar)`.
663
+ #
664
+ # To scan such code correctly, this scans `a( hash, foo: bar }` instead, stops when there is
665
+ # 1 more :on_embexpr_end (the last '}') than :on_embexpr_beg, and resurrects '{' afterwards.
666
+ balanced, rest = balance_tokens(text.sub(?{, METHOD_CALL_PREFIX), :on_embexpr_beg, :on_embexpr_end, count: 1)
667
+ attributes_hash = balanced.sub(METHOD_CALL_PREFIX, ?{)
657
668
  rescue SyntaxError => e
658
- if text.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
669
+ if e.message == Error.message(:unbalanced_brackets) && !@template.empty?
659
670
  text << "\n#{@next_line.text}"
660
671
  last_line += 1
661
672
  next_line
@@ -698,7 +709,7 @@ module Haml
698
709
  end
699
710
 
700
711
  static_attributes = {}
701
- dynamic_attributes = "{"
712
+ dynamic_attributes = "{".dup
702
713
  attributes.each do |name, (type, val)|
703
714
  if type == :static
704
715
  static_attributes[name] = val
@@ -713,7 +724,7 @@ module Haml
713
724
  end
714
725
 
715
726
  def parse_new_attribute(scanner)
716
- unless name = scanner.scan(/[-:\w]+/)
727
+ unless (name = scanner.scan(/[-:\w]+/))
717
728
  return if scanner.scan(/\)/)
718
729
  return false
719
730
  end
@@ -722,8 +733,8 @@ module Haml
722
733
  return name, [:static, true] unless scanner.scan(/=/) #/end
723
734
 
724
735
  scanner.scan(/\s*/)
725
- unless quote = scanner.scan(/["']/)
726
- return false unless var = scanner.scan(/(@@?|\$)?\w+/)
736
+ unless (quote = scanner.scan(/["']/))
737
+ return false unless (var = scanner.scan(/(@@?|\$)?\w+/))
727
738
  return name, [:dynamic, var]
728
739
  end
729
740
 
@@ -738,7 +749,7 @@ module Haml
738
749
 
739
750
  return name, [:static, content.first[1]] if content.size == 1
740
751
  return name, [:dynamic,
741
- %!"#{content.each_with_object('') {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
752
+ %!"#{content.each_with_object(''.dup) {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
742
753
  end
743
754
 
744
755
  def next_line
@@ -809,6 +820,25 @@ module Haml
809
820
  Haml::Util.balance(*args) or raise(SyntaxError.new(Error.message(:unbalanced_brackets)))
810
821
  end
811
822
 
823
+ # Unlike #balance, this balances Ripper tokens to balance something like `{ a: "}" }` correctly.
824
+ def balance_tokens(buf, start, finish, count: 0)
825
+ text = ''.dup
826
+ Ripper.lex(buf).each do |_, token, str|
827
+ text << str
828
+ case token
829
+ when start
830
+ count += 1
831
+ when finish
832
+ count -= 1
833
+ end
834
+
835
+ if count == 0
836
+ return text, buf.sub(text, '')
837
+ end
838
+ end
839
+ raise SyntaxError.new(Error.message(:unbalanced_brackets))
840
+ end
841
+
812
842
  def block_opened?
813
843
  @next_line.tabs > @line.tabs
814
844
  end
data/lib/haml/plugin.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
 
4
5
  # This module makes Haml work with Rails using the template handler API.
5
6
  class Plugin
7
+ class << self
8
+ attr_accessor :annotate_rendered_view_with_filenames
9
+ end
10
+ self.annotate_rendered_view_with_filenames = false
11
+
6
12
  def handles_encoding?; true; end
7
13
 
8
- def compile(template)
14
+ def compile(template, source)
9
15
  options = Haml::Template.options.dup
10
16
  if template.respond_to?(:type)
11
17
  options[:mime_type] = template.type
@@ -13,14 +19,28 @@ module Haml
13
19
  options[:mime_type] = template.mime_type
14
20
  end
15
21
  options[:filename] = template.identifier
16
- Haml::Engine.new(template.source, options).compiler.precompiled_with_ambles(
22
+
23
+ preamble = '@output_buffer = output_buffer ||= ActionView::OutputBuffer.new if defined?(ActionView::OutputBuffer);'
24
+ postamble = ''
25
+
26
+ if self.class.annotate_rendered_view_with_filenames
27
+ # short_identifier is only available in Rails 6+. On older versions, 'inspect' gives similar results.
28
+ ident = template.respond_to?(:short_identifier) ? template.short_identifier : template.inspect
29
+ preamble += "haml_concat '<!-- BEGIN #{ident} -->'.html_safe;"
30
+ postamble += "haml_concat '<!-- END #{ident} -->'.html_safe;"
31
+ end
32
+
33
+ Haml::Engine.new(source, options).compiler.precompiled_with_ambles(
17
34
  [],
18
- after_preamble: '@output_buffer = output_buffer ||= ActionView::OutputBuffer.new if defined?(ActionView::OutputBuffer)',
35
+ after_preamble: preamble,
36
+ before_postamble: postamble
19
37
  )
20
38
  end
21
39
 
22
- def self.call(template)
23
- new.compile(template)
40
+ def self.call(template, source = nil)
41
+ source ||= template.source
42
+
43
+ new.compile(template, source)
24
44
  end
25
45
 
26
46
  def cache_fragment(block, name = {}, options = nil)
data/lib/haml/railtie.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'haml/template/options'
3
4
 
4
5
  # check for a compatible Rails version when Haml is loaded
5
6
  if (activesupport_spec = Gem.loaded_specs['activesupport'])
6
- if activesupport_spec.version.to_s < '3.2'
7
- raise Exception.new("\n\n** Haml now requires Rails 3.2 and later. Use Haml version 4.0.4\n\n")
7
+ if activesupport_spec.version.to_s < '4.0'
8
+ raise Exception.new("\n\n** Haml now requires Rails 4.0 and later. Use Haml version 4.0.x\n\n")
8
9
  end
9
10
  end
10
11
 
@@ -33,8 +34,7 @@ module Haml
33
34
  # solved by looking for ::Erubi first.
34
35
  # However, in JRuby, the const_defined? finds it anyway, so we must make sure that it's
35
36
  # not just a reference to ::Erubi.
36
- if defined?(::Erubi) && const_defined?('ActionView::Template::Handlers::ERB::Erubi') &&
37
- ActionView::Template::Handlers::ERB::Erubi != ::Erubi
37
+ if defined?(::Erubi) && (::ActionView::Template::Handlers::ERB.const_get('Erubi') != ::Erubi)
38
38
  require "haml/helpers/safe_erubi_template"
39
39
  Haml::Filters::RailsErb.template_class = Haml::SafeErubiTemplate
40
40
  else
@@ -42,6 +42,11 @@ module Haml
42
42
  Haml::Filters::RailsErb.template_class = Haml::SafeErubisTemplate
43
43
  end
44
44
  Haml::Template.options[:filters] = { 'erb' => Haml::Filters::RailsErb }
45
+
46
+ if app.config.respond_to?(:action_view) &&
47
+ app.config.action_view.annotate_rendered_view_with_filenames
48
+ Haml::Plugin.annotate_rendered_view_with_filenames = true
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  module Filters
4
5
  # This is an extension of Sass::Rails's SassTemplate class that allows
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # We keep options in its own self-contained file
3
4
  # so that we can load it independently in Rails 3,
4
5
  # where the full template stuff is lazy-loaded.
data/lib/haml/template.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'haml/template/options'
3
4
  if defined?(ActiveSupport)
4
5
  ActiveSupport.on_load(:action_view) do
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  require 'temple'
3
4
  require 'haml/escapable'
4
5
  require 'haml/generator'
@@ -13,6 +14,7 @@ module Haml
13
14
  encoding: nil,
14
15
  escape_attrs: true,
15
16
  escape_html: false,
17
+ escape_filter_interpolations: nil,
16
18
  filename: '(haml)',
17
19
  format: :html5,
18
20
  hyphenate_data_attrs: true,
@@ -49,12 +51,12 @@ module Haml
49
51
  # @return [String]
50
52
  def precompiled
51
53
  encoding = Encoding.find(@encoding || '')
52
- return @precompiled.force_encoding(encoding) if encoding == Encoding::ASCII_8BIT
54
+ return @precompiled.dup.force_encoding(encoding) if encoding == Encoding::ASCII_8BIT
53
55
  return @precompiled.encode(encoding)
54
56
  end
55
57
 
56
58
  def precompiled_with_return_value
57
- "#{precompiled};#{precompiled_method_return_value}"
59
+ "#{precompiled};#{precompiled_method_return_value}".dup
58
60
  end
59
61
 
60
62
  # The source code that is evaluated to produce the Haml document.
@@ -63,21 +65,22 @@ module Haml
63
65
  # (see {file:REFERENCE.md#encodings the `:encoding` option}).
64
66
  #
65
67
  # @return [String]
66
- def precompiled_with_ambles(local_names, after_preamble: '')
67
- preamble = <<END.tr!("\n", ';')
68
+ def precompiled_with_ambles(local_names, after_preamble: '', before_postamble: '')
69
+ preamble = <<END.tr("\n", ';')
68
70
  begin
69
71
  extend Haml::Helpers
70
72
  _hamlout = @haml_buffer = Haml::Buffer.new(haml_buffer, #{Options.new(options).for_buffer.inspect})
71
73
  _erbout = _hamlout.buffer
72
74
  #{after_preamble}
73
75
  END
74
- postamble = <<END.tr!("\n", ';')
76
+ postamble = <<END.tr("\n", ';')
77
+ #{before_postamble}
75
78
  #{precompiled_method_return_value}
76
79
  ensure
77
80
  @haml_buffer = @haml_buffer.upper if @haml_buffer
78
81
  end
79
82
  END
80
- "#{preamble}#{locals_code(local_names)}#{precompiled}#{postamble}"
83
+ "#{preamble}#{locals_code(local_names)}#{precompiled}#{postamble}".dup
81
84
  end
82
85
 
83
86
  private
@@ -99,12 +102,12 @@ END
99
102
  def locals_code(names)
100
103
  names = names.keys if Hash === names
101
104
 
102
- names.each_with_object('') do |name, code|
105
+ names.map do |name|
103
106
  # Can't use || because someone might explicitly pass in false with a symbol
104
107
  sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
105
108
  str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
106
- code << "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local};"
107
- end
109
+ "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local};"
110
+ end.join
108
111
  end
109
112
 
110
113
  def inspect_obj(obj)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # A module to count lines of expected code. This would be faster than actual code generation
4
5
  # and counting newlines in it.
data/lib/haml/util.rb CHANGED
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
 
3
3
  begin
4
4
  require 'erubis/tiny'
@@ -166,7 +166,7 @@ MSG
166
166
  # and the rest of the string.
167
167
  # `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
168
168
  def balance(scanner, start, finish, count = 0)
169
- str = ''
169
+ str = ''.dup
170
170
  scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
171
171
  regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
172
172
  while scanner.scan(regexp)
@@ -199,7 +199,7 @@ MSG
199
199
  end
200
200
 
201
201
  def unescape_interpolation(str, escape_html = nil)
202
- res = ''
202
+ res = ''.dup
203
203
  rest = Haml::Util.handle_interpolation str.dump do |scan|
204
204
  escapes = (scan[2].size - 1) / 2
205
205
  char = scan[3] # '{', '@' or '$'
@@ -212,8 +212,8 @@ MSG
212
212
  else
213
213
  scan.scan(/\w+/)
214
214
  end
215
- content = eval('"' + interpolated + '"')
216
- content.prepend(char) if char == '@' || char == '$'
215
+ content = eval("\"#{interpolated}\"")
216
+ content = "#{char}#{content}" if char == '@' || char == '$'
217
217
  content = "Haml::Helpers.html_escape((#{content}))" if escape_html
218
218
 
219
219
  res << "\#{#{content}}"
@@ -234,7 +234,7 @@ MSG
234
234
  scanner = StringScanner.new(str.dup.force_encoding(Encoding::ASCII_8BIT))
235
235
  bom = scanner.scan(/\xEF\xBB\xBF/n)
236
236
  return bom unless scanner.scan(/-\s*#\s*/n)
237
- if coding = try_parse_haml_emacs_magic_comment(scanner)
237
+ if (coding = try_parse_haml_emacs_magic_comment(scanner))
238
238
  return bom, coding
239
239
  end
240
240
 
data/lib/haml/version.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
- VERSION = "5.0.4"
4
+ VERSION = "5.2.2"
4
5
  end
data/lib/haml.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'haml/version'
3
4
 
4
5
  # The module that contains everything Haml-related: