haml 5.1.2 → 6.3.0

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 +3 -0
  3. data/.github/workflows/test.yml +36 -0
  4. data/.gitignore +16 -15
  5. data/.yardopts +0 -3
  6. data/CHANGELOG.md +189 -1
  7. data/FAQ.md +1 -1
  8. data/Gemfile +20 -12
  9. data/MIT-LICENSE +1 -1
  10. data/README.md +10 -17
  11. data/REFERENCE.md +129 -164
  12. data/Rakefile +15 -89
  13. data/bin/bench +66 -0
  14. data/bin/console +11 -0
  15. data/bin/ruby +3 -0
  16. data/bin/setup +7 -0
  17. data/bin/stackprof +27 -0
  18. data/bin/test +24 -0
  19. data/exe/haml +6 -0
  20. data/haml.gemspec +34 -35
  21. data/lib/haml/ambles.rb +20 -0
  22. data/lib/haml/attribute_builder.rb +131 -133
  23. data/lib/haml/attribute_compiler.rb +91 -182
  24. data/lib/haml/attribute_parser.rb +92 -126
  25. data/lib/haml/cli.rb +154 -0
  26. data/lib/haml/compiler/children_compiler.rb +155 -0
  27. data/lib/haml/compiler/comment_compiler.rb +51 -0
  28. data/lib/haml/compiler/doctype_compiler.rb +52 -0
  29. data/lib/haml/compiler/script_compiler.rb +114 -0
  30. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  31. data/lib/haml/compiler/tag_compiler.rb +76 -0
  32. data/lib/haml/compiler.rb +63 -296
  33. data/lib/haml/dynamic_merger.rb +67 -0
  34. data/lib/haml/engine.rb +48 -227
  35. data/lib/haml/error.rb +5 -4
  36. data/lib/haml/escape.rb +13 -0
  37. data/lib/haml/escape_any.rb +21 -0
  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 +59 -0
  54. data/lib/haml/filters.rb +54 -378
  55. data/lib/haml/force_escape.rb +29 -0
  56. data/lib/haml/helpers.rb +3 -691
  57. data/lib/haml/html.rb +22 -0
  58. data/lib/haml/identity.rb +13 -0
  59. data/lib/haml/object_ref.rb +35 -0
  60. data/lib/haml/parser.rb +190 -27
  61. data/lib/haml/rails_helpers.rb +53 -0
  62. data/lib/haml/rails_template.rb +62 -0
  63. data/lib/haml/railtie.rb +3 -41
  64. data/lib/haml/ruby_expression.rb +32 -0
  65. data/lib/haml/string_splitter.rb +140 -0
  66. data/lib/haml/template.rb +15 -34
  67. data/lib/haml/temple_line_counter.rb +2 -1
  68. data/lib/haml/util.rb +20 -16
  69. data/lib/haml/version.rb +1 -2
  70. data/lib/haml/whitespace.rb +8 -0
  71. data/lib/haml.rb +8 -20
  72. metadata +205 -53
  73. data/.gitmodules +0 -3
  74. data/.travis.yml +0 -97
  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 -238
  80. data/lib/haml/escapable.rb +0 -50
  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 -111
  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/bin/stackprof ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'hamlit'
