haml 4.0.7 → 5.2.2

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.
Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +36 -0
  3. data/.gitignore +19 -0
  4. data/.gitmodules +3 -0
  5. data/.yardopts +2 -3
  6. data/CHANGELOG.md +146 -4
  7. data/FAQ.md +4 -14
  8. data/Gemfile +16 -0
  9. data/MIT-LICENSE +2 -2
  10. data/README.md +90 -47
  11. data/REFERENCE.md +160 -74
  12. data/Rakefile +44 -63
  13. data/TODO +24 -0
  14. data/benchmark.rb +70 -0
  15. data/haml.gemspec +45 -0
  16. data/lib/haml/.gitattributes +1 -0
  17. data/lib/haml/attribute_builder.rb +219 -0
  18. data/lib/haml/attribute_compiler.rb +237 -0
  19. data/lib/haml/attribute_parser.rb +150 -0
  20. data/lib/haml/buffer.rb +12 -175
  21. data/lib/haml/compiler.rb +110 -320
  22. data/lib/haml/engine.rb +34 -41
  23. data/lib/haml/error.rb +28 -24
  24. data/lib/haml/escapable.rb +77 -0
  25. data/lib/haml/exec.rb +38 -20
  26. data/lib/haml/filters.rb +22 -27
  27. data/lib/haml/generator.rb +42 -0
  28. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  29. data/lib/haml/helpers/action_view_mods.rb +45 -60
  30. data/lib/haml/helpers/action_view_xss_mods.rb +2 -0
  31. data/lib/haml/helpers/safe_erubi_template.rb +20 -0
  32. data/lib/haml/helpers/safe_erubis_template.rb +5 -1
  33. data/lib/haml/helpers/xss_mods.rb +23 -13
  34. data/lib/haml/helpers.rb +134 -89
  35. data/lib/haml/options.rb +63 -69
  36. data/lib/haml/parser.rb +319 -227
  37. data/lib/haml/plugin.rb +54 -0
  38. data/lib/haml/railtie.rb +43 -12
  39. data/lib/haml/sass_rails_filter.rb +18 -4
  40. data/lib/haml/template/options.rb +13 -2
  41. data/lib/haml/template.rb +13 -6
  42. data/lib/haml/temple_engine.rb +124 -0
  43. data/lib/haml/temple_line_counter.rb +30 -0
  44. data/lib/haml/util.rb +83 -202
  45. data/lib/haml/version.rb +3 -1
  46. data/lib/haml.rb +2 -0
  47. data/yard/default/.gitignore +1 -0
  48. data/yard/default/fulldoc/html/css/common.sass +15 -0
  49. data/yard/default/layout/html/footer.erb +12 -0
  50. metadata +73 -115
  51. data/lib/haml/template/plugin.rb +0 -41
  52. data/test/engine_test.rb +0 -2013
  53. data/test/erb/_av_partial_1.erb +0 -12
  54. data/test/erb/_av_partial_2.erb +0 -8
  55. data/test/erb/action_view.erb +0 -62
  56. data/test/erb/standard.erb +0 -55
  57. data/test/filters_test.rb +0 -254
  58. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  59. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  60. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  61. data/test/gemfiles/Gemfile.rails-4.0.x +0 -5
  62. data/test/haml-spec/LICENSE +0 -14
  63. data/test/haml-spec/README.md +0 -106
  64. data/test/haml-spec/lua_haml_spec.lua +0 -38
  65. data/test/haml-spec/perl_haml_test.pl +0 -81
  66. data/test/haml-spec/ruby_haml_test.rb +0 -23
  67. data/test/haml-spec/tests.json +0 -660
  68. data/test/helper_test.rb +0 -583
  69. data/test/markaby/standard.mab +0 -52
  70. data/test/mocks/article.rb +0 -6
  71. data/test/parser_test.rb +0 -105
  72. data/test/results/content_for_layout.xhtml +0 -12
  73. data/test/results/eval_suppressed.xhtml +0 -9
  74. data/test/results/helpers.xhtml +0 -70
  75. data/test/results/helpful.xhtml +0 -10
  76. data/test/results/just_stuff.xhtml +0 -70
  77. data/test/results/list.xhtml +0 -12
  78. data/test/results/nuke_inner_whitespace.xhtml +0 -40
  79. data/test/results/nuke_outer_whitespace.xhtml +0 -148
  80. data/test/results/original_engine.xhtml +0 -20
  81. data/test/results/partial_layout.xhtml +0 -5
  82. data/test/results/partial_layout_erb.xhtml +0 -5
  83. data/test/results/partials.xhtml +0 -21
  84. data/test/results/render_layout.xhtml +0 -3
  85. data/test/results/silent_script.xhtml +0 -74
  86. data/test/results/standard.xhtml +0 -162
  87. data/test/results/tag_parsing.xhtml +0 -23
  88. data/test/results/very_basic.xhtml +0 -5
  89. data/test/results/whitespace_handling.xhtml +0 -90
  90. data/test/template_test.rb +0 -354
  91. data/test/templates/_av_partial_1.haml +0 -9
  92. data/test/templates/_av_partial_1_ugly.haml +0 -9
  93. data/test/templates/_av_partial_2.haml +0 -5
  94. data/test/templates/_av_partial_2_ugly.haml +0 -5
  95. data/test/templates/_layout.erb +0 -3
  96. data/test/templates/_layout_for_partial.haml +0 -3
  97. data/test/templates/_partial.haml +0 -8
  98. data/test/templates/_text_area.haml +0 -3
  99. data/test/templates/_text_area_helper.html.haml +0 -4
  100. data/test/templates/action_view.haml +0 -47
  101. data/test/templates/action_view_ugly.haml +0 -47
  102. data/test/templates/breakage.haml +0 -8
  103. data/test/templates/content_for_layout.haml +0 -8
  104. data/test/templates/eval_suppressed.haml +0 -11
  105. data/test/templates/helpers.haml +0 -55
  106. data/test/templates/helpful.haml +0 -11
  107. data/test/templates/just_stuff.haml +0 -85
  108. data/test/templates/list.haml +0 -12
  109. data/test/templates/nuke_inner_whitespace.haml +0 -32
  110. data/test/templates/nuke_outer_whitespace.haml +0 -144
  111. data/test/templates/original_engine.haml +0 -17
  112. data/test/templates/partial_layout.haml +0 -3
  113. data/test/templates/partial_layout_erb.erb +0 -4
  114. data/test/templates/partialize.haml +0 -1
  115. data/test/templates/partials.haml +0 -12
  116. data/test/templates/render_layout.haml +0 -2
  117. data/test/templates/silent_script.haml +0 -45
  118. data/test/templates/standard.haml +0 -43
  119. data/test/templates/standard_ugly.haml +0 -43
  120. data/test/templates/tag_parsing.haml +0 -21
  121. data/test/templates/very_basic.haml +0 -4
  122. data/test/templates/whitespace_handling.haml +0 -87
  123. data/test/test_helper.rb +0 -81
  124. data/test/util_test.rb +0 -63
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Haml
4
+
5
+ # This module makes Haml work with Rails using the template handler API.
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
+
12
+ def handles_encoding?; true; end
13
+
14
+ def compile(template, source)
15
+ options = Haml::Template.options.dup
16
+ if template.respond_to?(:type)
17
+ options[:mime_type] = template.type
18
+ elsif template.respond_to? :mime_type
19
+ options[:mime_type] = template.mime_type
20
+ end
21
+ options[:filename] = template.identifier
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(
34
+ [],
35
+ after_preamble: preamble,
36
+ before_postamble: postamble
37
+ )
38
+ end
39
+
40
+ def self.call(template, source = nil)
41
+ source ||= template.source
42
+
43
+ new.compile(template, source)
44
+ end
45
+
46
+ def cache_fragment(block, name = {}, options = nil)
47
+ @view.fragment_for(block, name, options) do
48
+ eval("_hamlout.buffer", block.binding)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ActionView::Template.register_template_handler(:haml, Haml::Plugin)
data/lib/haml/railtie.rb CHANGED
@@ -1,22 +1,53 @@
1
- if defined?(ActiveSupport)
2
- require 'haml/template/options'
3
- ActiveSupport.on_load(:before_initialize) do
4
- ActiveSupport.on_load(:action_view) do
5
- require "haml/template"
6
- end
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")
7
9
  end
8
10
  end
9
11
 
10
12
  module Haml
13
+ module Filters
14
+ module RailsErb
15
+ extend Plain
16
+ extend TiltFilter
17
+ extend PrecompiledTiltFilter
18
+ end
19
+ end
20
+
11
21
  class Railtie < ::Rails::Railtie
12
22
  initializer :haml do |app|
13
- require "haml/template"
14
- if defined?(::Sass::Rails::SassTemplate) && app.config.assets.enabled
15
- require "haml/sass_rails_filter"
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 }
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
16
50
  end
17
51
  end
18
52
  end
19
53
  end
20
-
21
- require "haml/helpers/safe_erubis_template"
22
- Haml::Filters::Erb.template_class = Haml::SafeErubisTemplate
@@ -1,11 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Haml
2
4
  module Filters
3
5
  # This is an extension of Sass::Rails's SassTemplate class that allows
4
6
  # Rails's asset helpers to be used inside Haml Sass filter.
5
7
  class SassRailsTemplate < ::Sass::Rails::SassTemplate
6
- def render(scope=Object.new, locals={}, &block)
7
- scope = ::Rails.application.assets.context_class.new(::Rails.application.assets, "/", "/")
8
- super
8
+ if Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new('3.0.0')
9
+ def render(scope=Object.new, locals={}, &block)
10
+ environment = ::Sprockets::Railtie.build_environment(::Rails.application)
11
+ scope = environment.context_class.new(
12
+ environment: environment,
13
+ filename: "/",
14
+ metadata: {}
15
+ )
16
+ super
17
+ end
18
+ else
19
+ def render(scope=Object.new, locals={}, &block)
20
+ scope = ::Rails.application.assets.context_class.new(::Rails.application.assets, "/", "/")
21
+ super
22
+ end
9
23
  end
