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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/test.yml +15 -15
- data/.gitignore +16 -16
- data/.yardopts +0 -3
- data/CHANGELOG.md +168 -4
- data/FAQ.md +1 -1
- data/Gemfile +21 -10
- data/MIT-LICENSE +1 -1
- data/README.md +22 -34
- data/REFERENCE.md +95 -159
- data/Rakefile +15 -82
- data/bin/bench +66 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/haml +6 -0
- data/haml.gemspec +34 -36
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +127 -184
- data/lib/haml/attribute_compiler.rb +90 -194
- data/lib/haml/attribute_parser.rb +92 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +155 -0
- data/lib/haml/compiler/comment_compiler.rb +51 -0
- data/lib/haml/compiler/doctype_compiler.rb +52 -0
- data/lib/haml/compiler/script_compiler.rb +114 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +63 -296
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +48 -227
- data/lib/haml/error.rb +5 -4
- data/lib/haml/escape.rb +13 -0
- data/lib/haml/escape_any.rb +21 -0
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +59 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escape.rb +29 -0
- data/lib/haml/helpers.rb +3 -697
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +35 -0
- data/lib/haml/parser.rb +158 -23
- data/lib/haml/rails_helpers.rb +53 -0
- data/lib/haml/rails_template.rb +62 -0
- data/lib/haml/railtie.rb +3 -46
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +140 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +19 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml/whitespace.rb +8 -0
- data/lib/haml.rb +8 -20
- metadata +188 -50
- data/.gitmodules +0 -3
- data/TODO +0 -24
- data/benchmark.rb +0 -70
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -182
- data/lib/haml/escapable.rb +0 -77
- data/lib/haml/exec.rb +0 -347
- data/lib/haml/generator.rb +0 -42
- data/lib/haml/helpers/action_view_extensions.rb +0 -60
- data/lib/haml/helpers/action_view_mods.rb +0 -132
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
- data/lib/haml/helpers/safe_erubi_template.rb +0 -20
- data/lib/haml/helpers/safe_erubis_template.rb +0 -33
- data/lib/haml/helpers/xss_mods.rb +0 -114
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -54
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -124
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
data/haml.gemspec
CHANGED
@@ -1,45 +1,43 @@
|
|
1
|
-
|
2
|
-
|
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
|
6
|
-
spec.
|
7
|
-
spec.
|
8
|
-
spec.
|
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.
|
12
|
-
spec.
|
13
|
-
|
14
|
-
|
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.
|
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.
|
29
|
-
spec.add_dependency 'tilt'
|
22
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
30
23
|
|
31
|
-
spec.
|
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.
|
38
|
-
|
39
|
-
|
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
|
data/lib/haml/ambles.rb
ADDED
@@ -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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
65
|
+
def build_data(escape_attrs, quote, *hashes)
|
66
|
+
build_data_attribute(:data, escape_attrs, quote, *hashes)
|
67
|
+
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
97
|
+
def flatten_attributes(attributes)
|
98
|
+
flattened = {}
|
118
99
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
112
|
+
flattened[key] = value if value
|
147
113
|
end
|
148
114
|
end
|
115
|
+
flattened
|
116
|
+
end
|
149
117
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
[
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|