haml 5.0.3 → 5.2.0

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 '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
@@ -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
- # 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
-
@@ -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
@@ -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,
@@ -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
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  require 'strscan'
3
4
 
4
5
  module Haml
@@ -60,7 +61,7 @@ module Haml
60
61
  SILENT_SCRIPT,
61
62
  ESCAPE,
62
63
  FILTER
63
- ]
64
+ ].freeze
64
65
 
65
66
  # The value of the character that designates that a line is part
66
67
  # of a multiline string.
@@ -74,8 +75,8 @@ module Haml
74
75
  #
75
76
  BLOCK_WITH_SPACES = /do\s*\|\s*[^\|]*\s+\|\z/
76
77
 
77
- MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when]
78
- START_BLOCK_KEYWORDS = %w[if begin case unless]
78
+ MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when].freeze
79
+ START_BLOCK_KEYWORDS = %w[if begin case unless].freeze
79
80
  # Try to parse assignments to block starters as best as possible
80
81
  START_BLOCK_KEYWORD_REGEX = /(?:\w+(?:,\s*\w+)*\s*=\s*)?(#{START_BLOCK_KEYWORDS.join('|')})/
81
82
  BLOCK_KEYWORD_REGEX = /^-?\s*(?:(#{MID_BLOCK_KEYWORDS.join('|')})|#{START_BLOCK_KEYWORD_REGEX.source})\b/
@@ -178,7 +179,7 @@ module Haml
178
179
  private
179
180
 
180
181
  # @private
181
- class Line < Struct.new(:whitespace, :text, :full, :index, :parser, :eod)
182
+ Line = Struct.new(:whitespace, :text, :full, :index, :parser, :eod) do
182
183
  alias_method :eod?, :eod
183
184
 
184
185
  # @private
@@ -194,25 +195,26 @@ module Haml
194
195
  end
195
196
 
196
197
  # @private
197
- class ParseNode < Struct.new(:type, :line, :value, :parent, :children)
198
+ ParseNode = Struct.new(:type, :line, :value, :parent, :children) do
198
199
  def initialize(*args)
199
200
  super
200
201
  self.children ||= []
201
202
  end
202
203
 
203
204
  def inspect
204
- %Q[(#{type} #{value.inspect}#{children.each_with_object('') {|c, s| s << "\n#{c.inspect.gsub!(/^/, ' ')}"}})]
205
+ %Q[(#{type} #{value.inspect}#{children.each_with_object(''.dup) {|c, s| s << "\n#{c.inspect.gsub!(/^/, ' ')}"}})].dup
205
206
  end
206
207
  end
207
208
 
208
209
  # @param [String] new - Hash literal including dynamic values.
209
210
  # @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)
211
+ DynamicAttributes = Struct.new(:new, :old) do
212
+ undef :old=
211
213
  def old=(value)
212
214
  unless value =~ /\A{.*}\z/m
213
215
  raise ArgumentError.new('Old attributes must start with "{" and end with "}"')
214
216
  end
215
- super
217
+ self[:old] = value
216
218
  end
217
219
 
218
220
  # This will be a literal for Haml::Buffer#attributes's last argument, `attributes_hashes`.
@@ -287,7 +289,7 @@ module Haml
287
289
  end
288
290
 
289
291
  def block_keyword(text)
290
- return unless keyword = text.scan(BLOCK_KEYWORD_REGEX)[0]
292
+ return unless (keyword = text.scan(BLOCK_KEYWORD_REGEX)[0])
291
293
  keyword[0] || keyword[1]
292
294
  end
293
295
 
@@ -305,7 +307,7 @@ module Haml
305
307
  return ParseNode.new(:plain, line.index + 1, :text => line.text)
306
308
  end
307
309
 
308
- escape_html = @options.escape_html if escape_html.nil?
310
+ escape_html = @options.escape_html && @options.mime_type != 'text/plain' if escape_html.nil?
309
311
  line.text = unescape_interpolation(line.text, escape_html)
310
312
  script(line, false)
311
313
  end
@@ -534,7 +536,7 @@ module Haml
534
536
 
535
537
  # Post-process case statements to normalize the nesting of "when" clauses
536
538
  return unless node.value[:keyword] == "case"
537
- return unless first = node.children.first
539
+ return unless (first = node.children.first)
538
540
  return unless first.type == :silent_script && first.value[:keyword] == "when"
539
541
  return if first.children.empty?
540
542
  # If the case node has a "when" child with children, it's the
@@ -583,9 +585,9 @@ module Haml
583
585
  scanner = StringScanner.new(text)
584
586
  scanner.scan(/\s+/)
585
587
  until scanner.eos?
586
- return unless key = scanner.scan(LITERAL_VALUE_REGEX)
588
+ return unless (key = scanner.scan(LITERAL_VALUE_REGEX))
587
589
  return unless scanner.scan(/\s*=>\s*/)
588
- return unless value = scanner.scan(LITERAL_VALUE_REGEX)
590
+ return unless (value = scanner.scan(LITERAL_VALUE_REGEX))
589
591
  return unless scanner.scan(/\s*(?:,|$)\s*/)
590
592
  attributes[eval(key).to_s] = eval(value).to_s
591
593
  end
@@ -698,7 +700,7 @@ module Haml
698
700
  end
699
701
 
700
702
  static_attributes = {}
701
- dynamic_attributes = "{"
703
+ dynamic_attributes = "{".dup
702
704
  attributes.each do |name, (type, val)|
703
705
  if type == :static
704
706
  static_attributes[name] = val
@@ -713,7 +715,7 @@ module Haml
713
715
  end
714
716
 
715
717
  def parse_new_attribute(scanner)
716
- unless name = scanner.scan(/[-:\w]+/)
718
+ unless (name = scanner.scan(/[-:\w]+/))
717
719
  return if scanner.scan(/\)/)
718
720
  return false
719
721
  end
@@ -722,8 +724,8 @@ module Haml
722
724
  return name, [:static, true] unless scanner.scan(/=/) #/end
723
725
 
724
726
  scanner.scan(/\s*/)
725
- unless quote = scanner.scan(/["']/)
726
- return false unless var = scanner.scan(/(@@?|\$)?\w+/)
727
+ unless (quote = scanner.scan(/["']/))
728
+ return false unless (var = scanner.scan(/(@@?|\$)?\w+/))
727
729
  return name, [:dynamic, var]
728
730
  end
729
731
 
@@ -738,7 +740,7 @@ module Haml
738
740
 
739
741
  return name, [:static, content.first[1]] if content.size == 1
740
742
  return name, [:dynamic,
741
- %!"#{content.each_with_object('') {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
743
+ %!"#{content.each_with_object(''.dup) {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
742
744
  end
743
745
 
744
746
  def next_line