haml 5.2.2 → 6.0.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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +13 -9
- data/.gitignore +16 -16
- data/CHANGELOG.md +14 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +13 -19
- data/Rakefile +94 -93
- 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 +135 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +86 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +126 -0
- data/lib/haml/compiler/comment_compiler.rb +39 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +116 -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 +42 -227
- data/lib/haml/error.rb +5 -4
- data/lib/haml/escapable.rb +6 -70
- 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 +49 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escapable.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 +30 -0
- data/lib/haml/parser.rb +157 -22
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -45
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +17 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml.rb +8 -20
- metadata +205 -52
- data/.gitmodules +0 -3
- data/.yardopts +0 -22
- 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/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,30 @@
|
|
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 = underscore(object.class)
|
10
|
+
{
|
11
|
+
'class' => [prefix, suffix].compact.join('_'),
|
12
|
+
'id' => [prefix, suffix, object.id || 'new'].compact.join('_'),
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Haml::Buffer.underscore
|
19
|
+
def underscore(camel_cased_word)
|
20
|
+
word = camel_cased_word.to_s.dup
|
21
|
+
word.gsub!(/::/, '_')
|
22
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
23
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
24
|
+
word.tr!('-', '_')
|
25
|
+
word.downcase!
|
26
|
+
word
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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,51 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'haml/helpers'
|
3
|
+
|
4
|
+
# Currently this Haml::Helpers depends on
|
5
|
+
# ActionView internal implementation. (not desired)
|
6
|
+
module Haml
|
7
|
+
module RailsHelpers
|
8
|
+
include Helpers
|
9
|
+
extend self
|
10
|
+
|
11
|
+
DEFAULT_PRESERVE_TAGS = %w[textarea pre code].freeze
|
12
|
+
|
13
|
+
def find_and_preserve(input = nil, tags = DEFAULT_PRESERVE_TAGS, &block)
|
14
|
+
return find_and_preserve(capture_haml(&block), input || tags) if block
|
15
|
+
|
16
|
+
tags = tags.each_with_object('') do |t, s|
|
17
|
+
s << '|' unless s.empty?
|
18
|
+
s << Regexp.escape(t)
|
19
|
+
end
|
20
|
+
|
21
|
+
re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
|
22
|
+
input.to_s.gsub(re) do |s|
|
23
|
+
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
24
|
+
"<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def preserve(input = nil, &block)
|
29
|
+
return preserve(capture_haml(&block)) if block
|
30
|
+
super.html_safe
|
31
|
+
end
|
32
|
+
|
33
|
+
def surround(front, back = front, &block)
|
34
|
+
output = capture_haml(&block)
|
35
|
+
|
36
|
+
"#{escape_once(front)}#{output.chomp}#{escape_once(back)}\n".html_safe
|
37
|
+
end
|
38
|
+
|
39
|
+
def precede(str, &block)
|
40
|
+
"#{escape_once(str)}#{capture_haml(&block).chomp}\n".html_safe
|
41
|
+
end
|
42
|
+
|
43
|
+
def succeed(str, &block)
|
44
|
+
"#{capture_haml(&block).chomp}#{escape_once(str)}\n".html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
def capture_haml(*args, &block)
|
48
|
+
capture(*args, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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
|
+
ActionView::Base.send :include, Haml::RailsHelpers
|
data/lib/haml/railtie.rb
CHANGED
@@ -1,53 +1,15 @@
|
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
6
|
+
initializer :haml, before: :load_config_initializers do |app|
|
7
|
+
# Load haml/plugin first to override if available
|
8
|
+
begin
|
9
|
+
require 'haml/plugin'
|
10
|
+
rescue LoadError
|
50
11
|
end
|
12
|
+
require 'haml/rails_template'
|
51
13
|
end
|
52
14
|
end
|
53
15
|
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ripper'
|
3
|
+
require 'haml/ruby_expression'
|
4
|
+
|
5
|
+
module Haml
|
6
|
+
module StringSplitter
|
7
|
+
# `code` param must be valid string literal
|
8
|
+
def self.compile(code)
|
9
|
+
unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
|
10
|
+
return [[:dynamic, code]]
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Temple::Filters::StringSplitter.compile(code)
|
15
|
+
rescue Temple::FilterError => e
|
16
|
+
raise Haml::InternalError.new(e.message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/haml/template.rb
CHANGED
@@ -1,39 +1,20 @@
|
|
1
|
-
# frozen_string_literal:
|
2
|
-
|
3
|
-
require 'haml/
|
4
|
-
|
5
|
-
ActiveSupport.on_load(:action_view) do
|
6
|
-
require 'haml/helpers/action_view_mods'
|
7
|
-
require 'haml/helpers/action_view_extensions'
|
8
|
-
end
|
9
|
-
else
|
10
|
-
require 'haml/helpers/action_view_mods'
|
11
|
-
require 'haml/helpers/action_view_extensions'
|
12
|
-
end
|
13
|
-
require 'haml/helpers/xss_mods'
|
14
|
-
require 'haml/helpers/action_view_xss_mods'
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'temple'
|
3
|
+
require 'haml/engine'
|
4
|
+
require 'haml/helpers'
|
15
5
|
|
16
6
|
module Haml
|
17
|
-
|
18
|
-
|
19
|
-
|
7
|
+
Template = Temple::Templates::Tilt.create(
|
8
|
+
Haml::Engine,
|
9
|
+
register_as: [:haml, :haml],
|
10
|
+
)
|
11
|
+
|
12
|
+
module TemplateExtension
|
13
|
+
# Activate Haml::Helpers for tilt templates.
|
14
|
+
# https://github.com/judofyr/temple/blob/v0.7.6/lib/temple/mixins/template.rb#L7-L11
|
15
|
+
def compile(*)
|
16
|
+
"extend Haml::Helpers; #{super}"
|
20
17
|
end
|
21
|
-
alias_method :precompiled_method_return_value_without_haml_xss, :precompiled_method_return_value
|
22
|
-
alias_method :precompiled_method_return_value, :precompiled_method_return_value_with_haml_xss
|
23
18
|
end
|
24
|
-
|
25
|
-
module Helpers
|
26
|
-
include Haml::Helpers::XssMods
|
27
|
-
end
|
28
|
-
|
29
|
-
module Util
|
30
|
-
undef :rails_xss_safe? if defined? rails_xss_safe?
|
31
|
-
def rails_xss_safe?; true; end
|
32
|
-
end
|
33
|
-
|
19
|
+
Template.send(:extend, TemplateExtension)
|
34
20
|
end
|
35
|
-
|
36
|
-
|
37
|
-
Haml::Template.options[:escape_html] = true
|
38
|
-
|
39
|
-
require 'haml/plugin'
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
module Haml
|
4
3
|
# A module to count lines of expected code. This would be faster than actual code generation
|
5
4
|
# and counting newlines in it.
|
@@ -22,6 +21,8 @@ module Haml
|
|
22
21
|
end.reduce(:+)
|
23
22
|
when :escape
|
24
23
|
count_lines(args[1])
|
24
|
+
when :newline
|
25
|
+
1
|
25
26
|
else
|
26
27
|
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
|
27
28
|
end
|