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
  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: