haml 4.0.0 → 5.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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +117 -5
  4. data/FAQ.md +7 -17
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +85 -42
  7. data/REFERENCE.md +181 -86
  8. data/Rakefile +47 -51
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +215 -0
  11. data/lib/haml/attribute_parser.rb +144 -0
  12. data/lib/haml/buffer.rb +38 -128
  13. data/lib/haml/compiler.rb +88 -295
  14. data/lib/haml/engine.rb +25 -41
  15. data/lib/haml/error.rb +3 -0
  16. data/lib/haml/escapable.rb +49 -0
  17. data/lib/haml/exec.rb +33 -19
  18. data/lib/haml/filters.rb +20 -24
  19. data/lib/haml/generator.rb +41 -0
  20. data/lib/haml/helpers/action_view_extensions.rb +3 -2
  21. data/lib/haml/helpers/action_view_mods.rb +44 -66
  22. data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
  23. data/lib/haml/helpers/safe_erubi_template.rb +27 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +16 -4
  25. data/lib/haml/helpers/xss_mods.rb +18 -12
  26. data/lib/haml/helpers.rb +122 -58
  27. data/lib/haml/options.rb +39 -46
  28. data/lib/haml/parser.rb +278 -217
  29. data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
  30. data/lib/haml/railtie.rb +21 -11
  31. data/lib/haml/sass_rails_filter.rb +17 -4
  32. data/lib/haml/template/options.rb +12 -2
  33. data/lib/haml/template.rb +12 -6
  34. data/lib/haml/temple_engine.rb +120 -0
  35. data/lib/haml/temple_line_counter.rb +29 -0
  36. data/lib/haml/util.rb +80 -199
  37. data/lib/haml/version.rb +2 -1
  38. data/lib/haml.rb +2 -1
  39. data/test/attribute_parser_test.rb +101 -0
  40. data/test/engine_test.rb +306 -176
  41. data/test/filters_test.rb +32 -19
  42. data/test/gemfiles/Gemfile.rails-4.0.x +11 -0
  43. data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
  44. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  45. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  46. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  47. data/test/helper_test.rb +282 -96
  48. data/test/options_test.rb +22 -0
  49. data/test/parser_test.rb +71 -4
  50. data/test/results/bemit.xhtml +4 -0
  51. data/test/results/eval_suppressed.xhtml +4 -4
  52. data/test/results/helpers.xhtml +43 -41
  53. data/test/results/helpful.xhtml +6 -3
  54. data/test/results/just_stuff.xhtml +21 -20
  55. data/test/results/list.xhtml +9 -9
  56. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  57. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  58. data/test/results/original_engine.xhtml +17 -17
  59. data/test/results/partial_layout.xhtml +4 -3
  60. data/test/results/partial_layout_erb.xhtml +4 -3
  61. data/test/results/partials.xhtml +11 -10
  62. data/test/results/silent_script.xhtml +63 -63
  63. data/test/results/standard.xhtml +156 -159
  64. data/test/results/tag_parsing.xhtml +19 -19
  65. data/test/results/very_basic.xhtml +2 -2
  66. data/test/results/whitespace_handling.xhtml +56 -50
  67. data/test/template_test.rb +44 -53
  68. data/test/template_test_helper.rb +38 -0
  69. data/test/templates/_text_area_helper.html.haml +4 -0
  70. data/test/templates/bemit.haml +3 -0
  71. data/test/templates/just_stuff.haml +1 -0
  72. data/test/templates/partial_layout_erb.erb +1 -1
  73. data/test/templates/standard_ugly.haml +1 -0
  74. data/test/templates/with_bom.haml +1 -0
  75. data/test/temple_line_counter_test.rb +40 -0
  76. data/test/test_helper.rb +26 -12
  77. data/test/util_test.rb +6 -47
  78. metadata +88 -106
  79. data/lib/haml/helpers/rails_323_textarea_fix.rb +0 -24
  80. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  81. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  82. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  83. data/test/gemfiles/Gemfile.rails-master +0 -4
  84. data/test/templates/_av_partial_1_ugly.haml +0 -9
  85. data/test/templates/_av_partial_2_ugly.haml +0 -5
  86. data/test/templates/action_view_ugly.haml +0 -47
  87. data/test/templates/standard_ugly.haml +0 -43
