haml 5.1.2 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/test.yml +35 -0
  4. data/.gitignore +16 -15
  5. data/.yardopts +0 -3
  6. data/CHANGELOG.md +137 -0
  7. data/Gemfile +18 -14
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +5 -6
  10. data/REFERENCE.md +103 -150
  11. data/Rakefile +48 -88
  12. data/bin/bench +66 -0
  13. data/bin/console +11 -0
  14. data/bin/ruby +3 -0
  15. data/bin/setup +7 -0
  16. data/bin/stackprof +27 -0
  17. data/bin/test +24 -0
  18. data/exe/haml +6 -0
  19. data/ext/haml/extconf.rb +10 -0
  20. data/ext/haml/haml.c +537 -0
  21. data/ext/haml/hescape.c +108 -0
  22. data/ext/haml/hescape.h +20 -0
  23. data/haml.gemspec +39 -36
  24. data/lib/haml/ambles.rb +20 -0
  25. data/lib/haml/attribute_builder.rb +139 -129
  26. data/lib/haml/attribute_compiler.rb +86 -182
  27. data/lib/haml/attribute_parser.rb +92 -126
  28. data/lib/haml/cli.rb +154 -0
  29. data/lib/haml/compiler/children_compiler.rb +155 -0
  30. data/lib/haml/compiler/comment_compiler.rb +51 -0
  31. data/lib/haml/compiler/doctype_compiler.rb +46 -0
  32. data/lib/haml/compiler/script_compiler.rb +114 -0
  33. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  34. data/lib/haml/compiler/tag_compiler.rb +76 -0
  35. data/lib/haml/compiler.rb +63 -296
  36. data/lib/haml/dynamic_merger.rb +67 -0
  37. data/lib/haml/engine.rb +48 -227
  38. data/lib/haml/error.rb +5 -4
  39. data/lib/haml/escape.rb +13 -0
  40. data/lib/haml/escape_any.rb +21 -0
  41. data/lib/haml/filters/base.rb +12 -0
  42. data/lib/haml/filters/cdata.rb +20 -0
  43. data/lib/haml/filters/coffee.rb +17 -0
  44. data/lib/haml/filters/css.rb +33 -0
  45. data/lib/haml/filters/erb.rb +10 -0
  46. data/lib/haml/filters/escaped.rb +22 -0
  47. data/lib/haml/filters/javascript.rb +33 -0
  48. data/lib/haml/filters/less.rb +20 -0
  49. data/lib/haml/filters/markdown.rb +11 -0
  50. data/lib/haml/filters/plain.rb +29 -0
  51. data/lib/haml/filters/preserve.rb +22 -0
  52. data/lib/haml/filters/ruby.rb +10 -0
  53. data/lib/haml/filters/sass.rb +15 -0
  54. data/lib/haml/filters/scss.rb +15 -0
  55. data/lib/haml/filters/text_base.rb +25 -0
  56. data/lib/haml/filters/tilt_base.rb +59 -0
  57. data/lib/haml/filters.rb +54 -378
  58. data/lib/haml/force_escape.rb +29 -0
  59. data/lib/haml/helpers.rb +3 -691
  60. data/lib/haml/html.rb +22 -0
  61. data/lib/haml/identity.rb +13 -0
  62. data/lib/haml/object_ref.rb +35 -0
  63. data/lib/haml/parser.rb +189 -26
  64. data/lib/haml/rails_helpers.rb +53 -0
  65. data/lib/haml/rails_template.rb +57 -0
  66. data/lib/haml/railtie.rb +3 -41
  67. data/lib/haml/ruby_expression.rb +32 -0
  68. data/lib/haml/string_splitter.rb +140 -0
  69. data/lib/haml/template.rb +15 -34
  70. data/lib/haml/temple_line_counter.rb +2 -1
  71. data/lib/haml/util.rb +19 -16
  72. data/lib/haml/version.rb +1 -2
  73. data/lib/haml/whitespace.rb +8 -0
  74. data/lib/haml.rb +8 -20
  75. metadata +222 -52
  76. data/.gitmodules +0 -3
  77. data/.travis.yml +0 -97
  78. data/TODO +0 -24
  79. data/benchmark.rb +0 -70
  80. data/bin/haml +0 -9
  81. data/lib/haml/.gitattributes +0 -1
  82. data/lib/haml/buffer.rb +0 -238
  83. data/lib/haml/escapable.rb +0 -50
  84. data/lib/haml/exec.rb +0 -347
  85. data/lib/haml/generator.rb +0 -42
  86. data/lib/haml/helpers/action_view_extensions.rb +0 -60
  87. data/lib/haml/helpers/action_view_mods.rb +0 -132
  88. data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  89. data/lib/haml/helpers/safe_erubi_template.rb +0 -20
  90. data/lib/haml/helpers/safe_erubis_template.rb +0 -33
  91. data/lib/haml/helpers/xss_mods.rb +0 -111
  92. data/lib/haml/options.rb +0 -273
  93. data/lib/haml/plugin.rb +0 -37
  94. data/lib/haml/sass_rails_filter.rb +0 -47
  95. data/lib/haml/template/options.rb +0 -27
  96. data/lib/haml/temple_engine.rb +0 -123
  97. data/yard/default/.gitignore +0 -1
  98. data/yard/default/fulldoc/html/css/common.sass +0 -15
  99. data/yard/default/layout/html/footer.erb +0 -12
