hamlit 2.9.3

Sign up to get free protection for your applications and to get access to all the features.
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