@@ -1,31 +1,24 @@
1
+ # frozen_string_literal: true
1
2
  module Haml
2
3
 
3
4
  # This module makes Haml work with Rails using the template handler API.
4
- class Plugin < ActionView::Template::Handlers::ERB.superclass
5
-
6
- # Rails 3.1+, template handlers don't inherit from anything. In <= 3.0, they
7
- # do. To avoid messy logic figuring this out, we just inherit from whatever
8
- # the ERB handler does.
9
-
10
- # In Rails 3.1+, we don't need to include Compilable.
11
- if (ActionPack::VERSION::MAJOR == 3) && (ActionPack::VERSION::MINOR < 1)
12
- include ActionView::Template::Handlers::Compilable
13
- end
14
-
5
+ class Plugin
15
6
  def handles_encoding?; true; end
16
7
 
17
8
  def compile(template)
18
9
  options = Haml::Template.options.dup
19
- if (ActionPack::VERSION::MAJOR >= 4) && template.respond_to?(:type)
10
+ if template.respond_to?(:type)
20
11
  options[:mime_type] = template.type
21
12
  elsif template.respond_to? :mime_type
22
13
  options[:mime_type] = template.mime_type
23
14
  end
24
15
  options[:filename] = template.identifier
25
- Haml::Engine.new(template.source, options).compiler.precompiled_with_ambles([])
16
+ Haml::Engine.new(template.source, options).compiler.precompiled_with_ambles(
17
+ [],
18
+ after_preamble: '@output_buffer = output_buffer ||= ActionView::OutputBuffer.new if defined?(ActionView::OutputBuffer)',
19
+ )
26
20
  end
27
21
 
28
- # In Rails 3.1+, #call takes the place of #compile
29
22
  def self.call(template)
30
23
  new.compile(template)
31
24
  end
@@ -38,4 +31,4 @@ module Haml
38
31
  end
39
32
  end
40
33
 
41
- ActionView::Template.register_template_handler(:haml, Haml::Plugin)
34
+ ActionView::Template.register_template_handler(:haml, Haml::Plugin)
data/lib/haml/railtie.rb CHANGED
@@ -1,21 +1,31 @@
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
+ require 'haml/template/options'
3
+
4
+ # check for a compatible Rails version when Haml is loaded
5
+ if (activesupport_spec = Gem.loaded_specs['activesupport'])
6
+ if activesupport_spec.version.to_s < '3.2'
7
+ raise Exception.new("\n\n** Haml now requires Rails 3.2 and later. Use Haml version 4.0.4\n\n")
7
8
  end
8
9
  end
9
10
 
10
11
  module Haml
11
12
  class Railtie < ::Rails::Railtie
12
13
  initializer :haml do |app|
13
- if defined?(::Sass::Rails) && app.config.assets.enabled
14
- require "haml/sass_rails_filter"
14
+ ActiveSupport.on_load(:action_view) do
15
+ require "haml/template"
16
+
17
+ if defined?(::Sass::Rails::SassTemplate) && app.config.assets.enabled
18
+ require "haml/sass_rails_filter"
19
+ end
20
+
21
+ if defined? Erubi
22
+ require "haml/helpers/safe_erubi_template"
23
+ Haml::Filters::Erb.template_class = Haml::SafeErubiTemplate
24
+ else
25
+ require "haml/helpers/safe_erubis_template"
26
+ Haml::Filters::Erb.template_class = Haml::SafeErubisTemplate
27
+ end
15
28
  end
16
29
  end
17
30
  end
18
31
  end
19
-
20
- require "haml/helpers/safe_erubis_template"
21
- Haml::Filters::Erb.template_class = Haml::SafeErubisTemplate
@@ -1,11 +1,24 @@
1
+ # frozen_string_literal: true
1
2
  module Haml
2
3
  module Filters
3
4
  # This is an extension of Sass::Rails's SassTemplate class that allows
4
5
  # Rails's asset helpers to be used inside Haml Sass filter.
5
6
  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