data/haml.gemspec CHANGED
@@ -1,44 +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/master/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'
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
35
28
 
36
- spec.description = <<-END
37
- Haml (HTML Abstraction Markup Language) is a layer on top of HTML or XML that's
38
- designed to express the structure of documents in a non-repetitive, elegant, and
39
- easy way by using indentation rather than closing tags and allowing Ruby to be
40
- embedded with ease. It was originally envisioned as a plugin for Ruby on Rails,
41
- but it can function as a stand-alone templating engine.
42
- END
29
+ spec.add_dependency 'temple', '>= 0.8.2'
30
+ spec.add_dependency 'thor'
31
+ spec.add_dependency 'tilt'
43
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'
44
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,164 +1,174 @@
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
+ # For JRuby, TruffleRuby, and Wasm, fallback to Ruby implementation.
13
+ if /java|wasm/ === RUBY_PLATFORM || RUBY_ENGINE == 'truffleruby'
8
14
  class << self
9
- def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
10
- # @TODO this is an absolutely ridiculous amount of arguments. At least
11
- # some of this needs to be moved into an instance method.
12
- join_char = hyphenate_data_attrs ? '-' : '_'
13
-
14
- attributes.each do |key, value|
15
- if value.is_a?(Hash)
16
- data_attributes = attributes.delete(key)
17
- data_attributes = flatten_data_attributes(data_attributes, '', join_char)
18
- data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
19
- verify_attribute_names!(data_attributes.keys)
20
- attributes = data_attributes.merge(attributes)
15
+ def build(escape_attrs, quote, format, boolean_attributes, object_ref, *hashes)
16
+ hashes << Haml::ObjectRef.parse(object_ref) if object_ref
17
+ buf = []
18
+ hash = merge_all_attrs(hashes)
19
+
20
+ keys = hash.keys.sort!
21
+ keys.each do |key|
22
+ case key
23
+ when 'id'.freeze
24
+ buf << " id=#{quote}#{build_id(escape_attrs, *hash[key])}#{quote}"
25
+ when 'class'.freeze
26
+ buf << " class=#{quote}#{build_class(escape_attrs, *hash[key])}#{quote}"
27
+ when 'data'.freeze
28
+ buf << build_data(escape_attrs, quote, *hash[key])
29
+ when *boolean_attributes, /\Adata-/
30
+ build_boolean!(escape_attrs, quote, format, buf, key, hash[key])
31
+ else
32
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, hash[key].to_s)}#{quote}"
21
33
  end
22
34
  end
35
+ buf.join
36
+ end
23
37
 
