haml 5.0.4 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +36 -0
- data/.gitignore +1 -0
- data/.yardopts +1 -2
- data/CHANGELOG.md +51 -1
- data/Gemfile +2 -5
- data/MIT-LICENSE +2 -2
- data/README.md +22 -17
- data/REFERENCE.md +46 -12
- data/Rakefile +2 -13
- data/benchmark.rb +13 -9
- data/haml.gemspec +10 -3
- data/lib/haml/attribute_builder.rb +59 -3
- data/lib/haml/attribute_compiler.rb +46 -32
- data/lib/haml/attribute_parser.rb +3 -1
- data/lib/haml/buffer.rb +1 -54
- data/lib/haml/compiler.rb +23 -24
- data/lib/haml/engine.rb +12 -3
- data/lib/haml/error.rb +25 -24
- data/lib/haml/escapable.rb +39 -11
- data/lib/haml/exec.rb +5 -6
- data/lib/haml/filters.rb +12 -11
- data/lib/haml/generator.rb +2 -1
- data/lib/haml/helpers/action_view_extensions.rb +1 -0
- data/lib/haml/helpers/action_view_mods.rb +4 -1
- data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
- data/lib/haml/helpers/safe_erubi_template.rb +1 -0
- data/lib/haml/helpers/safe_erubis_template.rb +1 -0
- data/lib/haml/helpers/xss_mods.rb +7 -3
- data/lib/haml/helpers.rb +15 -13
- data/lib/haml/options.rb +36 -36
- data/lib/haml/parser.rb +52 -22
- data/lib/haml/plugin.rb +25 -5
- data/lib/haml/railtie.rb +9 -4
- data/lib/haml/sass_rails_filter.rb +1 -0
- data/lib/haml/template/options.rb +1 -0
- data/lib/haml/template.rb +1 -0
- data/lib/haml/temple_engine.rb +13 -10
- data/lib/haml/temple_line_counter.rb +1 -0
- data/lib/haml/util.rb +6 -6
- data/lib/haml/version.rb +2 -1
- data/lib/haml.rb +1 -0
- metadata +29 -10
- data/.travis.yml +0 -54
@@ -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[
|
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:
|
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.
|
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.
|
204
|
-
enum.
|
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
|
-
|
215
|
-
|
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 = {
|
596
|
+
HTML_ESCAPE = {'&' => '&', '<' => '<', '>' => '>', '"' => '"', "'" => '''}.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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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 = {}
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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:
|
35
|
+
after_preamble: preamble,
|
36
|
+
before_postamble: postamble
|
19
37
|
)
|
20
38
|
end
|
21
39
|
|
22
|
-
def self.call(template)
|
23
|
-
|
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 < '
|
7
|
-
raise Exception.new("\n\n** Haml now requires Rails
|
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) &&
|
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
|
data/lib/haml/template.rb
CHANGED
data/lib/haml/temple_engine.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# frozen_string_literal:
|
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
|
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
|
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.
|
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
|
-
|
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)
|
data/lib/haml/util.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
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(
|
216
|
-
content
|
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