haml 5.2.2 → 6.3.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/test.yml +15 -15
  4. data/.gitignore +16 -16
  5. data/.yardopts +0 -3
  6. data/CHANGELOG.md +168 -4
  7. data/FAQ.md +1 -1
  8. data/Gemfile +21 -10
  9. data/MIT-LICENSE +1 -1
  10. data/README.md +22 -34
  11. data/REFERENCE.md +95 -159
  12. data/Rakefile +15 -82
  13. data/bin/bench +66 -0
  14. data/bin/console +11 -0
  15. data/bin/ruby +3 -0
  16. data/bin/setup +7 -0
  17. data/bin/stackprof +27 -0
  18. data/bin/test +24 -0
  19. data/exe/haml +6 -0
  20. data/haml.gemspec +34 -36
  21. data/lib/haml/ambles.rb +20 -0
  22. data/lib/haml/attribute_builder.rb +127 -184
  23. data/lib/haml/attribute_compiler.rb +90 -194
  24. data/lib/haml/attribute_parser.rb +92 -126
  25. data/lib/haml/cli.rb +154 -0
  26. data/lib/haml/compiler/children_compiler.rb +155 -0
  27. data/lib/haml/compiler/comment_compiler.rb +51 -0
  28. data/lib/haml/compiler/doctype_compiler.rb +52 -0
  29. data/lib/haml/compiler/script_compiler.rb +114 -0
  30. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  31. data/lib/haml/compiler/tag_compiler.rb +76 -0
  32. data/lib/haml/compiler.rb +63 -296
  33. data/lib/haml/dynamic_merger.rb +67 -0
  34. data/lib/haml/engine.rb +48 -227
  35. data/lib/haml/error.rb +5 -4
  36. data/lib/haml/escape.rb +13 -0
  37. data/lib/haml/escape_any.rb +21 -0
  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 +59 -0
  54. data/lib/haml/filters.rb +54 -378
  55. data/lib/haml/force_escape.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 +35 -0
  60. data/lib/haml/parser.rb +158 -23
  61. data/lib/haml/rails_helpers.rb +53 -0
  62. data/lib/haml/rails_template.rb +62 -0
  63. data/lib/haml/railtie.rb +3 -46
  64. data/lib/haml/ruby_expression.rb +32 -0
  65. data/lib/haml/string_splitter.rb +140 -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 +19 -15
  69. data/lib/haml/version.rb +1 -2
  70. data/lib/haml/whitespace.rb +8 -0
  71. data/lib/haml.rb +8 -20
  72. metadata +188 -50
  73. data/.gitmodules +0 -3
  74. data/TODO +0 -24
  75. data/benchmark.rb +0 -70
  76. data/bin/haml +0 -9
  77. data/lib/haml/.gitattributes +0 -1
  78. data/lib/haml/buffer.rb +0 -182
  79. data/lib/haml/escapable.rb +0 -77
  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 -54
  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 -124
  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/haml.gemspec CHANGED
@@ -1,45 +1,43 @@
1
- ($LOAD_PATH << File.expand_path("../lib", __FILE__)).uniq!
2
- require "haml/version"
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'haml/version'
3
5
 
4
6
  Gem::Specification.new do |spec|
5
- spec.name = 'haml'
6
- spec.summary = "An elegant, structured (X)HTML/XML templating engine."
7
- spec.version = Haml::VERSION
8
- spec.authors = ['Natalie Weizenbaum', 'Hampton Catlin', 'Norman Clarke', 'Akira Matsuda']
9
- spec.email = ['haml@googlegroups.com', 'ronnie@dio.jp']
7
+ spec.name = 'haml'
8
+ spec.version = Haml::VERSION
9
+ spec.authors = ['Natalie Weizenbaum', 'Hampton Catlin', 'Norman Clarke', 'Akira Matsuda', 'Takashi Kokubun']
10
+ spec.email = ['haml@googlegroups.com', 'ronnie@dio.jp']
10
11
 
11
- spec.executables = ['haml']
12
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
13
- f.match(%r{\Atest/})
14
- end
15
- spec.homepage = 'http://haml.info/'
16
- spec.license = "MIT"
17
- spec.metadata = {
18
- "bug_tracker_uri" => "https://github.com/haml/haml/issues",
19
- "changelog_uri" => "https://github.com/haml/haml/blob/main/CHANGELOG.md",
20
- "documentation_uri" => "http://haml.info/docs.html",
21
- "homepage_uri" => "http://haml.info",
22
- "mailing_list_uri" => "https://groups.google.com/forum/?fromgroups#!forum/haml",
23
- "source_code_uri" => "https://github.com/haml/haml"
24
- }
12
+ spec.summary = %q{An elegant, structured (X)HTML/XML templating engine.}
13
+ spec.description = %q{An elegant, structured (X)HTML/XML templating engine.}
14
+ spec.homepage = 'https://haml.info'
15
+ spec.license = 'MIT'
25
16
 
