hamlit 2.9.3

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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +45 -0
  4. data/CHANGELOG.md +676 -0
  5. data/Gemfile +28 -0
  6. data/LICENSE.txt +44 -0
  7. data/README.md +150 -0
  8. data/REFERENCE.md +266 -0
  9. data/Rakefile +117 -0
  10. data/benchmark/boolean_attribute.haml +6 -0
  11. data/benchmark/class_attribute.haml +5 -0
  12. data/benchmark/common_attribute.haml +3 -0
  13. data/benchmark/data_attribute.haml +4 -0
  14. data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
  15. data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
  16. data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
  17. data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
  18. data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
  19. data/benchmark/dynamic_boolean_attribute.haml +4 -0
  20. data/benchmark/etc/attribute_builder.haml +5 -0
  21. data/benchmark/etc/real_sample.haml +888 -0
  22. data/benchmark/etc/real_sample.rb +11 -0
  23. data/benchmark/etc/static_analyzer.haml +1 -0
  24. data/benchmark/etc/string_interpolation.haml +2 -0
  25. data/benchmark/etc/tags.haml +3 -0
  26. data/benchmark/etc/tags_loop.haml +2 -0
  27. data/benchmark/ext/build_data.rb +17 -0
  28. data/benchmark/ext/build_id.rb +13 -0
  29. data/benchmark/id_attribute.haml +3 -0
  30. data/benchmark/plain.haml +4 -0
  31. data/benchmark/script.haml +4 -0
  32. data/benchmark/slim/LICENSE +21 -0
  33. data/benchmark/slim/context.rb +11 -0
  34. data/benchmark/slim/run-benchmarks.rb +94 -0
  35. data/benchmark/slim/view.erb +23 -0
  36. data/benchmark/slim/view.haml +18 -0
  37. data/benchmark/slim/view.slim +17 -0
  38. data/benchmark/utils/benchmark_ips_extension.rb +43 -0
  39. data/bin/bench +77 -0
  40. data/bin/console +11 -0
  41. data/bin/ruby +3 -0
  42. data/bin/setup +7 -0
  43. data/bin/stackprof +27 -0
  44. data/bin/test +24 -0
  45. data/exe/hamlit +6 -0
  46. data/ext/hamlit/extconf.rb +10 -0
  47. data/ext/hamlit/hamlit.c +553 -0
  48. data/ext/hamlit/hescape.c +108 -0
  49. data/ext/hamlit/hescape.h +20 -0
  50. data/hamlit.gemspec +45 -0
  51. data/lib/hamlit.rb +11 -0
  52. data/lib/hamlit/attribute_builder.rb +173 -0
  53. data/lib/hamlit/attribute_compiler.rb +123 -0
  54. data/lib/hamlit/attribute_parser.rb +110 -0
  55. data/lib/hamlit/cli.rb +130 -0
  56. data/lib/hamlit/compiler.rb +97 -0
  57. data/lib/hamlit/compiler/children_compiler.rb +112 -0
  58. data/lib/hamlit/compiler/comment_compiler.rb +36 -0
  59. data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
  60. data/lib/hamlit/compiler/script_compiler.rb +102 -0
  61. data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
  62. data/lib/hamlit/compiler/tag_compiler.rb +74 -0
  63. data/lib/hamlit/engine.rb +37 -0
  64. data/lib/hamlit/error.rb +15 -0
  65. data/lib/hamlit/escapable.rb +13 -0
  66. data/lib/hamlit/filters.rb +75 -0
  67. data/lib/hamlit/filters/base.rb +12 -0
  68. data/lib/hamlit/filters/cdata.rb +20 -0
  69. data/lib/hamlit/filters/coffee.rb +17 -0
  70. data/lib/hamlit/filters/css.rb +33 -0
  71. data/lib/hamlit/filters/erb.rb +10 -0
  72. data/lib/hamlit/filters/escaped.rb +22 -0
  73. data/lib/hamlit/filters/javascript.rb +33 -0
  74. data/lib/hamlit/filters/less.rb +20 -0
  75. data/lib/hamlit/filters/markdown.rb +10 -0
  76. data/lib/hamlit/filters/plain.rb +29 -0
  77. data/lib/hamlit/filters/preserve.rb +22 -0
  78. data/lib/hamlit/filters/ruby.rb +10 -0
  79. data/lib/hamlit/filters/sass.rb +15 -0
  80. data/lib/hamlit/filters/scss.rb +15 -0
  81. data/lib/hamlit/filters/text_base.rb +25 -0
  82. data/lib/hamlit/filters/tilt_base.rb +49 -0
  83. data/lib/hamlit/force_escapable.rb +29 -0
  84. data/lib/hamlit/helpers.rb +15 -0
  85. data/lib/hamlit/html.rb +14 -0
  86. data/lib/hamlit/identity.rb +13 -0
  87. data/lib/hamlit/object_ref.rb +30 -0
  88. data/lib/hamlit/parser.rb +49 -0
  89. data/lib/hamlit/parser/MIT-LICENSE +20 -0
  90. data/lib/hamlit/parser/README.md +30 -0
  91. data/lib/hamlit/parser/haml_buffer.rb +348 -0
  92. data/lib/hamlit/parser/haml_compiler.rb +553 -0
  93. data/lib/hamlit/parser/haml_error.rb +61 -0
  94. data/lib/hamlit/parser/haml_helpers.rb +727 -0
  95. data/lib/hamlit/parser/haml_options.rb +286 -0
  96. data/lib/hamlit/parser/haml_parser.rb +800 -0
  97. data/lib/hamlit/parser/haml_util.rb +288 -0
  98. data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
  99. data/lib/hamlit/rails_helpers.rb +51 -0
  100. data/lib/hamlit/rails_template.rb +59 -0
  101. data/lib/hamlit/railtie.rb +10 -0
  102. data/lib/hamlit/ruby_expression.rb +32 -0
  103. data/lib/hamlit/string_splitter.rb +88 -0
  104. data/lib/hamlit/template.rb +28 -0
  105. data/lib/hamlit/utils.rb +18 -0
  106. data/lib/hamlit/version.rb +4 -0
  107. metadata +361 -0
