haml 5.0.4 → 6.0.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +40 -0
  4. data/.gitignore +16 -15
  5. data/CHANGELOG.md +62 -1
  6. data/Gemfile +18 -14
  7. data/MIT-LICENSE +2 -2
  8. data/README.md +4 -5
  9. data/REFERENCE.md +46 -12
  10. data/Rakefile +93 -103
  11. data/bin/bench +66 -0
  12. data/bin/console +11 -0
  13. data/bin/ruby +3 -0
  14. data/bin/setup +7 -0
  15. data/bin/stackprof +27 -0
  16. data/bin/test +24 -0
  17. data/exe/haml +6 -0
  18. data/ext/haml/extconf.rb +10 -0
  19. data/ext/haml/haml.c +537 -0
  20. data/ext/haml/hescape.c +108 -0
  21. data/ext/haml/hescape.h +20 -0
  22. data/haml.gemspec +39 -30
  23. data/lib/haml/ambles.rb +20 -0
  24. data/lib/haml/attribute_builder.rb +140 -128
  25. data/lib/haml/attribute_compiler.rb +86 -181
  26. data/lib/haml/attribute_parser.rb +86 -124
  27. data/lib/haml/cli.rb +154 -0
  28. data/lib/haml/compiler/children_compiler.rb +126 -0
  29. data/lib/haml/compiler/comment_compiler.rb +39 -0
  30. data/lib/haml/compiler/doctype_compiler.rb +46 -0
  31. data/lib/haml/compiler/script_compiler.rb +116 -0
  32. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  33. data/lib/haml/compiler/tag_compiler.rb +76 -0
  34. data/lib/haml/compiler.rb +64 -298
  35. data/lib/haml/dynamic_merger.rb +67 -0
  36. data/lib/haml/engine.rb +43 -219
  37. data/lib/haml/error.rb +29 -27
  38. data/lib/haml/escapable.rb +6 -42
  39. data/lib/haml/filters/base.rb +12 -0
  40. data/lib/haml/filters/cdata.rb +20 -0
  41. data/lib/haml/filters/coffee.rb +17 -0
  42. data/lib/haml/filters/css.rb +33 -0
  43. data/lib/haml/filters/erb.rb +10 -0
  44. data/lib/haml/filters/escaped.rb +22 -0
  45. data/lib/haml/filters/javascript.rb +33 -0
  46. data/lib/haml/filters/less.rb +20 -0
  47. data/lib/haml/filters/markdown.rb +11 -0
  48. data/lib/haml/filters/plain.rb +29 -0
  49. data/lib/haml/filters/preserve.rb +22 -0
  50. data/lib/haml/filters/ruby.rb +10 -0
  51. data/lib/haml/filters/sass.rb +15 -0
  52. data/lib/haml/filters/scss.rb +15 -0
  53. data/lib/haml/filters/text_base.rb +25 -0
  54. data/lib/haml/filters/tilt_base.rb +49 -0
  55. data/lib/haml/filters.rb +55 -378
  56. data/lib/haml/force_escapable.rb +29 -0
  57. data/lib/haml/helpers.rb +4 -696
  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 +208 -43
  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 -40
  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 -33
  68. data/lib/haml/temple_line_counter.rb +2 -0
  69. data/lib/haml/util.rb +23 -21
  70. data/lib/haml/version.rb +1 -1
  71. data/lib/haml.rb +8 -19
  72. metadata +222 -50
  73. data/.gitmodules +0 -3
  74. data/.travis.yml +0 -54
  75. data/.yardopts +0 -23
  76. data/TODO +0 -24
  77. data/benchmark.rb +0 -66
  78. data/bin/haml +0 -9
  79. data/lib/haml/.gitattributes +0 -1
  80. data/lib/haml/buffer.rb +0 -235
  81. data/lib/haml/exec.rb +0 -348
  82. data/lib/haml/generator.rb +0 -41
  83. data/lib/haml/helpers/action_view_extensions.rb +0 -59
  84. data/lib/haml/helpers/action_view_mods.rb +0 -129
  85. data/lib/haml/helpers/action_view_xss_mods.rb +0 -59
  86. data/lib/haml/helpers/safe_erubi_template.rb +0 -19
  87. data/lib/haml/helpers/safe_erubis_template.rb +0 -32
  88. data/lib/haml/helpers/xss_mods.rb +0 -110
  89. data/lib/haml/options.rb +0 -273
  90. data/lib/haml/plugin.rb +0 -34
  91. data/lib/haml/sass_rails_filter.rb +0 -46
  92. data/lib/haml/template/options.rb +0 -26
  93. data/lib/haml/temple_engine.rb +0 -121
  94. data/yard/default/.gitignore +0 -1
  95. data/yard/default/fulldoc/html/css/common.sass +0 -15
  96. data/yard/default/layout/html/footer.erb +0 -12