5
+ require 'stackprof'
6
+
7
+ def open_flamegraph(report)
8
+ temp = `mktemp /tmp/stackflame-XXXXXXXX`.strip
9
+ data_path = "#{temp}.js"
10
+ system("mv #{temp} #{data_path}")
11
+
12
+ File.open(data_path, 'w') do |f|
13
+ report.print_flamegraph(f)
14
+ end
15
+
16
+ viewer_path = File.join(`bundle show stackprof`.strip, 'lib/stackprof/flamegraph/viewer.html')
17
+ url = "file://#{viewer_path}?data=#{data_path}"
18
+ system(%Q[osascript -e 'open location "#{url}"'])
19
+ end
20
+
21
+ haml = File.read(ARGV.first)
22
+ StackProf.start(mode: :wall, interval: 1, raw: false)
23
+ Hamlit::Engine.new.call(haml)
24
+ StackProf.stop
25
+
26
+ report = StackProf::Report.new(StackProf.results)
27
+ report.print_text(false)
data/bin/test ADDED
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ VERSIONS=(
4
+ 2.1.10
5
+ 2.2.5
6
+ 2.3.1
7
+ )
8
+
9
+ set -e
10
+ trap 'echo "${VERSIONS[2]}" > .ruby-version' 0
11
+
12
+ function test_with() {
13
+ version=$1
14
+ rbenv local $version
15
+ if ! bundle check > /dev/null; then
16
+ bundle install
17
+ fi
18
+ ruby -v
19
+ bundle exec rake test
20
+ }
21
+
22
+ for version in ${VERSIONS[@]}; do
23
+ test_with $version
24
+ done
data/exe/haml ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../lib', __dir__)
4
+ require 'haml/cli'
5
+
6
+ Haml::CLI.start(ARGV)
data/haml.gemspec CHANGED
@@ -1,44 +1,43 @@
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
- }
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.required_ruby_version = '>= 2.0.0'
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.add_dependency 'temple', '>= 0.8.0'
29
- spec.add_dependency 'tilt'
22
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
30
23
 
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'
24
+ spec.required_ruby_version = '>= 2.1.0'
35
25
 
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
26
+ spec.add_dependency 'temple', '>= 0.8.2'
27
+ spec.add_dependency 'thor'
28
+ spec.add_dependency 'tilt'
43
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'
44
43
  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,161 @@
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
-
8
- 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)
21
- end
22
- end
23
-
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'
29
-
30
- if value == true
31
- next " #{attr}" if is_html
32
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
33
- elsif value == false
34
- next
35
- 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}"
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}"
46
26
  end
47
- result.compact!
48
- result.sort!
49
- result.join
50
27
  end
28
+ buf.join
29
+ end
51
30
 
52
- # @return [String, nil]
53
- def filter_and_join(value, separator)
54
- return '' if (value.respond_to?(:empty?) && value.empty?)
31
+ def build_id(escape_attrs, *values)
32
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
33
+ end
55
34
 
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)
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
61
45
  else
62
- value = value ? value.to_s : nil
46
+ return ''
63
47
  end
64
- !value.nil? && !value.empty? && value
48
+ return escape_html(escape_attrs, value)
65
49
  end
66
50
 
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])
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
83
60
  end
84
- to
85
61
  end
62
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
63
+ end
86
64
 
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
96
- end
65
+ def build_data(escape_attrs, quote, *hashes)
66
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
67
+ end
97
68
 
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")
102
- end
69
+ def build_aria(escape_attrs, quote, *hashes)
70
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
71
+ end
72
+
73
+ private
74
+
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
81
+ end
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}"
103
92
  end
104
93
  end
94
+ attrs.join
95
+ end
105
96
 
106
- private
97
+ def flatten_attributes(attributes)
98
+ flattened = {}
107
99
 
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
124
- 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
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
132
110
  end
133
- merged_class
134
111
  else
135
- from
112
+ flattened[key] = value if value
136
113
  end
137
114
  end
115
+ flattened
116
+ end
138
117
 
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]
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
145
130
  else
146
- ["#{attr_name}-#{name}", value]
131
+ merged[key] = value
147
132
  end
148
- end]
133
+ end
149
134
  end
135
+ merged
136
+ end
150
137
 
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)
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}"
160
146
  end
147
+ when false, nil
148
+ # omitted
149
+ else
150
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
151
+ end
152
+ end
153
+
154
+ def escape_html(escape_attrs, str)
155
+ if escape_attrs
156
+ Haml::Util.escape_html(str)
157
+ else
158
+ str
161
159
  end
162
160
  end
163
161
  end