26
- spec.required_ruby_version = '>= 2.0.0'
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|sample|benchmark)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
27
21
 
28
- spec.add_dependency 'temple', '>= 0.8.0'
29
- spec.add_dependency 'tilt'
22
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
30
23
 
31
- spec.add_development_dependency 'rails', '>= 4.0.0'
32
- spec.add_development_dependency 'rbench'
33
- spec.add_development_dependency 'minitest', '>= 4.0'
34
- spec.add_development_dependency 'nokogiri'
35
- spec.add_development_dependency 'simplecov'
24
+ spec.required_ruby_version = '>= 2.1.0'
36
25
 
37
- spec.description = <<-END
38
- Haml (HTML Abstraction Markup Language) is a layer on top of HTML or XML that's
39
- designed to express the structure of documents in a non-repetitive, elegant, and
40
- easy way by using indentation rather than closing tags and allowing Ruby to be
41
- embedded with ease. It was originally envisioned as a plugin for Ruby on Rails,
42
- but it can function as a stand-alone templating engine.
43
- END
26
+ spec.add_dependency 'temple', '>= 0.8.2'
27
+ spec.add_dependency 'thor'
28
+ spec.add_dependency 'tilt'
44
29
 
30
+ spec.add_development_dependency 'benchmark_driver'
31
+ spec.add_development_dependency 'bundler'
32
+ spec.add_development_dependency 'coffee-script'
33
+ spec.add_development_dependency 'erubi'
34
+ spec.add_development_dependency 'haml', '>= 5'
35
+ spec.add_development_dependency 'less'
36
+ spec.add_development_dependency 'minitest-reporters', '~> 1.1'
37
+ spec.add_development_dependency 'rails', '>= 4.0'
38
+ spec.add_development_dependency 'rake'
39
+ spec.add_development_dependency 'sass'
40
+ spec.add_development_dependency 'slim'
41
+ spec.add_development_dependency 'string_template'
42
+ spec.add_development_dependency 'unindent'
45
43
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Ambles < Temple::Filter
4
+ define_options :preamble, :postamble
5
+
6
+ def initialize(*)
7
+ super
8
+ @preamble = options[:preamble]
9
+ @postamble = options[:postamble]
10
+ end
11
+
12
+ def call(ast)
13
+ ret = [:multi]
14
+ ret << [:static, @preamble] if @preamble
15
+ ret << ast
16
+ ret << [:static, @postamble] if @postamble
17
+ ret
18
+ end
19
+ end
20
+ end
@@ -1,218 +1,161 @@
1
1
  # frozen_string_literal: true
2
-
3
- module Haml
4
- module AttributeBuilder
5
- # https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
6
- INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
7
-
8
- class << self
9
- def build(class_id, obj_ref, is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, *attributes_hashes)
10
- attributes = class_id
11
- attributes_hashes.each do |old|
12
- result = {}
13
- old.each { |k, v| result[k.to_s] = v }
14
- merge_attributes!(attributes, result)
2
+ require 'haml/object_ref'
3
+
4
+ module Haml::AttributeBuilder
5
+ class << self
6
+ def build(escape_attrs, quote, format, object_ref, *hashes)
7
+ hashes << Haml::ObjectRef.parse(object_ref) if object_ref
8
+ buf = []
9
+ hash = merge_all_attrs(hashes)
10
+
11
+ keys = hash.keys.sort!
12
+ keys.each do |key|
13
+ case key
14
+ when 'id'
15
+ buf << " id=#{quote}#{build_id(escape_attrs, *hash[key])}#{quote}"
16
+ when 'class'
17
+ buf << " class=#{quote}#{build_class(escape_attrs, *hash[key])}#{quote}"
18
+ when 'data'
19
+ buf << build_data(escape_attrs, quote, *hash[key])
20
+ when 'aria'
21
+ buf << build_aria(escape_attrs, quote, *hash[key])
22
+ when *Haml::BOOLEAN_ATTRIBUTES, /\Adata-/, /\Aaria-/
23
+ build_boolean!(escape_attrs, quote, format, buf, key, hash[key])
24
+ else
25
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, hash[key].to_s)}#{quote}"
15
26
  end
16
- merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref
17
- build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes)
18
27
  end
28
+ buf.join
29
+ end
19
30
 
20
- def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
21
- # @TODO this is an absolutely ridiculous amount of arguments. At least
22
- # some of this needs to be moved into an instance method.
23
- join_char = hyphenate_data_attrs ? '-' : '_'
31
+ def build_id(escape_attrs, *values)
32
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
33
+ end
24
34
 
