ultra-lite-tool 0.0.1

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/haml-7.2.0/CHANGELOG.md +1705 -0
  3. data/haml-7.2.0/FAQ.md +147 -0
  4. data/haml-7.2.0/Gemfile +25 -0
  5. data/haml-7.2.0/MIT-LICENSE +20 -0
  6. data/haml-7.2.0/README.md +205 -0
  7. data/haml-7.2.0/REFERENCE.md +1309 -0
  8. data/haml-7.2.0/Rakefile +48 -0
  9. data/haml-7.2.0/bin/bench +66 -0
  10. data/haml-7.2.0/bin/console +10 -0
  11. data/haml-7.2.0/bin/ruby +3 -0
  12. data/haml-7.2.0/bin/setup +7 -0
  13. data/haml-7.2.0/bin/stackprof +27 -0
  14. data/haml-7.2.0/bin/test +24 -0
  15. data/haml-7.2.0/exe/haml +6 -0
  16. data/haml-7.2.0/haml.gemspec +45 -0
  17. data/haml-7.2.0/lib/haml/ambles.rb +20 -0
  18. data/haml-7.2.0/lib/haml/attribute_builder.rb +162 -0
  19. data/haml-7.2.0/lib/haml/attribute_compiler.rb +133 -0
  20. data/haml-7.2.0/lib/haml/attribute_parser.rb +116 -0
  21. data/haml-7.2.0/lib/haml/cli.rb +154 -0
  22. data/haml-7.2.0/lib/haml/compiler/children_compiler.rb +155 -0
  23. data/haml-7.2.0/lib/haml/compiler/comment_compiler.rb +51 -0
  24. data/haml-7.2.0/lib/haml/compiler/doctype_compiler.rb +52 -0
  25. data/haml-7.2.0/lib/haml/compiler/script_compiler.rb +114 -0
  26. data/haml-7.2.0/lib/haml/compiler/silent_script_compiler.rb +24 -0
  27. data/haml-7.2.0/lib/haml/compiler/tag_compiler.rb +76 -0
  28. data/haml-7.2.0/lib/haml/compiler.rb +97 -0
  29. data/haml-7.2.0/lib/haml/dynamic_merger.rb +67 -0
  30. data/haml-7.2.0/lib/haml/engine.rb +59 -0
  31. data/haml-7.2.0/lib/haml/error.rb +66 -0
  32. data/haml-7.2.0/lib/haml/escape.rb +13 -0
  33. data/haml-7.2.0/lib/haml/escape_any.rb +21 -0
  34. data/haml-7.2.0/lib/haml/filters/base.rb +12 -0
  35. data/haml-7.2.0/lib/haml/filters/cdata.rb +20 -0
  36. data/haml-7.2.0/lib/haml/filters/coffee.rb +17 -0
  37. data/haml-7.2.0/lib/haml/filters/css.rb +33 -0
  38. data/haml-7.2.0/lib/haml/filters/erb.rb +10 -0
  39. data/haml-7.2.0/lib/haml/filters/escaped.rb +22 -0
  40. data/haml-7.2.0/lib/haml/filters/javascript.rb +33 -0
  41. data/haml-7.2.0/lib/haml/filters/less.rb +20 -0
  42. data/haml-7.2.0/lib/haml/filters/markdown.rb +11 -0
  43. data/haml-7.2.0/lib/haml/filters/plain.rb +29 -0
  44. data/haml-7.2.0/lib/haml/filters/preserve.rb +22 -0
  45. data/haml-7.2.0/lib/haml/filters/ruby.rb +10 -0
  46. data/haml-7.2.0/lib/haml/filters/sass.rb +15 -0
  47. data/haml-7.2.0/lib/haml/filters/scss.rb +15 -0
  48. data/haml-7.2.0/lib/haml/filters/text_base.rb +25 -0
  49. data/haml-7.2.0/lib/haml/filters/tilt_base.rb +59 -0
  50. data/haml-7.2.0/lib/haml/filters.rb +75 -0
  51. data/haml-7.2.0/lib/haml/force_escape.rb +29 -0
  52. data/haml-7.2.0/lib/haml/helpers.rb +15 -0
  53. data/haml-7.2.0/lib/haml/html.rb +22 -0
  54. data/haml-7.2.0/lib/haml/identity.rb +13 -0
  55. data/haml-7.2.0/lib/haml/object_ref.rb +35 -0
  56. data/haml-7.2.0/lib/haml/parser.rb +991 -0
  57. data/haml-7.2.0/lib/haml/rails_helpers.rb +53 -0
  58. data/haml-7.2.0/lib/haml/rails_template.rb +62 -0
  59. data/haml-7.2.0/lib/haml/railtie.rb +10 -0
  60. data/haml-7.2.0/lib/haml/ruby_expression.rb +32 -0
  61. data/haml-7.2.0/lib/haml/string_splitter.rb +140 -0
  62. data/haml-7.2.0/lib/haml/template.rb +20 -0
  63. data/haml-7.2.0/lib/haml/temple_line_counter.rb +31 -0
  64. data/haml-7.2.0/lib/haml/util.rb +262 -0
  65. data/haml-7.2.0/lib/haml/version.rb +4 -0
  66. data/haml-7.2.0/lib/haml/whitespace.rb +8 -0
  67. data/haml-7.2.0/lib/haml.rb +9 -0
  68. data/ultra-lite-tool.gemspec +11 -0
  69. metadata +107 -0