@@ -0,0 +1,108 @@
1
+ #include <stdio.h>
2
+ #include <string.h>
3
+ #include <stdlib.h>
4
+ #include "hescape.h"
5
+
6
+ static const char *ESCAPED_STRING[] = {
7
+ "",
8
+ "&quot;",
9
+ "&amp;",
10
+ "&#39;",
11
+ "&lt;",
12
+ "&gt;",
13
+ };
14
+
15
+ // This is strlen(ESCAPED_STRING[x]) optimized specially.
16
+ // Mapping: 1 => 6, 2 => 5, 3 => 5, 4 => 4, 5 => 4
17
+ #define ESC_LEN(x) ((13 - x) / 2)
18
+
19
+ /*
20
+ * Given ASCII-compatible character, return index of ESCAPED_STRING.
21
+ *
22
+ * " (34) => 1 (&quot;)
23
+ * & (38) => 2 (&amp;)
24
+ * ' (39) => 3 (&#39;)
25
+ * < (60) => 4 (&lt;)
26
+ * > (62) => 5 (&gt;)
27
+ */
28
+ static const char HTML_ESCAPE_TABLE[] = {
29
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31
+ 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0,
32
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5, 0,
33
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45
+ };
46
+
47
+ static char*
48
+ ensure_allocated(char *buf, size_t size, size_t *asize)
49
+ {
50
+ size_t new_size;
51
+
52
+ if (size < *asize)
53
+ return buf;
54
+
55
+ if (*asize == 0) {
56
+ new_size = size;
57
+ } else {
58
+ new_size = *asize;
59
+ }
60
+
61
+ // Increase buffer size by 1.5x if realloced multiple times.
62
+ while (new_size < size)
63
+ new_size = (new_size << 1) - (new_size >> 1);
64
+
65
+ // Round allocation up to multiple of 8.
66
+ new_size = (new_size + 7) & ~7;
67
+
68
+ *asize = new_size;
69
+ return realloc(buf, new_size);
70
+ }
71
+
72
+ size_t
73
+ hesc_escape_html(char **dest, const char *buf, size_t size)
74
+ {
75
+ size_t asize = 0, esc_i = 0, esize = 0, i = 0, rbuf_end = 0;
76
+ const char *esc;
77
+ char *rbuf = NULL;
78
+
79
+ while (i < size) {
80
+ // Loop here to skip non-escaped characters fast.
81
+ while (i < size && (esc_i = HTML_ESCAPE_TABLE[(unsigned char)buf[i]]) == 0)
82
+ i++;
83
+
84
+ if (i < size && esc_i) {
85
+ esc = ESCAPED_STRING[esc_i];
86
+ rbuf = ensure_allocated(rbuf, sizeof(char) * (size + esize + ESC_LEN(esc_i) + 1), &asize);
87
+
88
+ // Copy pending characters and escaped string.
89
+ memmove(rbuf + rbuf_end, buf + (rbuf_end - esize), i - (rbuf_end - esize));
90
+ memmove(rbuf + i + esize, esc, ESC_LEN(esc_i));
91
+ rbuf_end = i + esize + ESC_LEN(esc_i);
92
+ esize += ESC_LEN(esc_i) - 1;
93
+ }
94
+ i++;
95
+ }
96
+
97
+ if (rbuf_end == 0) {
98
+ // Return given buf and size if there are no escaped characters.
99
+ *dest = (char *)buf;
100
+ return size;
101
+ } else {
102
+ // Copy pending characters including NULL character.
103
+ memmove(rbuf + rbuf_end, buf + (rbuf_end - esize), (size + 1) - (rbuf_end - esize));
104
+
105
+ *dest = rbuf;
106
+ return size + esize;
107
+ }
108
+ }
@@ -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
@@ -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 'hamlit/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'hamlit'
8
+ spec.version = Hamlit::VERSION
9
+ spec.authors = ['Takashi Kokubun']
10
+ spec.email = ['takashikkbn@gmail.com']
11
+
12
+ spec.summary = %q{High Performance Haml Implementation}
13
+ spec.description = %q{High Performance Haml Implementation}
14
+ spec.homepage = 'https://github.com/k0kubun/hamlit'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|sample)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ if /java/ === RUBY_PLATFORM
23
+ spec.platform = 'java'
24
+ else
25
+ spec.extensions = ['ext/hamlit/extconf.rb']
26
+ spec.required_ruby_version = '>= 2.1.0'
27
+ end
28
+
29
+ spec.add_dependency 'temple', '>= 0.8.0'
30
+ spec.add_dependency 'thor'
31
+ spec.add_dependency 'tilt'
32
+
33
+ spec.add_development_dependency 'bundler'
34
+ spec.add_development_dependency 'coffee-script'
35
+ spec.add_development_dependency 'erubi'
36
+ spec.add_development_dependency 'haml', '>= 5'
37
+ spec.add_development_dependency 'less'
38
+ spec.add_development_dependency 'minitest-reporters', '~> 1.1'
39
+ spec.add_development_dependency 'rails', '>= 4.0.0'
40
+ spec.add_development_dependency 'rake', '~> 10.0'
41
+ spec.add_development_dependency 'rake-compiler'
42
+ spec.add_development_dependency 'sass'
43
+ spec.add_development_dependency 'slim'
44
+ spec.add_development_dependency 'unindent'
45
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/engine'
3
+ require 'hamlit/error'
4
+ require 'hamlit/version'
5
+ require 'hamlit/template'
6
+
7
+ begin
8
+ require 'rails'
9
+ require 'hamlit/railtie'
10
+ rescue LoadError
11
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/object_ref'
3
+
4
+ module Hamlit::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
+ if /java/ === RUBY_PLATFORM # JRuby
13
+ class << self
14
+ def build(escape_attrs, quote, format, object_ref, *hashes)
15
+ hashes << Hamlit::ObjectRef.parse(object_ref) if object_ref
16
+ buf = []
17
+ hash = merge_all_attrs(hashes)
18
+
19
+ keys = hash.keys.sort!
20
+ keys.each do |key|
21
+ case key
22
+ when 'id'.freeze
23
+ buf << " id=#{quote}#{build_id(escape_attrs, *hash[key])}#{quote}"
24
+ when 'class'.freeze
25
+ buf << " class=#{quote}#{build_class(escape_attrs, *hash[key])}#{quote}"
26
+ when 'data'.freeze
27
+ buf << build_data(escape_attrs, quote, *hash[key])
28
+ when *BOOLEAN_ATTRIBUTES, /\Adata-/
29
+ build_boolean!(escape_attrs, quote, format, buf, key, hash[key])
30
+ else
31
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, hash[key].to_s)}#{quote}"
32
+ end
33
+ end
34
+ buf.join
35
+ end
36
+
37
+ def build_id(escape_attrs, *values)
38
+ escape_html(escape_attrs, values.flatten.select { |v| v }.join('_'))
39
+ end
40
+
41
+ def build_class(escape_attrs, *values)
42
+ if values.size == 1
43
+ value = values.first
44
+ case
45
+ when value.is_a?(String)
46
+ # noop
47
+ when value.is_a?(Array)
48
+ value = value.flatten.select { |v| v }.map(&:to_s).sort.uniq.join(' ')
49
+ when value
50
+ value = value.to_s
51
+ else
52
+ return ''
53
+ end
54
+ return escape_html(escape_attrs, value)
55
+ end
56
+
57
+ classes = []
58
+ values.each do |value|
59
+ case
60
+ when value.is_a?(String)
61
+ classes += value.split(' ')
62
+ when value.is_a?(Array)
63
+ classes += value.select { |v| v }
64
+ when value
65
+ classes << value.to_s
66
+ end
67
+ end
68
+ escape_html(escape_attrs, classes.map(&:to_s).sort.uniq.join(' '))
69
+ end
70
+
71
+ def build_data(escape_attrs, quote, *hashes)
72
+ build_data_attribute(:data, escape_attrs, quote, *hashes)
73
+ end
74
+
75
+ def build_aria(escape_attrs, quote, *hashes)
76
+ build_data_attribute(:aria, escape_attrs, quote, *hashes)
77
+ end
78
+
79
+ private
80
+
81
+ def build_data_attribute(key, escape_attrs, quote, *hashes)
82
+ attrs = []
83
+ if hashes.size > 1 && hashes.all? { |h| h.is_a?(Hash) }
84
+ data_value = merge_all_attrs(hashes)
85
+ else
86
+ data_value = hashes.last
87
+ end
88
+ hash = flatten_attributes(key => data_value)
89
+
90
+ hash.sort_by(&:first).each do |key, value|
91
+ case value
92
+ when true
93
+ attrs << " #{key}"
94
+ when nil, false
95
+ # noop
96
+ else
97
+ attrs << " #{key}=#{quote}#{escape_html(escape_attrs, value.to_s)}#{quote}"
98
+ end
99
+ end
100
+ attrs.join
101
+ end
102
+
103
+ def flatten_attributes(attributes)
104
+ flattened = {}
105
+
106
+ attributes.each do |key, value|
107
+ case value
108
+ when attributes
109
+ when Hash
110
+ flatten_attributes(value).each do |k, v|
111
+ if k.nil?
112
+ flattened[key] = v
113
+ else
114
+ flattened["#{key}-#{k.to_s.gsub(/_/, '-')}"] = v
115
+ end
116
+ end
117
+ else
118
+ flattened[key] = value if value
119
+ end
120
+ end
121
+ flattened
122
+ end
123
+
124
+ def merge_all_attrs(hashes)
125
+ merged = {}
126
+ hashes.each do |hash|
127
+ hash.each do |key, value|
128
+ key = key.to_s
129
+ case key
130
+ when 'id'.freeze, 'class'.freeze, 'data'.freeze
131
+ merged[key] ||= []
132
+ merged[key] << value
133
+ else
134
+ merged[key] = value
135
+ end
136
+ end
137
+ end
138
+ merged
139
+ end
140
+
141
+ def build_boolean!(escape_attrs, quote, format, buf, key, value)
142
+ case value
143
+ when true
144
+ case format
145
+ when :xhtml
146
+ buf << " #{key}=#{quote}#{key}#{quote}"
147
+ else
148
+ buf << " #{key}"
149
+ end
150
+ when false, nil
151
+ # omitted
152
+ else
153
+ buf << " #{key}=#{quote}#{escape_html(escape_attrs, value)}#{quote}"
154
+ end
155
+ end
156
+
157
+ def escape_html(escape_attrs, str)
158
+ if escape_attrs
159
+ Hamlit::Utils.escape_html(str)
160
+ else
161
+ str
162
+ end
163
+ end
164
+ end
165
+ else
166
+ # Hamlit::AttributeBuilder.build
167
+ # Hamlit::AttributeBuilder.build_id
168
+ # Hamlit::AttributeBuilder.build_class
169
+ # Hamlit::AttributeBuilder.build_data
170
+ # Hamlit::AttributeBuilder.build_aria
171
+ require 'hamlit/hamlit'
172
+ end
173
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/attribute_builder'
3
+ require 'hamlit/attribute_parser'
4
+ require 'hamlit/ruby_expression'
5
+
6
+ module Hamlit
7
+ class AttributeCompiler
8
+ def initialize(identity, options)
9
+ @identity = identity
10
+ @quote = options[:attr_quote]
11
+ @format = options[:format]
12
+ @escape_attrs = options[:escape_attrs]
13
+ end
14
+
15
+ def compile(node)
16
+ hashes = []
17
+ return runtime_compile(node) if node.value[:object_ref] != :nil
18
+ node.value[:attributes_hashes].each do |attribute_str|
19
+ hash = AttributeParser.parse(attribute_str)
20
+ return runtime_compile(node) unless hash
21
+ hashes << hash
22
+ end
23
+ static_compile(node.value[:attributes], hashes)
24
+ end
25
+
26
+ private
27
+
28
+ def runtime_compile(node)
29
+ attrs = node.value[:attributes_hashes]
30
+ attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
31
+
32
+ args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect].push(node.value[:object_ref]) + attrs
33
+ [:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')})"]]
34
+ end
35
+
36
+ def static_compile(static_hash, dynamic_hashes)
37
+ temple = [:html, :attrs]
38
+ keys = [*static_hash.keys, *dynamic_hashes.map(&:keys).flatten].uniq.sort
39
+ keys.each do |key|
40
+ values = [[:static, static_hash[key]], *dynamic_hashes.map { |h| [:dynamic, h[key]] }]
41
+ values.select! { |_, exp| exp != nil }
42
+
43
+ case key
44
+ when 'id'
45
+ compile_id!(temple, key, values)
46
+ when 'class'
47
+ compile_class!(temple, key, values)
48
+ when 'data', 'aria'
49
+ compile_data!(temple, key, values)
50
+ when *AttributeBuilder::BOOLEAN_ATTRIBUTES, /\Adata-/, /\Aaria-/
51
+ compile_boolean!(temple, key, values)
52
+ else
53
+ compile_common!(temple, key, values)
54
+ end
55
+ end
56
+ temple
57
+ end
58
+
59
+ def compile_id!(temple, key, values)
60
+ build_code = attribute_builder(:id, values)
61
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
62
+ temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
63
+ else
64
+ temple << [:html, :attr, key, [:dynamic, build_code]]
65
+ end
66
+ end
67
+
68
+ def compile_class!(temple, key, values)
69
+ build_code = attribute_builder(:class, values)
70
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
71
+ temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
72
+ else
73
+ temple << [:html, :attr, key, [:dynamic, build_code]]
74
+ end
75
+ end
76
+
77
+ def compile_data!(temple, key, values)
78
+ args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", values.map { |v| literal_for(v) }]
79
+ build_code = "::Hamlit::AttributeBuilder.build_#{key}(#{args.join(', ')})"
80
+
81
+ if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
82
+ temple << [:static, eval(build_code).to_s]
83
+ else
84
+ temple << [:dynamic, build_code]
85
+ end
86
+ end
87
+
88
+ def compile_boolean!(temple, key, values)
89
+ exp = literal_for(values.last)
90
+
91
+ if Temple::StaticAnalyzer.static?(exp)
92
+ value = eval(exp)
93
+ case value
94
+ when true then temple << [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]
95
+ when false, nil
96
+ else temple << [:html, :attr, key, [:fescape, @escape_attrs, [:static, value.to_s]]]
97
+ end
98
+ else
99
+ var = @identity.generate
100
+ temple << [
101
+ :case, "(#{var} = (#{exp}))",
102
+ ['true', [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]],
103
+ ['false, nil', [:multi]],
104
+ [:else, [:multi, [:static, " #{key}=#{@quote}"], [:fescape, @escape_attrs, [:dynamic, var]], [:static, @quote]]],
105
+ ]
106
+ end
107
+ end
108
+
109
+ def compile_common!(temple, key, values)
110
+ temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
111
+ end
112
+
113
+ def attribute_builder(type, values)
114
+ args = [@escape_attrs.inspect, *values.map { |v| literal_for(v) }]
115
+ "::Hamlit::AttributeBuilder.build_#{type}(#{args.join(', ')})"
116
+ end
117
+
118
+ def literal_for(value)
119
+ type, exp = value
120
+ type == :static ? "#{exp.inspect}.freeze" : exp
121
+ end
122
+ end
123
+ end