haml 5.2.2 → 6.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/test.yml +13 -14
- data/.gitignore +16 -16
- data/.yardopts +0 -3
- data/CHANGELOG.md +116 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +17 -23
- data/REFERENCE.md +69 -145
- data/Rakefile +48 -81
- data/bin/bench +66 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/haml +6 -0
- data/ext/haml/extconf.rb +10 -0
- data/ext/haml/haml.c +537 -0
- data/ext/haml/hescape.c +108 -0
- data/ext/haml/hescape.h +20 -0
- data/haml.gemspec +39 -37
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +134 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +92 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +155 -0
- data/lib/haml/compiler/comment_compiler.rb +51 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +114 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +63 -296
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +48 -227
- data/lib/haml/error.rb +5 -4
- data/lib/haml/escape.rb +13 -0
- data/lib/haml/escape_any.rb +21 -0
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +59 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escape.rb +29 -0
- data/lib/haml/helpers.rb +3 -697
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +35 -0
- data/lib/haml/parser.rb +157 -22
- data/lib/haml/rails_helpers.rb +53 -0
- data/lib/haml/rails_template.rb +57 -0
- data/lib/haml/railtie.rb +3 -46
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +140 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +18 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml/whitespace.rb +8 -0
- data/lib/haml.rb +8 -20
- metadata +211 -55
- data/.gitmodules +0 -3
- data/TODO +0 -24
- data/benchmark.rb +0 -70
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -182
- data/lib/haml/escapable.rb +0 -77
- data/lib/haml/exec.rb +0 -347
- data/lib/haml/generator.rb +0 -42
- data/lib/haml/helpers/action_view_extensions.rb +0 -60
- data/lib/haml/helpers/action_view_mods.rb +0 -132
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
- data/lib/haml/helpers/safe_erubi_template.rb +0 -20
- data/lib/haml/helpers/safe_erubis_template.rb +0 -33
- data/lib/haml/helpers/xss_mods.rb +0 -114
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -54
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -124
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
data/lib/haml/html.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
3
|
+
class HTML < Temple::HTML::Fast
|
4
|
+
DEPRECATED_FORMATS = %i[html4 html5].freeze
|
5
|
+
|
6
|
+
def initialize(opts = {})
|
7
|
+
if DEPRECATED_FORMATS.include?(opts[:format])
|
8
|
+
opts = opts.dup
|
9
|
+
opts[:format] = :html
|
10
|
+
end
|
11
|
+
super(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
# This dispatcher supports Haml's "revealed" conditional comment.
|
15
|
+
def on_html_condcomment(condition, content, revealed = false)
|
16
|
+
on_html_comment [:multi,
|
17
|
+
[:static, "[#{condition}]>#{'<!-->' if revealed}"],
|
18
|
+
content,
|
19
|
+
[:static, "#{'<!--' if revealed}<![endif]"]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
3
|
+
module ObjectRef
|
4
|
+
class << self
|
5
|
+
def parse(args)
|
6
|
+
object, prefix = args
|
7
|
+
return {} unless object
|
8
|
+
|
9
|
+
suffix =
|
10
|
+
if object.respond_to?(:haml_object_ref)
|
11
|
+
object.haml_object_ref
|
12
|
+
else
|
13
|
+
underscore(object.class)
|
14
|
+
end
|
15
|
+
{
|
16
|
+
'class' => [prefix, suffix].compact.join('_'),
|
17
|
+
'id' => [prefix, suffix, object.id || 'new'].compact.join('_'),
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Haml::Buffer.underscore
|
24
|
+
def underscore(camel_cased_word)
|
25
|
+
word = camel_cased_word.to_s.dup
|
26
|
+
word.gsub!(/::/, '_')
|
27
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
28
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
29
|
+
word.tr!('-', '_')
|
30
|
+
word.downcase!
|
31
|
+
word
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/haml/parser.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'ripper'
|
4
4
|
require 'strscan'
|
5
|
+
require 'haml/error'
|
6
|
+
require 'haml/util'
|
5
7
|
|
6
8
|
module Haml
|
7
9
|
class Parser
|
@@ -95,15 +97,23 @@ module Haml
|
|
95
97
|
METHOD_CALL_PREFIX = 'a('
|
96
98
|
|
97
99
|
def initialize(options)
|
98
|
-
@options =
|
100
|
+
@options = ParserOptions.new(options)
|
99
101
|
# Record the indent levels of "if" statements to validate the subsequent
|
100
102
|
# elsif and else statements are indented at the appropriate level.
|
101
103
|
@script_level_stack = []
|
102
104
|
@template_index = 0
|
103
105
|
@template_tabs = 0
|
106
|
+
# When used in Haml::Engine, which gives options[:generator] to every filter
|
107
|
+
# in the engine, including Haml::Parser, we don't want to throw exceptions.
|
108
|
+
# However, when Haml::Parser is used as a library, we want to throw exceptions.
|
109
|
+
@raise_error = !options.key?(:generator)
|
104
110
|
end
|
105
111
|
|
106
112
|
def call(template)
|
113
|
+
template = Haml::Util.check_haml_encoding(template) do |msg, line|
|
114
|
+
raise Haml::Error.new(msg, line)
|
115
|
+
end
|
116
|
+
|
107
117
|
match = template.rstrip.scan(/(([ \t]+)?(.*?))(?:\Z|\r\n|\r|\n)/m)
|
108
118
|
# discard the last match which is always blank
|
109
119
|
match.pop
|
@@ -152,7 +162,8 @@ module Haml
|
|
152
162
|
@root
|
153
163
|
rescue Haml::Error => e
|
154
164
|
e.backtrace.unshift "#{@options.filename}:#{(e.line ? e.line + 1 : @line.index + 1) + @options.line - 1}"
|
155
|
-
raise
|
165
|
+
raise if @raise_error
|
166
|
+
error_with_lineno(e)
|
156
167
|
end
|
157
168
|
|
158
169
|
def compute_tabs(line)
|
@@ -182,6 +193,16 @@ module Haml
|
|
182
193
|
|
183
194
|
private
|
184
195
|
|
196
|
+
def error_with_lineno(error)
|
197
|
+
return error if error.line
|
198
|
+
|
199
|
+
trace = error.backtrace.first
|
200
|
+
return error unless trace
|
201
|
+
|
202
|
+
line = trace.match(/\d+\z/).to_s.to_i
|
203
|
+
SyntaxError.new(error.message, line)
|
204
|
+
end
|
205
|
+
|
185
206
|
# @private
|
186
207
|
Line = Struct.new(:whitespace, :text, :full, :index, :parser, :eod) do
|
187
208
|
alias_method :eod?, :eod
|
@@ -221,7 +242,7 @@ module Haml
|
|
221
242
|
self[:old] = value
|
222
243
|
end
|
223
244
|
|
224
|
-
# This will be a literal for Haml::
|
245
|
+
# This will be a literal for Haml::HamlBuffer#attributes's last argument, `attributes_hashes`.
|
225
246
|
def to_literal
|
226
247
|
[new, stripped_old].compact.join(', ')
|
227
248
|
end
|
@@ -307,13 +328,13 @@ module Haml
|
|
307
328
|
raise SyntaxError.new(Error.message(:illegal_nesting_plain), @next_line.index)
|
308
329
|
end
|
309
330
|
|
310
|
-
unless contains_interpolation?(line.text)
|
331
|
+
unless Util.contains_interpolation?(line.text)
|
311
332
|
return ParseNode.new(:plain, line.index + 1, :text => line.text)
|
312
333
|
end
|
313
334
|
|
314
335
|
escape_html = @options.escape_html && @options.mime_type != 'text/plain' if escape_html.nil?
|
315
|
-
line.text = unescape_interpolation(line.text
|
316
|
-
script(line, false)
|
336
|
+
line.text = Util.unescape_interpolation(line.text)
|
337
|
+
script(line, false).tap { |n| n.value[:escape_interpolation] = true if escape_html }
|
317
338
|
end
|
318
339
|
|
319
340
|
def script(line, escape_html = nil, preserve = false)
|
@@ -400,7 +421,8 @@ module Haml
|
|
400
421
|
when '='
|
401
422
|
parse = true
|
402
423
|
if value[0] == ?=
|
403
|
-
value = unescape_interpolation(value[1..-1].strip
|
424
|
+
value = Util.unescape_interpolation(value[1..-1].strip)
|
425
|
+
escape_interpolation = true if escape_html
|
404
426
|
escape_html = false
|
405
427
|
end
|
406
428
|
when '&', '!'
|
@@ -408,19 +430,21 @@ module Haml
|
|
408
430
|
parse = true
|
409
431
|
preserve_script = (value[0] == ?~)
|
410
432
|
if value[1] == ?=
|
411
|
-
value = unescape_interpolation(value[2..-1].strip
|
433
|
+
value = Util.unescape_interpolation(value[2..-1].strip)
|
434
|
+
escape_interpolation = true if escape_html
|
412
435
|
escape_html = false
|
413
436
|
else
|
414
437
|
value = value[1..-1].strip
|
415
438
|
end
|
416
|
-
elsif contains_interpolation?(value)
|
417
|
-
value = unescape_interpolation(value
|
439
|
+
elsif Util.contains_interpolation?(value)
|
440
|
+
value = Util.unescape_interpolation(value)
|
441
|
+
escape_interpolation = true if escape_html
|
418
442
|
parse = true
|
419
443
|
escape_html = false
|
420
444
|
end
|
421
445
|
else
|
422
|
-
if contains_interpolation?(value)
|
423
|
-
value = unescape_interpolation(value, escape_html)
|
446
|
+
if Util.contains_interpolation?(value)
|
447
|
+
value = Util.unescape_interpolation(value, escape_html)
|
424
448
|
parse = true
|
425
449
|
escape_html = false
|
426
450
|
end
|
@@ -431,13 +455,13 @@ module Haml
|
|
431
455
|
|
432
456
|
if attributes_hashes[:new]
|
433
457
|
static_attributes, attributes_hash = attributes_hashes[:new]
|
434
|
-
|
458
|
+
AttributeMerger.merge_attributes!(attributes, static_attributes) if static_attributes
|
435
459
|
dynamic_attributes.new = attributes_hash
|
436
460
|
end
|
437
461
|
|
438
462
|
if attributes_hashes[:old]
|
439
463
|
static_attributes = parse_static_hash(attributes_hashes[:old])
|
440
|
-
|
464
|
+
AttributeMerger.merge_attributes!(attributes, static_attributes) if static_attributes
|
441
465
|
dynamic_attributes.old = attributes_hashes[:old] unless static_attributes || @options.suppress_eval
|
442
466
|
end
|
443
467
|
|
@@ -459,7 +483,8 @@ module Haml
|
|
459
483
|
:nuke_inner_whitespace => nuke_inner_whitespace,
|
460
484
|
:nuke_outer_whitespace => nuke_outer_whitespace, :object_ref => object_ref,
|
461
485
|
:escape_html => escape_html, :preserve_tag => preserve_tag,
|
462
|
-
:preserve_script => preserve_script, :parse => parse, :value => line.text
|
486
|
+
:preserve_script => preserve_script, :parse => parse, :value => line.text,
|
487
|
+
:escape_interpolation => escape_interpolation)
|
463
488
|
end
|
464
489
|
|
465
490
|
# Renders a line that creates an XHTML tag and has an implicit div because of
|
@@ -481,9 +506,9 @@ module Haml
|
|
481
506
|
conditional, text = balance(text, ?[, ?]) if text[0] == ?[
|
482
507
|
text.strip!
|
483
508
|
|
484
|
-
if contains_interpolation?(text)
|
509
|
+
if Util.contains_interpolation?(text)
|
485
510
|
parse = true
|
486
|
-
text = unescape_interpolation(text)
|
511
|
+
text = Util.unescape_interpolation(text)
|
487
512
|
else
|
488
513
|
parse = false
|
489
514
|
end
|
@@ -552,7 +577,7 @@ module Haml
|
|
552
577
|
|
553
578
|
alias :close_script :close_silent_script
|
554
579
|
|
555
|
-
# This is a class method so it can be accessed from {Haml::
|
580
|
+
# This is a class method so it can be accessed from {Haml::HamlHelpers}.
|
556
581
|
#
|
557
582
|
# Iterates through the classes and ids supplied through `.`
|
558
583
|
# and `#` syntax, and returns a hash with them as attributes,
|
@@ -576,8 +601,8 @@ module Haml
|
|
576
601
|
attributes
|
577
602
|
end
|
578
603
|
|
579
|
-
# This method doesn't use Haml::
|
580
|
-
# Ideally this logic should be placed in Haml::
|
604
|
+
# This method doesn't use Haml::HamlAttributeParser because currently it depends on Ripper and Rubinius doesn't provide it.
|
605
|
+
# Ideally this logic should be placed in Haml::HamlAttributeParser instead of here and this method should use it.
|
581
606
|
#
|
582
607
|
# @param [String] text - Hash literal or text inside old attributes
|
583
608
|
# @return [Hash,nil] - Return nil if text is not static Hash literal
|
@@ -714,7 +739,7 @@ module Haml
|
|
714
739
|
if type == :static
|
715
740
|
static_attributes[name] = val
|
716
741
|
else
|
717
|
-
dynamic_attributes << "#{inspect_obj(name)} => #{val},"
|
742
|
+
dynamic_attributes << "#{Util.inspect_obj(name)} => #{val},"
|
718
743
|
end
|
719
744
|
end
|
720
745
|
dynamic_attributes << "}"
|
@@ -749,7 +774,7 @@ module Haml
|
|
749
774
|
|
750
775
|
return name, [:static, content.first[1]] if content.size == 1
|
751
776
|
return name, [:dynamic,
|
752
|
-
%!"#{content.each_with_object(''.dup) {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
|
777
|
+
%!"#{content.each_with_object(''.dup) {|(t, v), s| s << (t == :str ? Util.inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
|
753
778
|
end
|
754
779
|
|
755
780
|
def next_line
|
@@ -852,5 +877,115 @@ module Haml
|
|
852
877
|
def flat?
|
853
878
|
@flat
|
854
879
|
end
|
880
|
+
|
881
|
+
class << AttributeMerger = Object.new
|
882
|
+
# Merges two attribute hashes.
|
883
|
+
# This is the same as `to.merge!(from)`,
|
884
|
+
# except that it merges id, class, and data attributes.
|
885
|
+
#
|
886
|
+
# ids are concatenated with `"_"`,
|
887
|
+
# and classes are concatenated with `" "`.
|
888
|
+
# data hashes are simply merged.
|
889
|
+
#
|
890
|
+
# Destructively modifies `to`.
|
891
|
+
#
|
892
|
+
# @param to [{String => String,Hash}] The attribute hash to merge into
|
893
|
+
# @param from [{String => Object}] The attribute hash to merge from
|
894
|
+
# @return [{String => String,Hash}] `to`, after being merged
|
895
|
+
def merge_attributes!(to, from)
|
896
|
+
from.keys.each do |key|
|
897
|
+
to[key] = merge_value(key, to[key], from[key])
|
898
|
+
end
|
899
|
+
to
|
900
|
+
end
|
901
|
+
|
902
|
+
private
|
903
|
+
|
904
|
+
# @return [String, nil]
|
905
|
+
def filter_and_join(value, separator)
|
906
|
+
return '' if (value.respond_to?(:empty?) && value.empty?)
|
907
|
+
|
908
|
+
if value.is_a?(Array)
|
909
|
+
value = value.flatten
|
910
|
+
value.map! {|item| item ? item.to_s : nil}
|
911
|
+
value.compact!
|
912
|
+
value = value.join(separator)
|
913
|
+
else
|
914
|
+
value = value ? value.to_s : nil
|
915
|
+
end
|
916
|
+
!value.nil? && !value.empty? && value
|
917
|
+
end
|
918
|
+
|
919
|
+
# Merge a couple of values to one attribute value. No destructive operation.
|
920
|
+
#
|
921
|
+
# @param to [String,Hash,nil]
|
922
|
+
# @param from [Object]
|
923
|
+
# @return [String,Hash]
|
924
|
+
def merge_value(key, to, from)
|
925
|
+
if from.kind_of?(Hash) || to.kind_of?(Hash)
|
926
|
+
from = { nil => from } if !from.is_a?(Hash)
|
927
|
+
to = { nil => to } if !to.is_a?(Hash)
|
928
|
+
to.merge(from)
|
929
|
+
elsif key == 'id'
|
930
|
+
merged_id = filter_and_join(from, '_')
|
931
|
+
if to && merged_id
|
932
|
+
merged_id = "#{to}_#{merged_id}"
|
933
|
+
elsif to || merged_id
|
934
|
+
merged_id ||= to
|
935
|
+
end
|
936
|
+
merged_id
|
937
|
+
elsif key == 'class'
|
938
|
+
merged_class = filter_and_join(from, ' ')
|
939
|
+
if to && merged_class
|
940
|
+
merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
|
941
|
+
elsif to || merged_class
|
942
|
+
merged_class ||= to
|
943
|
+
end
|
944
|
+
merged_class
|
945
|
+
else
|
946
|
+
from
|
947
|
+
end
|
948
|
+
end
|
949
|
+
end
|
950
|
+
private_constant :AttributeMerger
|
951
|
+
|
952
|
+
class ParserOptions
|
953
|
+
# A list of options that are actually used in the parser
|
954
|
+
AVAILABLE_OPTIONS = %i[
|
955
|
+
autoclose
|
956
|
+
escape_html
|
957
|
+
filename
|
958
|
+
line
|
959
|
+
mime_type
|
960
|
+
preserve
|
961
|
+
remove_whitespace
|
962
|
+
suppress_eval
|
963
|
+
].each do |option|
|
964
|
+
attr_reader option
|
965
|
+
end
|
966
|
+
|
967
|
+
DEFAULTS = {
|
968
|
+
autoclose: %w(area base basefont br col command embed frame
|
969
|
+
hr img input isindex keygen link menuitem meta
|
970
|
+
param source track wbr),
|
971
|
+
escape_html: false,
|
972
|
+
filename: '(haml)',
|
973
|
+
line: 1,
|
974
|
+
mime_type: 'text/html',
|
975
|
+
preserve: %w(textarea pre code),
|
976
|
+
remove_whitespace: false,
|
977
|
+
suppress_eval: false,
|
978
|
+
}
|
979
|
+
|
980
|
+
def initialize(values = {})
|
981
|
+
DEFAULTS.each {|k, v| instance_variable_set :"@#{k}", v}
|
982
|
+
AVAILABLE_OPTIONS.each do |key|
|
983
|
+
if values.key?(key)
|
984
|
+
instance_variable_set :"@#{key}", values[key]
|
985
|
+
end
|
986
|
+
end
|
987
|
+
end
|
988
|
+
end
|
989
|
+
private_constant :ParserOptions
|
855
990
|
end
|
856
991
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'haml/helpers'
|
3
|
+
|
4
|
+
# There are only helpers that depend on ActionView internals.
|
5
|
+
module Haml
|
6
|
+
module RailsHelpers
|
7
|
+
include Helpers
|
8
|
+
extend self
|
9
|
+
|
10
|
+
DEFAULT_PRESERVE_TAGS = %w[textarea pre code].freeze
|
11
|
+
|
12
|
+
def find_and_preserve(input = nil, tags = DEFAULT_PRESERVE_TAGS, &block)
|
13
|
+
return find_and_preserve(capture_haml(&block), input || tags) if block
|
14
|
+
|
15
|
+
tags = tags.each_with_object('') do |t, s|
|
16
|
+
s << '|' unless s.empty?
|
17
|
+
s << Regexp.escape(t)
|
18
|
+
end
|
19
|
+
|
20
|
+
re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
|
21
|
+
input.to_s.gsub(re) do |s|
|
22
|
+
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
23
|
+
"<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def preserve(input = nil, &block)
|
28
|
+
return preserve(capture_haml(&block)) if block
|
29
|
+
super.html_safe
|
30
|
+
end
|
31
|
+
|
32
|
+
def surround(front, back = front, &block)
|
33
|
+
output = capture_haml(&block)
|
34
|
+
front = escape_once(front) unless front.html_safe?
|
35
|
+
back = escape_once(back) unless back.html_safe?
|
36
|
+
"#{front}#{output.chomp}#{back}\n".html_safe
|
37
|
+
end
|
38
|
+
|
39
|
+
def precede(str, &block)
|
40
|
+
str = escape_once(str) unless str.html_safe?
|
41
|
+
"#{str}#{capture_haml(&block).chomp}\n".html_safe
|
42
|
+
end
|
43
|
+
|
44
|
+
def succeed(str, &block)
|
45
|
+
str = escape_once(str) unless str.html_safe?
|
46
|
+
"#{capture_haml(&block).chomp}#{str}\n".html_safe
|
47
|
+
end
|
48
|
+
|
49
|
+
def capture_haml(*args, &block)
|
50
|
+
capture(*args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'temple'
|
3
|
+
require 'haml/engine'
|
4
|
+
require 'haml/rails_helpers'
|
5
|
+
require 'haml/util'
|
6
|
+
|
7
|
+
module Haml
|
8
|
+
class RailsTemplate
|
9
|
+
# Compatible with: https://github.com/judofyr/temple/blob/v0.7.7/lib/temple/mixins/options.rb#L15-L24
|
10
|
+
class << self
|
11
|
+
def options
|
12
|
+
@options ||= {
|
13
|
+
generator: Temple::Generators::RailsOutputBuffer,
|
14
|
+
use_html_safe: true,
|
15
|
+
streaming: true,
|
16
|
+
buffer_class: 'ActionView::OutputBuffer',
|
17
|
+
disable_capture: true,
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_options(opts)
|
22
|
+
options.update(opts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(template, source = nil)
|
27
|
+
source ||= template.source
|
28
|
+
options = RailsTemplate.options
|
29
|
+
|
30
|
+
# https://github.com/haml/haml/blob/4.0.7/lib/haml/template/plugin.rb#L19-L20
|
31
|
+
# https://github.com/haml/haml/blob/4.0.7/lib/haml/options.rb#L228
|
32
|
+
if template.respond_to?(:type) && template.type == 'text/xml'
|
33
|
+
options = options.merge(format: :xhtml)
|
34
|
+
end
|
35
|
+
|
36
|
+
if ActionView::Base.try(:annotate_rendered_view_with_filenames) && template.format == :html
|
37
|
+
options = options.merge(
|
38
|
+
preamble: "<!-- BEGIN #{template.short_identifier} -->\n",
|
39
|
+
postamble: "<!-- END #{template.short_identifier} -->\n",
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
Engine.new(options).call(source)
|
44
|
+
end
|
45
|
+
|
46
|
+
def supports_streaming?
|
47
|
+
RailsTemplate.options[:streaming]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
ActionView::Template.register_template_handler(:haml, RailsTemplate.new)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Haml extends Haml::Helpers in ActionView each time.
|
54
|
+
# It costs much, so Haml includes a compatible module at first.
|
55
|
+
ActiveSupport.on_load(:action_view) do
|
56
|
+
include Haml::RailsHelpers
|
57
|
+
end
|
data/lib/haml/railtie.rb
CHANGED
@@ -1,53 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'haml/template/options'
|
4
|
-
|
5
|
-
# check for a compatible Rails version when Haml is loaded
|
6
|
-
if (activesupport_spec = Gem.loaded_specs['activesupport'])
|
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")
|
9
|
-
end
|
10
|
-
end
|
2
|
+
require 'rails'
|
11
3
|
|
12
4
|
module Haml
|
13
|
-
module Filters
|
14
|
-
module RailsErb
|
15
|
-
extend Plain
|
16
|
-
extend TiltFilter
|
17
|
-
extend PrecompiledTiltFilter
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
5
|
class Railtie < ::Rails::Railtie
|
22
|
-
initializer :haml do |app|
|
23
|
-
|
24
|
-
require "haml/template"
|
25
|
-
|
26
|
-
if defined?(::Sass::Rails::SassTemplate) && app.config.assets.enabled
|
27
|
-
require "haml/sass_rails_filter"
|
28
|
-
end
|
29
|
-
|
30
|
-
# Any object under ActionView::Template will be defined as the root constant with the same
|
31
|
-
# name if it exists. If Erubi is loaded at all, ActionView::Template::Handlers::ERB::Erubi
|
32
|
-
# will turn out to be a reference to the ::Erubi module.
|
33
|
-
# In Rails 4.2, calling const_defined? results in odd exceptions, which seems to be
|
34
|
-
# solved by looking for ::Erubi first.
|
35
|
-
# However, in JRuby, the const_defined? finds it anyway, so we must make sure that it's
|
36
|
-
# not just a reference to ::Erubi.
|
37
|
-
if defined?(::Erubi) && (::ActionView::Template::Handlers::ERB.const_get('Erubi') != ::Erubi)
|
38
|
-
require "haml/helpers/safe_erubi_template"
|
39
|
-
Haml::Filters::RailsErb.template_class = Haml::SafeErubiTemplate
|
40
|
-
else
|
41
|
-
require "haml/helpers/safe_erubis_template"
|
42
|
-
Haml::Filters::RailsErb.template_class = Haml::SafeErubisTemplate
|
43
|
-
end
|
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
|
50
|
-
end
|
6
|
+
initializer :haml, before: :load_config_initializers do |app|
|
7
|
+
require 'haml/rails_template'
|
51
8
|
end
|
52
9
|
end
|
53
10
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ripper'
|
3
|
+
|
4
|
+
module Haml
|
5
|
+
class RubyExpression < Ripper
|
6
|
+
class ParseError < StandardError; end
|
7
|
+
|
8
|
+
def self.syntax_error?(code)
|
9
|
+
self.new(code).parse
|
10
|
+
false
|
11
|
+
rescue ParseError
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.string_literal?(code)
|
16
|
+
return false if syntax_error?(code)
|
17
|
+
|
18
|
+
type, instructions = Ripper.sexp(code)
|
19
|
+
return false if type != :program
|
20
|
+
return false if instructions.size > 1
|
21
|
+
|
22
|
+
type, _ = instructions.first
|
23
|
+
type == :string_literal
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def on_parse_error(*)
|
29
|
+
raise ParseError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|