25
- attributes.each do |key, value|
26
- if value.is_a?(Hash)
27
- data_attributes = attributes.delete(key)
28
- data_attributes = flatten_data_attributes(data_attributes, '', join_char)
29
- data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
30
- verify_attribute_names!(data_attributes.keys)
31
- attributes = data_attributes.merge(attributes)
32
- end
35
+ def build_class(escape_attrs, *values)
36
+ if values.size == 1
37
+ value = values.first
38
+ case
39
+ when value.is_a?(String)
40
+ # noop
41
+ when value.is_a?(Array)
42
+ value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
43
+ when value
44
+ value = value.to_s
45
+ else
46
+ return ''
33
47
  end
48
+ return escape_html(escape_attrs, value)
49
+ end
34
50
 
35
- result = attributes.collect do |attr, value|
36
- next if value.nil?
37
-
38
- value = filter_and_join(value, ' ') if attr == 'class'
39
- value = filter_and_join(value, '_') if attr == 'id'
40
-
41
- if value == true
42
- next " #{attr}" if is_html
43
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
44
- elsif value == false
45
- next
46
- end
47
-
48
- value =
49
- if escape_attrs == :once
50
- Haml::Helpers.escape_once_without_haml_xss(value.to_s)
51
- elsif escape_attrs
52
- Haml::Helpers.html_escape_without_haml_xss(value.to_s)
53
- else
54
- value.to_s
55
- end
56
- " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
51
+ classes = []
52
+ values.each do |value|
53
+ case
54
+ when value.is_a?(String)
55
+ classes += value.split(' ')
56
+ when value.is_a?(Array)
57
+ classes += value.select { |v| v }
58
+ when value
59
+ classes << value.to_s
57
60
  end
58
- result.compact!
59
- result.sort!
60
- result.join
61
61
  end
62
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
63
+ end
62
64
 
63
- # @return [String, nil]
64
- def filter_and_join(value, separator)
65
- return '' if (value.respond_to?(:empty?) && value.empty?)
65
+ def build_data(escape_attrs, quote, *hashes)
66
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
67
+ end
66
68
 
67
- if value.is_a?(Array)
68
- value = value.flatten
69
- value.map! {|item| item ? item.to_s : nil}
70
- value.compact!
71
- value = value.join(separator)
72
- else
73
- value = value ? value.to_s : nil
74
- end
75
- !value.nil? && !value.empty? && value
76
- end
69
+ def build_aria(escape_attrs, quote, *hashes)
70
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
71
+ end
77
72
 
78
- # Merges two attribute hashes.
79
- # This is the same as `to.merge!(from)`,
80
- # except that it merges id, class, and data attributes.
81
- #
82
- # ids are concatenated with `"_"`,
83
- # and classes are concatenated with `" "`.
84
- # data hashes are simply merged.
85
- #
86
- # Destructively modifies `to`.
87
- #
88
- # @param to [{String => String,Hash}] The attribute hash to merge into
89
- # @param from [{String => Object}] The attribute hash to merge from
90
- # @return [{String => String,Hash}] `to`, after being merged
91
- def merge_attributes!(to, from)
92
- from.keys.each do |key|
93
- to[key] = merge_value(key, to[key], from[key])
94
- end
95
- to
96
- end
73
+ private
97
74
 
98
- # Merge multiple values to one attribute value. No destructive operation.
99
- #
100
- # @param key [String]
101
- # @param values [Array<Object>]
102
- # @return [String,Hash]
103
- def merge_values(key, *values)
104
- values.inject(nil) do |to, from|
105
- merge_value(key, to, from)
106
- end
75
+ def build_data_attribute(key, escape_attrs, quote, *hashes)
76
+ attrs = []
77
+ if hashes.size > 1 && hashes.all? { |h| h.is_a?(Hash) }
78
+ data_value = merge_all_attrs(hashes)
79
+ else
80
+ data_value = hashes.last
107
81
  end
108
-
109
- def verify_attribute_names!(attribute_names)
110
- attribute_names.each do |attribute_name|
111
- if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
112
- raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
113
- end
82
+ hash = flatten_attributes(key => data_value)
83
+
84
+ hash.sort_by(&:first).each do |key, value|
85
+ case value
86
+ when true
87
+ attrs << " #{key}"
88
+ when nil, false
89
+ # noop
90
+ else
91
+ attrs << " #{key}=#{quote}#{escape_html(escape_attrs, value.to_s)}#{quote}"
114
92
  end
115
93
  end
94
+ attrs.join
95
+ end
116
96
 
117
- private
97
+ def flatten_attributes(attributes)
98
+ flattened = {}
118
99
 