@@ -0,0 +1,20 @@
1
+ #ifndef HESCAPE_H
2
+ #define HESCAPE_H
3
+
4
+ #include <sys/types.h>
5
+
6
+ /*
7
+ * Replace characters according to the following rules.
8
+ * Note that this function can handle only ASCII-compatible string.
9
+ *
10
+ * " => &quot;
11
+ * & => &amp;
12
+ * ' => &#39;
13
+ * < => &lt;
14
+ * > => &gt;
15
+ *
16
+ * @return size of dest. If it's larger than len, dest is required to be freed.
17
+ */
18
+ extern size_t hesc_escape_html(char **dest, const char *src, size_t size);
19
+
20
+ #endif
data/haml.gemspec CHANGED
@@ -1,38 +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', 'norman@njclarke.com']
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
- readmes = Dir['*'].reject{ |x| x =~ /(^|[^.a-z])[a-z]+/ || x == "TODO" }
12
- spec.executables = ['haml']
13
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
- f.match(%r{\Atest/})
15
- end
16
- spec.homepage = 'http://haml.info/'
17
- spec.has_rdoc = false
18
- spec.license = "MIT"
19
-
20
- 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'
21
16
 
22
- spec.add_dependency 'temple', '>= 0.8.0'
23
- 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']
24
21
 
25
- spec.add_development_dependency 'rails', '>= 4.0.0'
26
- spec.add_development_dependency 'rbench'
27
- spec.add_development_dependency 'minitest', '>= 4.0'
28
- 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
29
28
 
30
- spec.description = <<-END
31
- Haml (HTML Abstraction Markup Language) is a layer on top of HTML or XML that's
32
- designed to express the structure of documents in a non-repetitive, elegant, and
33
- easy way by using indentation rather than closing tags and allowing Ruby to be
34
- embedded with ease. It was originally envisioned as a plugin for Ruby on Rails,
35
- but it can function as a stand-alone templating engine.
36
- END
29
+ spec.add_dependency 'temple', '>= 0.8.2'
30
+ spec.add_dependency 'thor'
31
+ spec.add_dependency 'tilt'
37
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'
38
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,163 +1,175 @@
1
1
  # frozen_string_literal: true
2
- module Haml
3
- module AttributeBuilder
4
- # https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
5
- INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
6
-
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'
7
15
  class << self
8
- def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
9
- # @TODO this is an absolutely ridiculous amount of arguments. At least
10
- # some of this needs to be moved into an instance method.
11
- join_char = hyphenate_data_attrs ? '-' : '_'
12
-
13
- attributes.each do |key, value|
14
- if value.is_a?(Hash)
15
- data_attributes = attributes.delete(key)
16
- data_attributes = flatten_data_attributes(data_attributes, '', join_char)
17
- data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
18
- verify_attribute_names!(data_attributes.keys)
19
- attributes = data_attributes.merge(attributes)
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}"
20
34
  end
21
35
  end
36
+ buf.join
37
+ end
22
38
 
23
- result = attributes.collect do |attr, value|
24
- next if value.nil?
25
-
26
- value = filter_and_join(value, ' ') if attr == 'class'
27
- value = filter_and_join(value, '_') if attr == 'id'
39
+ def build_id(escape_attrs, *values)
40
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
41
+ end
28
42
 
29
- if value == true
30
- next " #{attr}" if is_html
31
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
32
- elsif value == false
33
- next
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 ''
34
55
  end
35
-
36
- value =
37
- if escape_attrs == :once
38
- Haml::Helpers.escape_once(value.to_s)
39
- elsif escape_attrs
40
- Haml::Helpers.html_escape(value.to_s)
41
- else
42
- value.to_s
43
- end
44
- " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
56
+ return escape_html(escape_attrs, value)
45
57
  end
46
- result.compact!
47
- result.sort!
48
- result.join
49
- end
50
-
51
- # @return [String, nil]
52
- def filter_and_join(value, separator)
53
- return '' if (value.respond_to?(:empty?) && value.empty?)
54
58
 