10
24
 
11
25
  def sass_options(scope)
@@ -30,4 +44,4 @@ module Haml
30
44
  register_tilt_filter "Sass", :extend => "Css", :template_class => SassRailsTemplate
31
45
  register_tilt_filter "Scss", :extend => "Css", :template_class => ScssRailsTemplate
32
46
  end
33
- end
47
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # We keep options in its own self-contained file
2
4
  # so that we can load it independently in Rails 3,
3
5
  # where the full template stuff is lazy-loaded.
@@ -6,11 +8,20 @@ module Haml
6
8
  module Template
7
9
  extend self
8
10
 
9
- @options = {}
11
+ class Options < Hash
12
+ def []=(key, value)
13
+ super
14
+ if Haml::Options.buffer_defaults.key?(key)
15
+ Haml::Options.buffer_defaults[key] = value
16
+ end
17
+ end
18
+ end
19
+
20
+ @options = ::Haml::Template::Options.new
10
21
  # The options hash for Haml when used within Rails.
11
22
  # See {file:REFERENCE.md#options the Haml options documentation}.
12
23
  #
13
- # @return [{Symbol => Object}]
24
+ # @return [Haml::Template::Options<Symbol => Object>]
14
25
  attr_accessor :options
15
26
  end
16
27
  end
data/lib/haml/template.rb CHANGED
@@ -1,12 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'haml/template/options'
2
- require 'haml/engine'
3
- require 'haml/helpers/action_view_mods'
4
- require 'haml/helpers/action_view_extensions'
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
5
13
  require 'haml/helpers/xss_mods'
6
14
  require 'haml/helpers/action_view_xss_mods'
7
15
 
8
16
  module Haml
9
- class Compiler
17
+ class TempleEngine
10
18
  def precompiled_method_return_value_with_haml_xss
11
19
  "::Haml::Util.html_safe(#{precompiled_method_return_value_without_haml_xss})"
12
20
  end
@@ -26,7 +34,6 @@ module Haml
26
34
  end
27
35
 
28
36
 
29
- Haml::Template.options[:ugly] = defined?(Rails) ? !Rails.env.development? : true
30
37
  Haml::Template.options[:escape_html] = true
31
38
 