24
- result = attributes.collect do |attr, value|
25
- next if value.nil?
26
-
27
- value = filter_and_join(value, ' ') if attr == 'class'
28
- value = filter_and_join(value, '_') if attr == 'id'
38
+ def build_id(escape_attrs, *values)
39
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
40
+ end
29
41
 
30
- if value == true
31
- next " #{attr}" if is_html
32
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
33
- elsif value == false
34
- next
42
+ def build_class(escape_attrs, *values)
43
+ if values.size == 1
44
+ value = values.first
45
+ case
46
+ when value.is_a?(String)
47
+ # noop
48
+ when value.is_a?(Array)
49
+ value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
50
+ when value
51
+ value = value.to_s
52
+ else
53
+ return ''
35
54
  end
36
-
37
- value =
38
- if escape_attrs == :once
39
- Haml::Helpers.escape_once(value.to_s)
40
- elsif escape_attrs
41
- Haml::Helpers.html_escape(value.to_s)
42
- else
43
- value.to_s
44
- end
45
- " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
55
+ return escape_html(escape_attrs, value)
46
56
  end
47
- result.compact!
48
- result.sort!
49
- result.join
50
- end
51
57
 
52
- # @return [String, nil]
53
- def filter_and_join(value, separator)
54
- return '' if (value.respond_to?(:empty?) && value.empty?)
55
-
56
- if value.is_a?(Array)
57
- value = value.flatten
58
- value.map! {|item| item ? item.to_s : nil}
59
- value.compact!
60
- value = value.join(separator)
61
- else
62
- value = value ? value.to_s : nil
58
+ classes = []
59
+ values.each do |value|
60
+ case
61
+ when value.is_a?(String)
62
+ classes += value.split(' ')
63
+ when value.is_a?(Array)
64
+ classes += value.select { |v| v }
65
+ when value
66
+ classes << value.to_s
67
+ end
63
68
  end
64
- !value.nil? && !value.empty? && value
69
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
65
70
  end
66
71
 
67
- # Merges two attribute hashes.
68
- # This is the same as `to.merge!(from)`,
69
- # except that it merges id, class, and data attributes.
70
- #
71
- # ids are concatenated with `"_"`,
72
- # and classes are concatenated with `" "`.
73
- # data hashes are simply merged.
74
- #
75
- # Destructively modifies `to`.
76
- #
77
- # @param to [{String => String,Hash}] The attribute hash to merge into
78
- # @param from [{String => Object}] The attribute hash to merge from
79
- # @return [{String => String,Hash}] `to`, after being merged
80
- def merge_attributes!(to, from)
81
- from.keys.each do |key|
82
- to[key] = merge_value(key, to[key], from[key])
83
- end
84
- to
72
+ def build_data(escape_attrs, quote, *hashes)
73
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
85
74
  end
86
75
 
87
- # Merge multiple values to one attribute value. No destructive operation.
88
- #
89
- # @param key [String]
90
- # @param values [Array<Object>]
91
- # @return [String,Hash]
92
- def merge_values(key, *values)
93
- values.inject(nil) do |to, from|
94
- merge_value(key, to, from)
95
- end
76
+ def build_aria(escape_attrs, quote, *hashes)
77
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
96
78
  end
97
79
 
98
- def verify_attribute_names!(attribute_names)
99
- attribute_names.each do |attribute_name|
100
- if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
101
- raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
80
+ private
81
+
82
+ def build_data_attribute(key, escape_attrs, quote, *hashes)
83
+ attrs = []
84
+ if hashes.size > 1 && hashes.all? { |h| h.is_a?(Hash) }
85
+ data_value = merge_all_attrs(hashes)
86
+ else
87
+ data_value = hashes.last
88
+ end
89
+ hash = flatten_attributes(key => data_value)
90
+
91
+ hash.sort_by(&:first).each do |key, value|
92
+ case value
93
+ when true
94
+ attrs << " #{key}"
95
+ when nil, false
96
+ # noop
97
+ else
98
+ attrs << " #{key}=#{quote}#{escape_html(escape_attrs, value.to_s)}#{quote}"
102
99
  end