55
- if value.is_a?(Array)
56
- value = value.flatten
57
- value.map! {|item| item ? item.to_s : nil}
58
- value.compact!
59
- value = value.join(separator)
60
- else
61
- value = value ? value.to_s : nil
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
68
+ end
62
69
  end
63
- !value.nil? && !value.empty? && value
70
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
64
71
  end
65
72
 
66
- # Merges two attribute hashes.
67
- # This is the same as `to.merge!(from)`,
68
- # except that it merges id, class, and data attributes.
69
- #
70
- # ids are concatenated with `"_"`,
71
- # and classes are concatenated with `" "`.
72
- # data hashes are simply merged.
73
- #
74
- # Destructively modifies `to`.
75
- #
76
- # @param to [{String => String,Hash}] The attribute hash to merge into
77
- # @param from [{String => Object}] The attribute hash to merge from
78
- # @return [{String => String,Hash}] `to`, after being merged
79
- def merge_attributes!(to, from)
80
- from.keys.each do |key|
81
- to[key] = merge_value(key, to[key], from[key])
82
- end
83
- to
73
+ def build_data(escape_attrs, quote, *hashes)
74
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
84
75
  end
85
76
 
86
- # Merge multiple values to one attribute value. No destructive operation.
87
- #
88
- # @param key [String]
89
- # @param values [Array<Object>]
90
- # @return [String,Hash]
91
- def merge_values(key, *values)
92
- values.inject(nil) do |to, from|
93
- merge_value(key, to, from)
94
- end
77
+ def build_aria(escape_attrs, quote, *hashes)
78
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
95
79
  end
96
80
 
97
- def verify_attribute_names!(attribute_names)
98
- attribute_names.each do |attribute_name|
99
- if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
100
- raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
81
+ private
82
+
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}"
101
100
  end
102
101
  end
102
+ attrs.join
103
103
  end
104
104
 
105
- private
105
+ def flatten_attributes(attributes)
106
+ flattened = {}
106
107
 
107
- # Merge a couple of values to one attribute value. No destructive operation.
108
- #
109
- # @param to [String,Hash,nil]
110
- # @param from [Object]
111
- # @return [String,Hash]
112
- def merge_value(key, to, from)
113
- if from.kind_of?(Hash) || to.kind_of?(Hash)
114
- from = { nil => from } if !from.is_a?(Hash)
115
- to = { nil => to } if !to.is_a?(Hash)
116
- to.merge(from)
117
- elsif key == 'id'
118
- merged_id = filter_and_join(from, '_')
119
- if to && merged_id
120
- merged_id = "#{to}_#{merged_id}"
121
- elsif to || merged_id
122
- merged_id ||= 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
123
121
  end
124
- merged_id
125
- elsif key == 'class'
126
- merged_class = filter_and_join(from, ' ')
127
- if to && merged_class
128
- merged_class = (merged_class.split(' ') | to.split(' ')).sort.join(' ')
129
- elsif to || merged_class
130
- merged_class ||= to
122
+ end
123
+ flattened
124
+ end
125
+
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
131
138
  end
132
- merged_class
133
- else
134
- from
135
139
  end
140
+ merged
136
141
  end
137
142
 
138
- def build_data_keys(data_hash, hyphenate, attr_name="data")
139
- Hash[data_hash.map do |name, value|
140
- if name == nil
141
- [attr_name, value]
142
- elsif hyphenate
143
- ["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
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}"
144
149
  else
145
- ["#{attr_name}-#{name}", value]
150
+ buf << " #{key}"
146
151
  end
147
- end]
152
+ when false, nil
153
+ # omitted
154
+ else
155
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
156
+ end
148
157
  end
149
158
 
150
- def flatten_data_attributes(data, key, join_char, seen = [])
151
- return {key => data} unless data.is_a?(Hash)
152
-
153
- return {key => nil} if seen.include? data.object_id
154
- seen << data.object_id
155
-
156
- data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
157
- joined = key == '' ? k : [key, k].join(join_char)
158
- hash.merge! flatten_data_attributes(v, joined, join_char, seen)
159
+ def escape_html(escape_attrs, str)
160
+ if escape_attrs
161
+ Haml::Util.escape_html(str)
162
+ else
163
+ str
159
164
  end
160
165
  end
161
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'
162
174
  end
163
175
  end