7
+ if Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new('3.0.0')
8
+ def render(scope=Object.new, locals={}, &block)
9
+ environment = ::Sprockets::Railtie.build_environment(Rails.application)
10
+ scope = environment.context_class.new(
11
+ environment: environment,
12
+ filename: "/",
13
+ metadata: {}
14
+ )
15
+ super
16
+ end
17
+ else
18
+ def render(scope=Object.new, locals={}, &block)
19
+ scope = ::Rails.application.assets.context_class.new(::Rails.application.assets, "/", "/")
20
+ super
21
+ end
9
22
  end
10
23
 
11
24
  def sass_options(scope)
@@ -30,4 +43,4 @@ module Haml
30
43
  register_tilt_filter "Sass", :extend => "Css", :template_class => SassRailsTemplate
31
44
  register_tilt_filter "Scss", :extend => "Css", :template_class => ScssRailsTemplate
32
45
  end
33
- end
46
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # We keep options in its own self-contained file
2
3
  # so that we can load it independently in Rails 3,
3
4
  # where the full template stuff is lazy-loaded.
@@ -6,11 +7,20 @@ module Haml
6
7
  module Template
7
8
  extend self
8
9
 
9
- @options = {}
10
+ class Options < Hash
11
+ def []=(key, value)
12
+ super
13
+ if Haml::Options.buffer_defaults.key?(key)
14
+ Haml::Options.buffer_defaults[key] = value
15
+ end
16
+ end
17
+ end
18
+
19
+ @options = ::Haml::Template::Options.new
10
20
  # The options hash for Haml when used within Rails.
11
21
  # See {file:REFERENCE.md#options the Haml options documentation}.
12
22
  #
13
- # @return [{Symbol => Object}]
23
+ # @return [Haml::Template::Options<Symbol => Object>]
14
24
  attr_accessor :options
15
25
  end
16
26
  end
data/lib/haml/template.rb CHANGED
@@ -1,12 +1,19 @@
1
+ # frozen_string_literal: true
1
2
  require 'haml/template/options'
2
- require 'haml/engine'
3
- require 'haml/helpers/action_view_mods'
4
- require 'haml/helpers/action_view_extensions'
3
+ if defined?(ActiveSupport)
4
+ ActiveSupport.on_load(:action_view) do
5
+ require 'haml/helpers/action_view_mods'
6
+ require 'haml/helpers/action_view_extensions'
7
+ end
8
+ else
9
+ require 'haml/helpers/action_view_mods'
10
+ require 'haml/helpers/action_view_extensions'
11
+ end
5
12
  require 'haml/helpers/xss_mods'
6
13
  require 'haml/helpers/action_view_xss_mods'
7
14
 
8
15
  module Haml
9
- class Compiler
16
+ class TempleEngine
10
17
  def precompiled_method_return_value_with_haml_xss
11
18
  "::Haml::Util.html_safe(#{precompiled_method_return_value_without_haml_xss})"
12
19
  end
@@ -26,7 +33,6 @@ module Haml
26
33
  end
27
34
 
28
35
 
29
- Haml::Template.options[:ugly] = !Rails.env.development?
30
36
  Haml::Template.options[:escape_html] = true
31
37
 
