haml 5.2.0 → 6.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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 -16
  5. data/CHANGELOG.md +26 -1
  6. data/Gemfile +18 -11
  7. data/MIT-LICENSE +1 -1
  8. data/REFERENCE.md +19 -8
  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 +140 -129
  24. data/lib/haml/attribute_compiler.rb +85 -192
  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 +208 -50
  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 -34
  68. data/lib/haml/temple_line_counter.rb +2 -1
  69. data/lib/haml/util.rb +18 -16
  70. data/lib/haml/version.rb +1 -2
  71. data/lib/haml.rb +8 -20
  72. metadata +216 -62
  73. data/.gitmodules +0 -3
  74. data/.travis.yml +0 -72
  75. data/.yardopts +0 -22
  76. data/TODO +0 -24
  77. data/benchmark.rb +0 -70
  78. data/bin/haml +0 -9
  79. data/lib/haml/.gitattributes +0 -1
  80. data/lib/haml/buffer.rb +0 -238
  81. data/lib/haml/exec.rb +0 -347
  82. data/lib/haml/generator.rb +0 -42
  83. data/lib/haml/helpers/action_view_extensions.rb +0 -60
  84. data/lib/haml/helpers/action_view_mods.rb +0 -132
  85. data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  86. data/lib/haml/helpers/safe_erubi_template.rb +0 -20
  87. data/lib/haml/helpers/safe_erubis_template.rb +0 -33
  88. data/lib/haml/helpers/xss_mods.rb +0 -114
  89. data/lib/haml/options.rb +0 -273
  90. data/lib/haml/plugin.rb +0 -37
  91. data/lib/haml/sass_rails_filter.rb +0 -47
  92. data/lib/haml/template/options.rb +0 -27
  93. data/lib/haml/temple_engine.rb +0 -123
  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
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', '0.17.1' # Locked to this version due to https://github.com/codeclimate/test-reporter/issues/418
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,164 +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_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)
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}"
21
34
  end
22
35
  end
36
+ buf.join
37
+ end
23
38
 
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'
39
+ def build_id(escape_attrs, *values)
40
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
41
+ end
29
42
 
30
- if value == true
31
- next " #{attr}" if is_html
32
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
33
- elsif value == false
34
- 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 ''
35
55
  end
36
-
37
- value =
38
- if escape_attrs == :once
39
- Haml::Helpers.escape_once_without_haml_xss(value.to_s)
40
- elsif escape_attrs
41
- Haml::Helpers.html_escape_without_haml_xss(value.to_s)
42
- else
43
- value.to_s
44
- end
45
- " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
56
+ return escape_html(escape_attrs, value)
46
57
  end
47
- result.compact!
48
- result.sort!
49
- result.join
50
- end
51
58
 
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
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
63
69
  end
64
- !value.nil? && !value.empty? && value
70
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
65
71
  end
66
72
 
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
73
+ def build_data(escape_attrs, quote, *hashes)
74
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
85
75
  end
86
76
 
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
77
+ def build_aria(escape_attrs, quote, *hashes)
78
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
96
79
  end
97
80
 
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")
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}"
102
100
  end
103
101
  end
102
+ attrs.join
104
103
  end
105
104
 
106
- private
105
+ def flatten_attributes(attributes)
106
+ flattened = {}
107
107
 
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
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
124
121
  end
125
- merged_id
126
- elsif key == 'class'
127
- merged_class = filter_and_join(from, ' ')
128
- if to && merged_class
129
- merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
130
- elsif to || merged_class
131
- 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
132
138
  end
133
- merged_class
134
- else
135
- from
136
139
  end
140
+ merged
137
141
  end
138
142
 
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]
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}"
145
149
  else
146
- ["#{attr_name}-#{name}", value]
150
+ buf << " #{key}"
147
151
  end
148
- end]
152
+ when false, nil
153
+ # omitted
154
+ else
155
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
156
+ end
149
157
  end
150
158
 
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)
159
+ def escape_html(escape_attrs, str)
160
+ if escape_attrs
161
+ Haml::Util.escape_html(str)
162
+ else
163
+ str
160
164
  end
161
165
  end
162
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'
163
174
  end
164
175
  end