haml 5.2.2 → 6.0.0.beta.1

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 +1 -0
  3. data/.github/workflows/test.yml +13 -9
  4. data/.gitignore +16 -16
  5. data/CHANGELOG.md +13 -3
  6. data/Gemfile +18 -11
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +13 -19
  9. data/Rakefile +95 -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 +135 -179
  24. data/lib/haml/attribute_compiler.rb +85 -194
  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 +3 -52
  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/haml_error.rb +66 -0
  57. data/lib/haml/helpers.rb +3 -697
  58. data/lib/haml/html.rb +22 -0
  59. data/lib/haml/identity.rb +13 -0
  60. data/lib/haml/object_ref.rb +30 -0
  61. data/lib/haml/parser.rb +179 -49
  62. data/lib/haml/rails_helpers.rb +51 -0
  63. data/lib/haml/rails_template.rb +55 -0
  64. data/lib/haml/railtie.rb +7 -45
  65. data/lib/haml/ruby_expression.rb +32 -0
  66. data/lib/haml/string_splitter.rb +20 -0
  67. data/lib/haml/template.rb +15 -34
  68. data/lib/haml/temple_line_counter.rb +2 -1
  69. data/lib/haml/util.rb +17 -15
  70. data/lib/haml/version.rb +1 -2
  71. data/lib/haml.rb +8 -20
  72. metadata +211 -57
  73. data/.gitmodules +0 -3
  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 -182
  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,47 @@
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
- }
25
-
26
- spec.required_ruby_version = '>= 2.0.0'
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'
27
16
 
28
- spec.add_dependency 'temple', '>= 0.8.0'
29
- spec.add_dependency 'tilt'
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']
30
21
 
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'
22
+ if /java/ === RUBY_PLATFORM
23
+ spec.platform = 'java'
24
+ else
25
+ spec.extensions = ['ext/haml/extconf.rb']
26
+ spec.required_ruby_version = '>= 2.1.0'
27
+ end
36
28
 
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
29
+ spec.add_dependency 'temple', '>= 0.8.2'
30
+ spec.add_dependency 'thor'
31
+ spec.add_dependency 'tilt'
44
32
 
33
+ spec.add_development_dependency 'benchmark_driver'
34
+ spec.add_development_dependency 'bundler'
35
+ spec.add_development_dependency 'coffee-script'
36
+ spec.add_development_dependency 'erubi'
37
+ spec.add_development_dependency 'haml', '>= 5'
38
+ spec.add_development_dependency 'less'
39
+ spec.add_development_dependency 'minitest-reporters', '~> 1.1'
40
+ spec.add_development_dependency 'rails', '>= 4.0'
41
+ spec.add_development_dependency 'rake'
42
+ spec.add_development_dependency 'rake-compiler'
43
+ spec.add_development_dependency 'sass'
44
+ spec.add_development_dependency 'slim'
45
+ spec.add_development_dependency 'string_template'
46
+ spec.add_development_dependency 'unindent'
45
47
  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,219 +1,175 @@
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
-
2
+ require 'haml/object_ref'
3
+
4
+ module Haml::AttributeBuilder
5
+ BOOLEAN_ATTRIBUTES = %w[disabled readonly multiple checked autobuffer
6
+ autoplay controls loop selected hidden scoped async
7
+ defer reversed ismap seamless muted required
8
+ autofocus novalidate formnovalidate open pubdate
9
+ itemscope allowfullscreen default inert sortable
10
+ truespeed typemustmatch download].freeze
11
+
12
+ # Java extension is not implemented for JRuby yet.
13
+ # TruffleRuby does not implement `rb_ary_sort_bang`, etc.
14
+ if /java/ === RUBY_PLATFORM || RUBY_ENGINE == 'truffleruby'
8
15
  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)
16
+ def build(escape_attrs, quote, format, boolean_attributes, object_ref, *hashes)
17
+ hashes << Haml::ObjectRef.parse(object_ref) if object_ref
18
+ buf = []
19
+ hash = merge_all_attrs(hashes)
20
+
21
+ keys = hash.keys.sort!
22
+ keys.each do |key|
23
+ case key
24
+ when 'id'.freeze
25
+ buf << " id=#{quote}#{build_id(escape_attrs, *hash[key])}#{quote}"
26
+ when 'class'.freeze
27
+ buf << " class=#{quote}#{build_class(escape_attrs, *hash[key])}#{quote}"
28
+ when 'data'.freeze
29
+ buf << build_data(escape_attrs, quote, *hash[key])
30
+ when *boolean_attributes, /\Adata-/
31
+ build_boolean!(escape_attrs, quote, format, buf, key, hash[key])
32
+ else
33
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, hash[key].to_s)}#{quote}"
34
+ end
15
35
  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)