32
- require 'haml/template/plugin'
38
+ require 'haml/plugin'
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: false
2
+ require 'temple'
3
+ require 'haml/escapable'
4
+ require 'haml/generator'
5
+
6
+ module Haml
7
+ class TempleEngine < Temple::Engine
8
+ define_options(
9
+ attr_wrapper: "'",
10
+ autoclose: %w(area base basefont br col command embed frame
11
+ hr img input isindex keygen link menuitem meta
12
+ param source track wbr),
13
+ encoding: nil,
14
+ escape_attrs: true,
15
+ escape_html: false,
16
+ filename: '(haml)',
17
+ format: :html5,
18
+ hyphenate_data_attrs: true,
19
+ line: 1,
20
+ mime_type: 'text/html',
21
+ preserve: %w(textarea pre code),
22
+ remove_whitespace: false,
23
+ suppress_eval: false,
24
+ cdata: false,
25
+ parser_class: ::Haml::Parser,
26
+ compiler_class: ::Haml::Compiler,
27
+ trace: false,
28
+ )
29
+
30
+ use :Parser, -> { options[:parser_class] }
31
+ use :Compiler, -> { options[:compiler_class] }
32
+ use Escapable
33
+ filter :ControlFlow
34
+ filter :MultiFlattener
35
+ filter :StaticMerger
36
+ use Generator
37
+
38
+ def compile(template)
39
+ initialize_encoding(template, options[:encoding])
40
+ @precompiled = call(template)
41
+ end
42
+
43
+ # The source code that is evaluated to produce the Haml document.
44
+ #
45
+ # This is automatically converted to the correct encoding
46
+ # (see {file:REFERENCE.md#encodings the `:encoding` option}).
47
+ #
48
+ # @return [String]
49
+ def precompiled
50
+ encoding = Encoding.find(@encoding || '')
51
+ return @precompiled.force_encoding(encoding) if encoding == Encoding::ASCII_8BIT
52
+ return @precompiled.encode(encoding)
53
+ end
54
+
55
+ def precompiled_with_return_value
56
+ "#{precompiled};#{precompiled_method_return_value}"
57
+ end
58
+
59
+ # The source code that is evaluated to produce the Haml document.
60
+ #
61
+ # This is automatically converted to the correct encoding
62
+ # (see {file:REFERENCE.md#encodings the `:encoding` option}).
63
+ #
64
+ # @return [String]
65
+ def precompiled_with_ambles(local_names, after_preamble: '')
66
+ preamble = <<END.tr!("\n", ';')
67
+ begin
68
+ extend Haml::Helpers
69
+ _hamlout = @haml_buffer = Haml::Buffer.new(haml_buffer, #{Options.new(options).for_buffer.inspect})
70
+ _erbout = _hamlout.buffer
71
+ #{after_preamble}
72
+ END
73
+ postamble = <<END.tr!("\n", ';')
74
+ #{precompiled_method_return_value}
75
+ ensure
76
+ @haml_buffer = @haml_buffer.upper if @haml_buffer
77
+ end
78
+ END
79
+ "#{preamble}#{locals_code(local_names)}#{precompiled}#{postamble}"
80
+ end
81
+
82
+ private
83
+
84
+ def initialize_encoding(template, given_value)
85
+ if given_value
86
+ @encoding = given_value
87
+ else
88
+ @encoding = Encoding.default_internal || template.encoding
89
+ end
90
+ end
91
+
92
+ # Returns the string used as the return value of the precompiled method.
93
+ # This method exists so it can be monkeypatched to return modified values.
94
+ def precompiled_method_return_value
95
+ "_erbout"
96
+ end
97
+
98
+ def locals_code(names)
99
+ names = names.keys if Hash === names
100
+
101
+ names.each_with_object('') do |name, code|
102
+ # Can't use || because someone might explicitly pass in false with a symbol
103
+ sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
104
+ str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
105
+ code << "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local};"
106
+ end
107
+ end
108
+
109
+ def inspect_obj(obj)
110
+ case obj
111
+ when String
112
+ %Q!"#{obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]}}"!
113
+ when Symbol
114
+ ":#{inspect_obj(obj.to_s)}"
115
+ else
116
+ obj.inspect
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ # A module to count lines of expected code. This would be faster than actual code generation
4
+ # and counting newlines in it.
5
+ module TempleLineCounter
6
+ class UnexpectedExpression < StandardError; end
7
+
8
+ def self.count_lines(exp)
9
+ type, *args = exp
10
+ case type
11
+ when :multi
12
+ args.map { |a| count_lines(a) }.reduce(:+) || 0
13
+ when :dynamic, :code
14
+ args.first.count("\n")
15
+ when :static
16
+ 0 # It has not real newline "\n" but escaped "\\n".
17
+ when :case
18
+ arg, *cases = args
19
+ arg.count("\n") + cases.map do |cond, e|
20
+ (cond == :else ? 0 : cond.count("\n")) + count_lines(e)
21
+ end.reduce(:+)
22
+ when :escape
23
+ count_lines(args[1])
24
+ else
25
+ raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
26
+ end
27
+ end
28
+ end
29
+ end