haml 5.2.1 → 6.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +40 -0
  4. data/.gitignore +16 -16
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +18 -11
  7. data/MIT-LICENSE +1 -1
  8. data/REFERENCE.md +10 -3
  9. data/Rakefile +94 -93
  10. data/bin/bench +66 -0
  11. data/bin/console +11 -0
  12. data/bin/ruby +3 -0
  13. data/bin/setup +7 -0
  14. data/bin/stackprof +27 -0
  15. data/bin/test +24 -0
  16. data/exe/haml +6 -0
  17. data/ext/haml/extconf.rb +10 -0
  18. data/ext/haml/haml.c +537 -0
  19. data/ext/haml/hescape.c +108 -0
  20. data/ext/haml/hescape.h +20 -0
  21. data/haml.gemspec +39 -37
  22. data/lib/haml/ambles.rb +20 -0
  23. data/lib/haml/attribute_builder.rb +140 -129
  24. data/lib/haml/attribute_compiler.rb +85 -192
  25. data/lib/haml/attribute_parser.rb +86 -126
  26. data/lib/haml/cli.rb +154 -0
  27. data/lib/haml/compiler/children_compiler.rb +126 -0
  28. data/lib/haml/compiler/comment_compiler.rb +39 -0
  29. data/lib/haml/compiler/doctype_compiler.rb +46 -0
  30. data/lib/haml/compiler/script_compiler.rb +116 -0
  31. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  32. data/lib/haml/compiler/tag_compiler.rb +76 -0
  33. data/lib/haml/compiler.rb +63 -296
  34. data/lib/haml/dynamic_merger.rb +67 -0
  35. data/lib/haml/engine.rb +42 -227
  36. data/lib/haml/error.rb +5 -4
  37. data/lib/haml/escapable.rb +6 -70
  38. data/lib/haml/filters/base.rb +12 -0
  39. data/lib/haml/filters/cdata.rb +20 -0
  40. data/lib/haml/filters/coffee.rb +17 -0
  41. data/lib/haml/filters/css.rb +33 -0
  42. data/lib/haml/filters/erb.rb +10 -0
  43. data/lib/haml/filters/escaped.rb +22 -0
  44. data/lib/haml/filters/javascript.rb +33 -0
  45. data/lib/haml/filters/less.rb +20 -0
  46. data/lib/haml/filters/markdown.rb +11 -0
  47. data/lib/haml/filters/plain.rb +29 -0
  48. data/lib/haml/filters/preserve.rb +22 -0
  49. data/lib/haml/filters/ruby.rb +10 -0
  50. data/lib/haml/filters/sass.rb +15 -0
  51. data/lib/haml/filters/scss.rb +15 -0
  52. data/lib/haml/filters/text_base.rb +25 -0
  53. data/lib/haml/filters/tilt_base.rb +49 -0
  54. data/lib/haml/filters.rb +54 -378
  55. data/lib/haml/force_escapable.rb +29 -0
  56. data/lib/haml/helpers.rb +3 -697
  57. data/lib/haml/html.rb +22 -0
  58. data/lib/haml/identity.rb +13 -0
  59. data/lib/haml/object_ref.rb +30 -0
  60. data/lib/haml/parser.rb +157 -22
  61. data/lib/haml/rails_helpers.rb +51 -0
  62. data/lib/haml/rails_template.rb +55 -0
  63. data/lib/haml/railtie.rb +7 -40
  64. data/lib/haml/ruby_expression.rb +32 -0
  65. data/lib/haml/string_splitter.rb +20 -0
  66. data/lib/haml/template.rb +15 -34
  67. data/lib/haml/temple_line_counter.rb +2 -1
  68. data/lib/haml/util.rb +17 -15
  69. data/lib/haml/version.rb +1 -2
  70. data/lib/haml.rb +8 -20
  71. metadata +211 -58
  72. data/.gitmodules +0 -3
  73. data/.travis.yml +0 -111
  74. data/.yardopts +0 -22
  75. data/TODO +0 -24
  76. data/benchmark.rb +0 -70
  77. data/bin/haml +0 -9
  78. data/lib/haml/.gitattributes +0 -1
  79. data/lib/haml/buffer.rb +0 -238
  80. data/lib/haml/exec.rb +0 -347
  81. data/lib/haml/generator.rb +0 -42
  82. data/lib/haml/helpers/action_view_extensions.rb +0 -60
  83. data/lib/haml/helpers/action_view_mods.rb +0 -132
  84. data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  85. data/lib/haml/helpers/safe_erubi_template.rb +0 -20
  86. data/lib/haml/helpers/safe_erubis_template.rb +0 -33
  87. data/lib/haml/helpers/xss_mods.rb +0 -114
  88. data/lib/haml/options.rb +0 -273
  89. data/lib/haml/plugin.rb +0 -37
  90. data/lib/haml/sass_rails_filter.rb +0 -47
  91. data/lib/haml/template/options.rb +0 -27
  92. data/lib/haml/temple_engine.rb +0 -123
  93. data/yard/default/.gitignore +0 -1
  94. data/yard/default/fulldoc/html/css/common.sass +0 -15
  95. 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,13 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Identity
4
+ def initialize
5
+ @unique_id = 0
6
+ end
7
+
8
+ def generate
9
+ @unique_id += 1
10
+ "_haml_compiler#{@unique_id}"
11
+ end
12
+ end
13
+ 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 = Options.wrap(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::Buffer#attributes's last argument, `attributes_hashes`.
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, escape_html)
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, escape_html)
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, escape_html)
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, escape_html)
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
- AttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
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
- AttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
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::Helpers}.
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::AttributeParser because currently it depends on Ripper and Rubinius doesn't provide it.
580
- # Ideally this logic should be placed in Haml::AttributeParser instead of here and this method should use it.
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,48 +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
- ActiveSupport.on_load(:action_view) do
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 }
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
45
11
  end
12
+ require 'haml/rails_template'
46
13
  end
47
14
  end
48
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: true
2
-
3
- require 'haml/template/options'
4
- if defined?(ActiveSupport)
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
- class TempleEngine
18
- def precompiled_method_return_value_with_haml_xss
19
- "::Haml::Util.html_safe(#{precompiled_method_return_value_without_haml_xss})"
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