119
- # Merge a couple of values to one attribute value. No destructive operation.
120
- #
121
- # @param to [String,Hash,nil]
122
- # @param from [Object]
123
- # @return [String,Hash]
124
- def merge_value(key, to, from)
125
- if from.kind_of?(Hash) || to.kind_of?(Hash)
126
- from = { nil => from } if !from.is_a?(Hash)
127
- to = { nil => to } if !to.is_a?(Hash)
128
- to.merge(from)
129
- elsif key == 'id'
130
- merged_id = filter_and_join(from, '_')
131
- if to && merged_id
132
- merged_id = "#{to}_#{merged_id}"
133
- elsif to || merged_id
134
- merged_id ||= to
135
- end
136
- merged_id
137
- elsif key == 'class'
138
- merged_class = filter_and_join(from, ' ')
139
- if to && merged_class
140
- merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
141
- elsif to || merged_class
142
- merged_class ||= to
100
+ attributes.each do |key, value|
101
+ case value
102
+ when attributes
103
+ when Hash
104
+ flatten_attributes(value).each do |k, v|
105
+ if k.nil?
106
+ flattened[key] = v
107
+ else
108
+ flattened["#{key}-#{k.to_s.gsub(/_/, '-')}"] = v
109
+ end
143
110
  end
144
- merged_class
145
111
  else
146
- from
112
+ flattened[key] = value if value
147
113
  end
148
114
  end
115
+ flattened
116
+ end
149
117
 
150
- def build_data_keys(data_hash, hyphenate, attr_name="data")
151
- Hash[data_hash.map do |name, value|
152
- if name == nil
153
- [attr_name, value]
154
- elsif hyphenate
155
- ["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
118
+ def merge_all_attrs(hashes)
119
+ merged = {}
120
+ hashes.each do |hash|
121
+ unless hash.is_a?(Hash)
122
+ raise ArgumentError, "Non-hash object is given to attributes!"
123
+ end
124
+ hash.each do |key, value|
125
+ key = key.to_s
126
+ case key
127
+ when 'id', 'class', 'data', 'aria'
128
+ merged[key] ||= []
129
+ merged[key] << value
156
130
  else
157
- ["#{attr_name}-#{name}", value]
131
+ merged[key] = value
158
132
  end
159
- end]
160
- end
161
-
162
- def flatten_data_attributes(data, key, join_char, seen = [])
163
- return {key => data} unless data.is_a?(Hash)
164
-
165
- return {key => nil} if seen.include? data.object_id
166
- seen << data.object_id
167
-
168
- data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
169
- joined = key == '' ? k : [key, k].join(join_char)
170
- hash.merge! flatten_data_attributes(v, joined, join_char, seen)
171
133
  end
172
134
  end
135
+ merged
136
+ end
173
137
 
174
- # Takes an array of objects and uses the class and id of the first
175
- # one to create an attributes hash.
176
- # The second object, if present, is used as a prefix,
177
- # just like you can do with `dom_id()` and `dom_class()` in Rails
178
- def parse_object_ref(ref)
179
- prefix = ref[1]
180
- ref = ref[0]
181
- # Let's make sure the value isn't nil. If it is, return the default Hash.
182
- return {} if ref.nil?
183
- class_name =
184
- if ref.respond_to?(:haml_object_ref)
185
- ref.haml_object_ref
186
- else
187
- underscore(ref.class)
188
- end
189
- ref_id =
190
- if ref.respond_to?(:to_key)
191
- key = ref.to_key
192
- key.join('_') unless key.nil?
193
- else
194
- ref.id
195
- end
196
- id = "#{class_name}_#{ref_id || 'new'}"
197
- if prefix
198
- class_name = "#{ prefix }_#{ class_name}"
199
- id = "#{ prefix }_#{ id }"
138
+ def build_boolean!(escape_attrs, quote, format, buf, key, value)
139
+ case value
140
+ when true
141
+ case format
142
+ when :xhtml
143
+ buf << " #{key}=#{quote}#{key}#{quote}"
144
+ else
145
+ buf << " #{key}"
200
146
  end
201
-
202
- { 'id'.freeze => id, 'class'.freeze => class_name }
147
+ when false, nil
148
+ # omitted
149
+ else
150
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
203
151
  end
152
+ end
204
153
 
205
- # Changes a word from camel case to underscores.
206
- # Based on the method of the same name in Rails' Inflector,
207
- # but copied here so it'll run properly without Rails.
208
- def underscore(camel_cased_word)
209
- word = camel_cased_word.to_s.dup
210
- word.gsub!(/::/, '_')
211
- word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
212
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
213
- word.tr!('-', '_')
214
- word.downcase!
215
- word
154
+ def escape_html(escape_attrs, str)
155
+ if escape_attrs
156
+ Haml::Util.escape_html(str)
157
+ else
158
+ str
216
159
  end
217
160
  end
218
161
  end