103
100
  end
101
+ attrs.join
104
102
  end
105
103
 
106
- private
104
+ def flatten_attributes(attributes)
105
+ flattened = {}
107
106
 
108
- # Merge a couple of values to one attribute value. No destructive operation.
109
- #
110
- # @param to [String,Hash,nil]
111
- # @param from [Object]
112
- # @return [String,Hash]
113
- def merge_value(key, to, from)
114
- if from.kind_of?(Hash) || to.kind_of?(Hash)
115
- from = { nil => from } if !from.is_a?(Hash)
116
- to = { nil => to } if !to.is_a?(Hash)
117
- to.merge(from)
118
- elsif key == 'id'
119
- merged_id = filter_and_join(from, '_')
120
- if to && merged_id
121
- merged_id = "#{to}_#{merged_id}"
122
- elsif to || merged_id
123
- merged_id ||= to
107
+ attributes.each do |key, value|
108
+ case value
109
+ when attributes
110
+ when Hash
111
+ flatten_attributes(value).each do |k, v|
112
+ if k.nil?
113
+ flattened[key] = v
114
+ else
115
+ flattened["#{key}-#{k.to_s.gsub(/_/, '-')}"] = v
116
+ end
117
+ end
118
+ else
119
+ flattened[key] = value if value
124
120
  end
125
- merged_id
126
- elsif key == 'class'
127
- merged_class = filter_and_join(from, ' ')
128
- if to && merged_class
129
- merged_class = (merged_class.split(' ') | to.split(' ')).sort.join(' ')
130
- elsif to || merged_class
131
- merged_class ||= to
121
+ end
122
+ flattened
123
+ end
124
+
125
+ def merge_all_attrs(hashes)
126
+ merged = {}
127
+ hashes.each do |hash|
128
+ hash.each do |key, value|
129
+ key = key.to_s
130
+ case key
131
+ when 'id'.freeze, 'class'.freeze, 'data'.freeze
132
+ merged[key] ||= []
133
+ merged[key] << value
134
+ else
135
+ merged[key] = value
136
+ end
132
137
  end
133
- merged_class
134
- else
135
- from
136
138
  end
139
+ merged
137
140
  end
138
141
 
139
- def build_data_keys(data_hash, hyphenate, attr_name="data")
140
- Hash[data_hash.map do |name, value|
141
- if name == nil
142
- [attr_name, value]
143
- elsif hyphenate
144
- ["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
142
+ def build_boolean!(escape_attrs, quote, format, buf, key, value)
143
+ case value
144
+ when true
145
+ case format
146
+ when :xhtml
147
+ buf << " #{key}=#{quote}#{key}#{quote}"
145
148
  else
146
- ["#{attr_name}-#{name}", value]
149
+ buf << " #{key}"
147
150
  end
148
- end]
151
+ when false, nil
152
+ # omitted
153
+ else
154
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
155
+ end
149
156
  end
150
157
 
151
- def flatten_data_attributes(data, key, join_char, seen = [])
152
- return {key => data} unless data.is_a?(Hash)
153
-
154
- return {key => nil} if seen.include? data.object_id
155
- seen << data.object_id
156
-
157
- data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
158
- joined = key == '' ? k : [key, k].join(join_char)
159
- hash.merge! flatten_data_attributes(v, joined, join_char, seen)
158
+ def escape_html(escape_attrs, str)
159
+ if escape_attrs
160
+ Haml::Util.escape_html(str)
161
+ else
162
+ str
160
163
  end
161
164
  end
162
165
  end
166
+ else
167
+ # Haml::AttributeBuilder.build
168
+ # Haml::AttributeBuilder.build_id
169
+ # Haml::AttributeBuilder.build_class
170
+ # Haml::AttributeBuilder.build_data
171
+ # Haml::AttributeBuilder.build_aria
172
+ require 'haml/haml'
163
173
  end
164
174
  end