32
- require 'haml/template/plugin'
39
+ require 'haml/plugin'
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temple'
4
+ require 'haml/escapable'
5
+ require 'haml/generator'
6
+
7
+ module Haml
8
+ class TempleEngine < Temple::Engine
9
+ define_options(
10
+ attr_wrapper: "'",
11
+ autoclose: %w(area base basefont br col command embed frame
12
+ hr img input isindex keygen link menuitem meta
13
+ param source track wbr),
14
+ encoding: nil,
15
+ escape_attrs: true,
16
+ escape_html: false,
17
+ escape_filter_interpolations: nil,
18
+ filename: '(haml)',
19
+ format: :html5,
20
+ hyphenate_data_attrs: true,
21
+ line: 1,
22
+ mime_type: 'text/html',
23
+ preserve: %w(textarea pre code),
24
+ remove_whitespace: false,
25
+ suppress_eval: false,
26
+ cdata: false,
27
+ parser_class: ::Haml::Parser,
28
+ compiler_class: ::Haml::Compiler,
29
+ trace: false,
30
+ filters: {},
31
+ )
32
+
33
+ use :Parser, -> { options[:parser_class] }
34
+ use :Compiler, -> { options[:compiler_class] }
35
+ use Escapable
36
+ filter :ControlFlow
37
+ filter :MultiFlattener
38
+ filter :StaticMerger
39
+ use Generator
40
+
41
+ def compile(template)
42
+ initialize_encoding(template, options[:encoding])
43
+ @precompiled = call(template)
44
+ end
45
+
46
+ # The source code that is evaluated to produce the Haml document.
47
+ #
48
+ # This is automatically converted to the correct encoding
49
+ # (see {file:REFERENCE.md#encodings the `:encoding` option}).
50
+ #
51
+ # @return [String]
52
+ def precompiled
53
+ encoding = Encoding.find(@encoding || '')
54
+ return @precompiled.dup.force_encoding(encoding) if encoding == Encoding::ASCII_8BIT
55
+ return @precompiled.encode(encoding)
56
+ end
57
+
58
+ def precompiled_with_return_value
59
+ "#{precompiled};#{precompiled_method_return_value}".dup
60
+ end
61
+
62
+ # The source code that is evaluated to produce the Haml document.
63
+ #
64
+ # This is automatically converted to the correct encoding
65
+ # (see {file:REFERENCE.md#encodings the `:encoding` option}).
66
+ #
67
+ # @return [String]
68
+ def precompiled_with_ambles(local_names, after_preamble: '', before_postamble: '')
69
+ preamble = <<END.tr("\n", ';')
70
+ begin
71
+ extend Haml::Helpers
72
+ _hamlout = @haml_buffer = Haml::Buffer.new(haml_buffer, #{Options.new(options).for_buffer.inspect})
73
+ _erbout = _hamlout.buffer
74
+ #{after_preamble}
75
+ END
76
+ postamble = <<END.tr("\n", ';')
77
+ #{before_postamble}
78
+ #{precompiled_method_return_value}
79
+ ensure
80
+ @haml_buffer = @haml_buffer.upper if @haml_buffer
81
+ end
82
+ END
83
+ "#{preamble}#{locals_code(local_names)}#{precompiled}#{postamble}".dup
84
+ end
85
+
86
+ private
87
+
88
+ def initialize_encoding(template, given_value)
89
+ if given_value
90
+ @encoding = given_value
91
+ else
92
+ @encoding = Encoding.default_internal || template.encoding
93
+ end
94
+ end
95
+
96
+ # Returns the string used as the return value of the precompiled method.
97
+ # This method exists so it can be monkeypatched to return modified values.
98
+ def precompiled_method_return_value
99
+ "_erbout"
100
+ end
101
+
102
+ def locals_code(names)
103
+ names = names.keys if Hash === names
104
+
105
+ names.map do |name|
106
+ # Can't use || because someone might explicitly pass in false with a symbol
107
+ sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
108
+ str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
109
+ "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local};"
110
+ end.join
111
+ end
112
+
113
+ def inspect_obj(obj)
114
+ case obj
115
+ when String
116
+ %Q!"#{obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]}}"!
117
+ when Symbol
118
+ ":#{inspect_obj(obj.to_s)}"
119
+ else
120
+ obj.inspect
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Haml
4
+ # A module to count lines of expected code. This would be faster than actual code generation
5
+ # and counting newlines in it.
6
+ module TempleLineCounter
7
+ class UnexpectedExpression < StandardError; end
8
+
9
+ def self.count_lines(exp)
10
+ type, *args = exp
11
+ case type
12
+ when :multi
13
+ args.map { |a| count_lines(a) }.reduce(:+) || 0
14
+ when :dynamic, :code
15
+ args.first.count("\n")
16
+ when :static
17
+ 0 # It has not real newline "\n" but escaped "\\n".
18
+ when :case
19
+ arg, *cases = args
20
+ arg.count("\n") + cases.map do |cond, e|
21
+ (cond == :else ? 0 : cond.count("\n")) + count_lines(e)
22
+ end.reduce(:+)
23
+ when :escape
24
+ count_lines(args[1])
25
+ else
26
+ raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
27
+ end
28
+ end
29
+ end
30
+ end