@@ -0,0 +1,48 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib' << 'test'
6
+ files = Dir['test/haml/**/*_test.rb']
7
+ t.ruby_opts = %w[-rtest_helper]
8
+ t.test_files = files
9
+ t.verbose = true
10
+ end
11
+ task :test
12
+
13
+ desc 'bench task for CI'
14
+ task :bench do
15
+ if ENV['SLIM_BENCH'] == '1'
16
+ cmd = %w[bundle exec ruby benchmark/slim/run-benchmarks.rb]
17
+ else
18
+ cmd = ['bin/bench', 'bench', ('-c' if ENV['COMPILE'] == '1'), *ENV['TEMPLATE'].split(',')].compact
19
+ end
20
+ exit system(*cmd)
21
+ end
22
+
23
+ namespace :doc do
24
+ task :sass do
25
+ require 'sass'
26
+ Dir["yard/default/**/*.sass"].each do |sass|
27
+ File.open(sass.gsub(/sass$/, 'css'), 'w') do |f|
28
+ f.write(Sass::Engine.new(File.read(sass)).render)
29
+ end
30
+ end
31
+ end
32
+
33
+ desc "List all undocumented methods and classes."
34
+ task :undocumented do
35
+ command = 'yard --list --query '
36
+ command << '"object.docstring.blank? && '
37
+ command << '!(object.type == :method && object.is_alias?)"'
38
+ sh command
39
+ end
40
+ end
41
+
42
+ desc "Generate documentation"
43
+ task(:doc => 'doc:sass') {sh "yard"}
44
+
45
+ desc "Generate documentation incrementally"
46
+ task(:redoc) {sh "yard -c"}
47
+
48
+ task default: :test
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'haml'
5
+ require 'thor'
6
+ require 'benchmark/ips'
7
+ require_relative '../benchmark/utils/benchmark_ips_extension'
8
+
9
+ class Bench < Thor
10
+ class_option :show_template, type: :boolean, aliases: ['-t']
11
+
12
+ desc 'bench HAML', 'Benchmark haml template'
13
+ option :compile, type: :boolean, aliases: ['-c']
14
+ option :show_code, type: :boolean, aliases: ['-s']
15
+ def bench(*files)
16
+ files.each { |file| render(file) }
17
+ files.each { |file| compile(file) if options[:compile] }
18
+ files.each { |file| code(file) if options[:show_code] }
19
+ end
20
+
21
+ desc 'compile HAML', 'Benchmark compilation'
22
+ def compile(file)
23
+ puts "#{?= * 49}\n Compilation: #{file}\n#{?= * 49}"
24
+ haml = File.read(file)
25
+
26
+ Benchmark.ips do |x|
27
+ x.report("haml v#{Haml::VERSION}") { Haml::Engine.new.call(haml) }
28
+ x.compare!
29
+ end
30
+ end
31
+
32
+ desc 'render HAML', 'Benchmark rendering'
33
+ def render(file)
34
+ puts "#{?= * 49}\n Rendering: #{file}\n#{?= * 49}"
35
+ haml = File.read(file)
36
+ puts haml + "\n" if options[:show_template]
37
+ object = Object.new
38
+ ruby_file = file.gsub(/\.haml\z/, '.rb')
39
+ if File.exist?(ruby_file)
40
+ object.instance_eval(File.read(ruby_file))
41
+ end
42
+
43
+ object.instance_eval "def haml; #{Haml::Engine.new.call(haml)}; end"
44
+
45
+ Benchmark.ips do |x|
46
+ x.report("haml v#{Haml::VERSION}") { object.haml }
47
+ x.compare!
48
+ end
49
+ end
50
+
51
+ desc 'code HAML', 'Show compiled code'
52
+ def code(file)
53
+ haml = File.read(file)
54
+ puts "\n#{?= * 49}\n Haml Source: #{file}\n#{?= * 49}"
55
+ puts Haml::Engine.new.call(haml)
56
+ end
57
+
58
+ private
59
+
60
+ def method_missing(*args)
61
+ return super if args.length > 1
62
+ render(args.first.to_s)
63
+ end
64
+ end
65
+
66
+ Bench.start
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'haml'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require 'irb'
10
+ IRB.start
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ bundle exec ruby -Ilib:test -rtest_helper $@
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'haml'
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
+ Haml::Engine.new.call(haml)
24
+ StackProf.stop
25
+
26
+ report = StackProf::Report.new(StackProf.results)
27
+ report.print_text(false)
@@ -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
@@ -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)
@@ -0,0 +1,45 @@
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'
5
+
6
+ Gem::Specification.new do |spec|
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']
11
+
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'
16
+
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']
21
+
22
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
23
+
24
+ spec.metadata["changelog_uri"] = "https://github.com/haml/haml/releases"
25
+ spec.metadata["source_code_uri"] = "https://github.com/haml/haml"
26
+
27
+ spec.required_ruby_version = '>= 3.2.0'
28
+
29
+ spec.add_dependency 'temple', '>= 0.8.2'
30
+ spec.add_dependency 'thor'
31
+ spec.add_dependency 'tilt'
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 'less'
38
+ spec.add_development_dependency 'minitest-reporters', '~> 1.1'
39
+ spec.add_development_dependency 'rails', '>= 4.0'
40
+ spec.add_development_dependency 'rake'
41
+ spec.add_development_dependency 'sass'
42
+ spec.add_development_dependency 'slim'
43
+ spec.add_development_dependency 'string_template'
44
+ spec.add_development_dependency 'unindent'
45
+ 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
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
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, format, *hash[key])
20
+ when 'aria'
21
+ buf << build_aria(escape_attrs, quote, format, *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}"
26
+ end
27
+ end
28
+ buf.join
29
+ end
30
+
31
+ def build_id(escape_attrs, *values)
32
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
33
+ end
34
+
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 ''
47
+ end
48
+ return escape_html(escape_attrs, value)
49
+ end
50
+
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.flatten.select { |v| v }
58
+ when value
59
+ classes << value.to_s
60
+ end
61
+ end
62
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
63
+ end
64
+
65
+ def build_data(escape_attrs, quote, format, *hashes)
66
+ build_data_attribute(:data, escape_attrs, quote, format, *hashes)
67
+ end
68
+
69
+ def build_aria(escape_attrs, quote, format, *hashes)
70
+ build_data_attribute(:aria, escape_attrs, quote, format, *hashes)
71
+ end
72
+
73
+ private
74
+
75
+ def build_data_attribute(key, escape_attrs, quote, format, *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
+ build_boolean!(escape_attrs, quote, format, attrs, key, value)
88
+ when nil, false
89
+ # noop
90
+ else
91
+ attrs << " #{key}=#{quote}#{escape_html(escape_attrs, value.to_s)}#{quote}"
92
+ end
93
+ end
94
+ attrs.join
95
+ end
96
+
97
+ def flatten_attributes(attributes)
98
+ flattened = {}
99
+
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.tr('_', '-')}"] = v
109
+ end
110
+ end
111
+ else
112
+ flattened[key] = value if value
113
+ end
114
+ end
115
+ flattened
116
+ end
117
+
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
130
+ else
131
+ merged[key] = value
132
+ end
133
+ end
134
+ end
135
+ merged
136
+ end
137
+
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}"
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
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+ require 'haml/attribute_builder'
3
+ require 'haml/attribute_parser'
4
+ require 'haml/ruby_expression'
5
+
6
+ module Haml
7
+ # The list of boolean attributes. You may add custom attributes to this constant.
8
+ BOOLEAN_ATTRIBUTES = %w[disabled readonly multiple checked autobuffer
9
+ autoplay controls loop selected hidden scoped async
10
+ defer reversed ismap seamless muted required
11
+ autofocus novalidate formnovalidate open pubdate
12
+ itemscope allowfullscreen default inert sortable
13
+ truespeed typemustmatch download]
14
+
15
+ class AttributeCompiler
16
+ def initialize(identity, options)
17
+ @identity = identity
18
+ @quote = options[:attr_quote]
19
+ @format = options[:format]
20
+ @escape_attrs = options[:escape_attrs]
21
+ end
22
+
23
+ def compile(node)
24
+ hashes = []
25
+ if node.value[:object_ref] != :nil || !AttributeParser.available?
26
+ return runtime_compile(node)
27
+ end
28
+ [node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
29
+ hash = AttributeParser.parse(attribute_str)
30
+ return runtime_compile(node) if hash.nil? || hash.any? { |_key, value| value.empty? }
31
+ hashes << hash
32
+ end
33
+ static_compile(node.value[:attributes], hashes)
34
+ end
35
+
36
+ private
37
+
38
+ def runtime_compile(node)
39
+ attrs = []
40
+ attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
41
+
42
+ args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect, node.value[:object_ref]] + attrs
43
+ [:html, :attrs, [:dynamic, "::Haml::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
44
+ end
45
+
46
+ def static_compile(static_hash, dynamic_hashes)
47
+ temple = [:html, :attrs]
48
+ keys = [*static_hash.keys, *dynamic_hashes.map(&:keys).flatten].uniq.sort
49
+ keys.each do |key|
50
+ values = [[:static, static_hash[key]], *dynamic_hashes.map { |h| [:dynamic, h[key]] }]
51
+ values.select! { |_, exp| exp != nil }
52
+
53
+ case key
54
+ when 'id'
55
+ compile_id!(temple, key, values)
56
+ when 'class'
57
+ compile_class!(temple, key, values)
58
+ when 'data', 'aria'
59
+ compile_data!(temple, key, values)
60
+ when *BOOLEAN_ATTRIBUTES, /\Adata-/, /\Aaria-/
61
+ compile_boolean!(temple, key, values)
62
+ else
63
+ compile_common!(temple, key, values)
64
+ end
65
+ end
66
+ temple
67
+ end
68
+
69
+ def compile_id!(temple, key, values)
70
+ build_code = attribute_builder(:id, values)
71
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
72
+ temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
73
+ else
74
+ temple << [:html, :attr, key, [:dynamic, build_code]]
75
+ end
76
+ end
77
+
78
+ def compile_class!(temple, key, values)
79
+ build_code = attribute_builder(:class, values)
80
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
81
+ temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
82
+ else
83
+ temple << [:html, :attr, key, [:dynamic, build_code]]
84
+ end
85
+ end
86
+
87
+ def compile_data!(temple, key, values)
88
+ args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect, values.map { |v| literal_for(v) }]
89
+ build_code = "::Haml::AttributeBuilder.build_#{key}(#{args.join(', ')})"
90
+
91
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
92
+ temple << [:static, eval(build_code).to_s]
93
+ else
94
+ temple << [:dynamic, build_code]
95
+ end
96
+ end
97
+
98
+ def compile_boolean!(temple, key, values)
99
+ exp = literal_for(values.last)
100
+
101
+ if values.last.first == :static || Temple::StaticAnalyzer.static?(exp)
102
+ value = eval(exp)
103
+ case value
104
+ when true then temple << [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]
105
+ when false, nil
106
+ else temple << [:html, :attr, key, [:fescape, @escape_attrs, [:static, value.to_s]]]
107
+ end
108
+ else
109
+ var = @identity.generate
110
+ temple << [
111
+ :case, "(#{var} = (#{exp}))",
112
+ ['true', [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]],
113
+ ['false, nil', [:multi]],
114
+ [:else, [:multi, [:static, " #{key}=#{@quote}"], [:fescape, @escape_attrs, [:dynamic, var]], [:static, @quote]]],
115
+ ]
116
+ end
117
+ end
118
+
119
+ def compile_common!(temple, key, values)
120
+ temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
121
+ end
122
+
123
+ def attribute_builder(type, values)
124
+ args = [@escape_attrs.inspect, *values.map { |v| literal_for(v) }]
125
+ "::Haml::AttributeBuilder.build_#{type}(#{args.join(', ')})"
126
+ end
127
+
128
+ def literal_for(value)
129
+ type, exp = value
130
+ type == :static ? "#{exp.inspect}.freeze" : exp
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+ require 'haml/ruby_expression'
3
+
4
+ module Haml
5
+ class AttributeParser
6
+ class ParseSkip < StandardError
7
+ end
8
+
9
+ # @return [TrueClass, FalseClass] - return true if AttributeParser.parse can be used.
10
+ def self.available?
11
+ # TruffleRuby doesn't have Ripper.lex
12
+ defined?(Ripper) && Ripper.respond_to?(:lex) && Temple::StaticAnalyzer.available?
13
+ end
14
+
15
+ def self.parse(text)
16
+ self.new.parse(text)
17
+ end
18
+
19
+ def parse(text)
20
+ exp = wrap_bracket(text)
21
+ return if Temple::StaticAnalyzer.syntax_error?(exp)
22
+
23
+ hash = {}
24
+ tokens = Ripper.lex(exp)[1..-2] || []
25
+ each_attr(tokens) do |attr_tokens|
26
+ key = parse_key!(attr_tokens)
27
+ hash[key] = attr_tokens.map { |t| t[2] }.join.strip
28
+ end
29
+ hash
30
+ rescue ParseSkip
31
+ nil
32
+ end
33
+
34
+ private
35
+
36
+ def wrap_bracket(text)
37
+ text = text.strip
38
+ return text if text[0] == '{'
39
+ "{#{text}}"
40
+ end
41
+
42
+ def parse_key!(tokens)
43
+ _, type, str = tokens.shift
44
+ case type
45
+ when :on_sp
46
+ parse_key!(tokens)
47
+ when :on_label
48
+ str.tr(':', '')
49
+ when :on_symbeg
50
+ _, _, key = tokens.shift
51
+ assert_type!(tokens.shift, :on_tstring_end) if str != ':'
52
+ skip_until_hash_rocket!(tokens)
53
+ key
54
+ when :on_tstring_beg
55
+ _, _, key = tokens.shift
56
+ next_token = tokens.shift
57
+ unless next_token[1] == :on_label_end
58
+ assert_type!(next_token, :on_tstring_end)
59
+ skip_until_hash_rocket!(tokens)
60
+ end
61
+ key
62
+ else
63
+ raise ParseSkip
64
+ end
65
+ end
66
+
67
+ def assert_type!(token, type)
68
+ raise ParseSkip if token[1] != type
69
+ end
70
+
71
+ def skip_until_hash_rocket!(tokens)
72
+ until tokens.empty?
73
+ _, type, str = tokens.shift
74
+ break if type == :on_op && str == '=>'
75
+ end
76
+ end
77
+
78
+ def each_attr(tokens)
79
+ attr_tokens = []
80
+ open_tokens = Hash.new { |h, k| h[k] = 0 }
81
+
82
+ tokens.each do |token|
83
+ _, type, _ = token
84
+ case type
85
+ when :on_comma
86
+ if open_tokens.values.all?(&:zero?)
87
+ yield(attr_tokens)
88
+ attr_tokens = []
89
+ next
90
+ end
91
+ when :on_lbracket
92
+ open_tokens[:array] += 1
93
+ when :on_rbracket
94
+ open_tokens[:array] -= 1
95
+ when :on_lbrace
96
+ open_tokens[:block] += 1
97
+ when :on_rbrace
98
+ open_tokens[:block] -= 1
99
+ when :on_lparen
100
+ open_tokens[:paren] += 1
101
+ when :on_rparen
102
+ open_tokens[:paren] -= 1
103
+ when :on_embexpr_beg
104
+ open_tokens[:embexpr] += 1
105
+ when :on_embexpr_end
106
+ open_tokens[:embexpr] -= 1
107
+ when :on_sp
108
+ next if attr_tokens.empty?
109
+ end
110
+
111
+ attr_tokens << token
112
+ end
113
+ yield(attr_tokens) unless attr_tokens.empty?
114
+ end
115
+ end
116
+ end