36
+ buf.join
18
37
  end
19
38
 
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 ? '-' : '_'
39
+ def build_id(escape_attrs, *values)
40
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
41
+ end
24
42
 
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)
43
+ def build_class(escape_attrs, *values)
44
+ if values.size == 1
45
+ value = values.first
46
+ case
47
+ when value.is_a?(String)
48
+ # noop
49
+ when value.is_a?(Array)
50
+ value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
51
+ when value
52
+ value = value.to_s
53
+ else
54
+ return ''
32
55
  end
56
+ return escape_html(escape_attrs, value)
33
57
  end
34
58
 
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
59
+ classes = []
60
+ values.each do |value|
61
+ case
62
+ when value.is_a?(String)
63
+ classes += value.split(' ')
64
+ when value.is_a?(Array)
65
+ classes += value.select { |v| v }
66
+ when value
67
+ classes << value.to_s
46
68
  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}"
57
69
  end
58
- result.compact!
59
- result.sort!
60
- result.join
70
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
61
71
  end
62
72
 
63
- # @return [String, nil]
64
- def filter_and_join(value, separator)
65
- return '' if (value.respond_to?(:empty?) && value.empty?)
66
-
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
73
+ def build_data(escape_attrs, quote, *hashes)
74
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
76
75
  end
77
76
 
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
77
+ def build_aria(escape_attrs, quote, *hashes)
78
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
96
79
  end
97
80
 
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
107
- end
81
+ private
108
82
 
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")
83
+ def build_data_attribute(key, escape_attrs, quote, *hashes)
84
+ attrs = []
85
+ if hashes.size > 1 && hashes.all? { |h| h.is_a?(Hash) }
86
+ data_value = merge_all_attrs(hashes)
87
+ else
88
+ data_value = hashes.last
89
+ end
90
+ hash = flatten_attributes(key => data_value)
91
+
92
+ hash.sort_by(&:first).each do |key, value|
93
+ case value
94
+ when true
95
+ attrs << " #{key}"
96
+ when nil, false
97
+ # noop
98
+ else
99
+ attrs << " #{key}=#{quote}#{escape_html(escape_attrs, value.to_s)}#{quote}"
113
100
  end
114
101
  end
102
+ attrs.join
115
103
  end
116
104
 
117
- private
105
+ def flatten_attributes(attributes)
106
+ flattened = {}
118
107
 
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
108
+ attributes.each do |key, value|
109
+ case value
110
+ when attributes
111
+ when Hash
112
+ flatten_attributes(value).each do |k, v|
113
+ if k.nil?
114
+ flattened[key] = v
115
+ else
116
+ flattened["#{key}-#{k.to_s.gsub(/_/, '-')}"] = v
117
+ end
118
+ end
119
+ else
120
+ flattened[key] = value if value
143
121
  end
144
- merged_class
145
- else
146
- from
147
122
  end
123
+ flattened
148
124
  end
149
125
 
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]
156
- else
157
- ["#{attr_name}-#{name}", value]
126
+ def merge_all_attrs(hashes)
127
+ merged = {}
128
+ hashes.each do |hash|
129
+ hash.each do |key, value|
130
+ key = key.to_s
131
+ case key
132
+ when 'id'.freeze, 'class'.freeze, 'data'.freeze
133
+ merged[key] ||= []
134
+ merged[key] << value
135
+ else
136
+ merged[key] = value
137
+ end
158
138
  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
139
  end
140
+ merged
172
141
  end
173
142
 
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?
143
+ def build_boolean!(escape_attrs, quote, format, buf, key, value)
144
+ case value
145
+ when true
146
+ case format
147
+ when :xhtml
148
+ buf << " #{key}=#{quote}#{key}#{quote}"
193
149
  else
194
- ref.id
150
+ buf << " #{key}"
195
151
  end
196
- id = "#{class_name}_#{ref_id || 'new'}"
197
- if prefix
198
- class_name = "#{ prefix }_#{ class_name}"
199
- id = "#{ prefix }_#{ id }"
152
+ when false, nil
153
+ # omitted
154
+ else
155
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
200
156
  end
201
-
202
- { 'id'.freeze => id, 'class'.freeze => class_name }
203
157
  end
204
158
 
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
159
+ def escape_html(escape_attrs, str)
160
+ if escape_attrs
161
+ Haml::Util.escape_html(str)
162
+ else
163
+ str
164
+ end
216
165
  end
217
166
  end
167
+ else
168
+ # Haml::AttributeBuilder.build
169
+ # Haml::AttributeBuilder.build_id
170
+ # Haml::AttributeBuilder.build_class
171
+ # Haml::AttributeBuilder.build_data
172
+ # Haml::AttributeBuilder.build_aria
173
+ require 'haml/haml'
218
174
  end
219
175
  end