haml 5.2.2 → 6.3.0

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 +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