haml 4.1.0.beta.1 → 5.0.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +36 -6
- data/FAQ.md +4 -14
- data/MIT-LICENSE +1 -1
- data/README.md +81 -48
- data/REFERENCE.md +86 -50
- data/Rakefile +28 -41
- data/lib/haml/attribute_builder.rb +163 -0
- data/lib/haml/attribute_compiler.rb +214 -0
- data/lib/haml/attribute_parser.rb +112 -0
- data/lib/haml/buffer.rb +24 -126
- data/lib/haml/compiler.rb +62 -281
- data/lib/haml/engine.rb +16 -23
- data/lib/haml/error.rb +2 -0
- data/lib/haml/escapable.rb +48 -0
- data/lib/haml/exec.rb +23 -12
- data/lib/haml/filters.rb +3 -4
- data/lib/haml/generator.rb +36 -0
- data/lib/haml/helpers.rb +61 -48
- data/lib/haml/helpers/action_view_extensions.rb +1 -1
- data/lib/haml/helpers/action_view_mods.rb +32 -50
- data/lib/haml/helpers/safe_erubi_template.rb +26 -0
- data/lib/haml/helpers/safe_erubis_template.rb +2 -0
- data/lib/haml/helpers/xss_mods.rb +17 -12
- data/lib/haml/options.rb +32 -36
- data/lib/haml/parser.rb +61 -38
- data/lib/haml/{template/plugin.rb → plugin.rb} +5 -2
- data/lib/haml/railtie.rb +14 -6
- data/lib/haml/template.rb +11 -6
- data/lib/haml/temple_engine.rb +119 -0
- data/lib/haml/temple_line_counter.rb +28 -0
- data/lib/haml/util.rb +17 -112
- data/lib/haml/version.rb +1 -1
- data/test/attribute_parser_test.rb +105 -0
- data/test/engine_test.rb +202 -106
- data/test/filters_test.rb +32 -19
- data/test/gemfiles/Gemfile.rails-4.0.x +7 -1
- data/test/gemfiles/Gemfile.rails-4.0.x.lock +57 -71
- data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
- data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
- data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
- data/test/helper_test.rb +156 -109
- data/test/options_test.rb +21 -0
- data/test/parser_test.rb +49 -4
- data/test/results/eval_suppressed.xhtml +4 -4
- data/test/results/helpers.xhtml +43 -41
- data/test/results/helpful.xhtml +6 -3
- data/test/results/just_stuff.xhtml +21 -20
- data/test/results/list.xhtml +9 -9
- data/test/results/nuke_inner_whitespace.xhtml +22 -22
- data/test/results/nuke_outer_whitespace.xhtml +84 -92
- data/test/results/original_engine.xhtml +17 -17
- data/test/results/partial_layout.xhtml +4 -3
- data/test/results/partial_layout_erb.xhtml +4 -3
- data/test/results/partials.xhtml +11 -10
- data/test/results/silent_script.xhtml +63 -63
- data/test/results/standard.xhtml +156 -159
- data/test/results/tag_parsing.xhtml +19 -19
- data/test/results/very_basic.xhtml +2 -2
- data/test/results/whitespace_handling.xhtml +77 -76
- data/test/template_test.rb +21 -48
- data/test/template_test_helper.rb +38 -0
- data/test/templates/just_stuff.haml +1 -0
- data/test/templates/standard_ugly.haml +1 -0
- data/test/temple_line_counter_test.rb +40 -0
- data/test/test_helper.rb +10 -10
- data/test/util_test.rb +1 -48
- metadata +49 -35
- data/lib/haml/temple.rb +0 -85
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -4
- data/test/templates/_av_partial_1_ugly.haml +0 -9
- data/test/templates/_av_partial_2_ugly.haml +0 -5
- data/test/templates/action_view_ugly.haml +0 -47
- data/test/templates/standard_ugly.haml +0 -43
data/Rakefile
CHANGED
@@ -1,19 +1,12 @@
|
|
1
1
|
require "rake/clean"
|
2
2
|
require "rake/testtask"
|
3
|
-
require "
|
3
|
+
require "bundler/gem_tasks"
|
4
4
|
|
5
5
|
task :default => :test
|
6
6
|
|
7
7
|
CLEAN.replace %w(pkg doc coverage .yardoc test/haml vendor)
|
8
8
|
|
9
|
-
|
10
|
-
the_real_stderr, $stderr = $stderr, StringIO.new
|
11
|
-
yield
|
12
|
-
ensure
|
13
|
-
$stderr = the_real_stderr
|
14
|
-
end
|
15
|
-
|
16
|
-
desc "Benchmark Haml against ERb. TIMES=n sets the number of runs, default is 1000."
|
9
|
+
desc "Benchmark Haml against ERB. TIMES=n sets the number of runs, default is 1000."
|
17
10
|
task :benchmark do
|
18
11
|
sh "ruby benchmark.rb #{ENV['TIMES']}"
|
19
12
|
end
|
@@ -36,11 +29,6 @@ end
|
|
36
29
|
desc "Run Simplecov"
|
37
30
|
task :coverage => [:set_coverage_env, :test]
|
38
31
|
|
39
|
-
gemspec = File.expand_path("../haml.gemspec", __FILE__)
|
40
|
-
if File.exist? gemspec
|
41
|
-
Gem::PackageTask.new(eval(File.read(gemspec))) { |pkg| }
|
42
|
-
end
|
43
|
-
|
44
32
|
task :submodules do
|
45
33
|
if File.exist?(File.dirname(__FILE__) + "/.git")
|
46
34
|
sh %{git submodule sync}
|
@@ -48,31 +36,32 @@ task :submodules do
|
|
48
36
|
end
|
49
37
|
end
|
50
38
|
|
51
|
-
|
52
|
-
|
53
|
-
require '
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
task :undocumented do
|
59
|
-
command = 'yard --list --query '
|
60
|
-
command << '"object.docstring.blank? && '
|
61
|
-
command << '!(object.type == :method && object.is_alias?)"'
|
62
|
-
sh command
|
39
|
+
namespace :doc do
|
40
|
+
task :sass do
|
41
|
+
require 'sass'
|
42
|
+
Dir["yard/default/**/*.sass"].each do |sass|
|
43
|
+
File.open(sass.gsub(/sass$/, 'css'), 'w') do |f|
|
44
|
+
f.write(Sass::Engine.new(File.read(sass)).render)
|
45
|
+
end
|
63
46
|
end
|
64
47
|
end
|
65
48
|
|
66
|
-
desc "
|
67
|
-
task
|
49
|
+
desc "List all undocumented methods and classes."
|
50
|
+
task :undocumented do
|
51
|
+
command = 'yard --list --query '
|
52
|
+
command << '"object.docstring.blank? && '
|
53
|
+
command << '!(object.type == :method && object.is_alias?)"'
|
54
|
+
sh command
|
55
|
+
end
|
56
|
+
end
|
68
57
|
|
69
|
-
|
70
|
-
|
58
|
+
desc "Generate documentation"
|
59
|
+
task(:doc => 'doc:sass') {sh "yard"}
|
71
60
|
|
72
|
-
|
73
|
-
|
61
|
+
desc "Generate documentation incrementally"
|
62
|
+
task(:redoc) {sh "yard -c"}
|
74
63
|
|
75
|
-
|
64
|
+
desc <<END
|
76
65
|
Profile Haml.
|
77
66
|
TIMES=n sets the number of runs. Defaults to 1000.
|
78
67
|
FILE=str sets the file to profile. Defaults to 'standard'
|
@@ -86,10 +75,9 @@ task :profile do
|
|
86
75
|
require 'bundler/setup'
|
87
76
|
require 'ruby-prof'
|
88
77
|
require 'haml'
|
89
|
-
default =
|
90
78
|
file = File.read(File.expand_path("../#{file}", __FILE__))
|
91
79
|
obj = Object.new
|
92
|
-
Haml::Engine.new(file
|
80
|
+
Haml::Engine.new(file).def_method(obj, :render)
|
93
81
|
result = RubyProf.profile { times.times { obj.render } }
|
94
82
|
|
95
83
|
RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
|
@@ -104,14 +92,13 @@ def gemfiles
|
|
104
92
|
end
|
105
93
|
|
106
94
|
def with_each_gemfile
|
107
|
-
old_env = ENV['BUNDLE_GEMFILE']
|
108
95
|
gemfiles.each do |gemfile|
|
109
|
-
|
110
|
-
|
111
|
-
|
96
|
+
Bundler.with_clean_env do
|
97
|
+
puts "Using gemfile: #{gemfile}"
|
98
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
99
|
+
yield
|
100
|
+
end
|
112
101
|
end
|
113
|
-
ensure
|
114
|
-
ENV['BUNDLE_GEMFILE'] = old_env
|
115
102
|
end
|
116
103
|
|
117
104
|
namespace :test do
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
3
|
+
module AttributeBuilder
|
4
|
+
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
5
|
+
INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
|
9
|
+
# @TODO this is an absolutely ridiculous amount of arguments. At least
|
10
|
+
# some of this needs to be moved into an instance method.
|
11
|
+
join_char = hyphenate_data_attrs ? '-' : '_'
|
12
|
+
|
13
|
+
attributes.each do |key, value|
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
data_attributes = attributes.delete(key)
|
16
|
+
data_attributes = flatten_data_attributes(data_attributes, '', join_char)
|
17
|
+
data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
|
18
|
+
verify_attribute_names!(data_attributes.keys)
|
19
|
+
attributes = data_attributes.merge(attributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
result = attributes.collect do |attr, value|
|
24
|
+
next if value.nil?
|
25
|
+
|
26
|
+
value = filter_and_join(value, ' ') if attr == 'class'
|
27
|
+
value = filter_and_join(value, '_') if attr == 'id'
|
28
|
+
|
29
|
+
if value == true
|
30
|
+
next " #{attr}" if is_html
|
31
|
+
next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
|
32
|
+
elsif value == false
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
value =
|
37
|
+
if escape_attrs == :once
|
38
|
+
Haml::Helpers.escape_once(value.to_s)
|
39
|
+
elsif escape_attrs
|
40
|
+
Haml::Helpers.html_escape(value.to_s)
|
41
|
+
else
|
42
|
+
value.to_s
|
43
|
+
end
|
44
|
+
" #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
|
45
|
+
end
|
46
|
+
result.compact!
|
47
|
+
result.sort!
|
48
|
+
result.join
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [String, nil]
|
52
|
+
def filter_and_join(value, separator)
|
53
|
+
return '' if (value.respond_to?(:empty?) && value.empty?)
|
54
|
+
|
55
|
+
if value.is_a?(Array)
|
56
|
+
value = value.flatten
|
57
|
+
value.map! {|item| item ? item.to_s : nil}
|
58
|
+
value.compact!
|
59
|
+
value = value.join(separator)
|
60
|
+
else
|
61
|
+
value = value ? value.to_s : nil
|
62
|
+
end
|
63
|
+
!value.nil? && !value.empty? && value
|
64
|
+
end
|
65
|
+
|
66
|
+
# Merges two attribute hashes.
|
67
|
+
# This is the same as `to.merge!(from)`,
|
68
|
+
# except that it merges id, class, and data attributes.
|
69
|
+
#
|
70
|
+
# ids are concatenated with `"_"`,
|
71
|
+
# and classes are concatenated with `" "`.
|
72
|
+
# data hashes are simply merged.
|
73
|
+
#
|
74
|
+
# Destructively modifies `to`.
|
75
|
+
#
|
76
|
+
# @param to [{String => String,Hash}] The attribute hash to merge into
|
77
|
+
# @param from [{String => Object}] The attribute hash to merge from
|
78
|
+
# @return [{String => String,Hash}] `to`, after being merged
|
79
|
+
def merge_attributes!(to, from)
|
80
|
+
from.keys.each do |key|
|
81
|
+
to[key] = merge_value(key, to[key], from[key])
|
82
|
+
end
|
83
|
+
to
|
84
|
+
end
|
85
|
+
|
86
|
+
# Merge multiple values to one attribute value. No destructive operation.
|
87
|
+
#
|
88
|
+
# @param key [String]
|
89
|
+
# @param values [Array<Object>]
|
90
|
+
# @return [String,Hash]
|
91
|
+
def merge_values(key, *values)
|
92
|
+
values.inject(nil) do |to, from|
|
93
|
+
merge_value(key, to, from)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def verify_attribute_names!(attribute_names)
|
98
|
+
attribute_names.each do |attribute_name|
|
99
|
+
if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
|
100
|
+
raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Merge a couple of values to one attribute value. No destructive operation.
|
108
|
+
#
|
109
|
+
# @param to [String,Hash,nil]
|
110
|
+
# @param from [Object]
|
111
|
+
# @return [String,Hash]
|
112
|
+
def merge_value(key, to, from)
|
113
|
+
if from.kind_of?(Hash) || to.kind_of?(Hash)
|
114
|
+
from = { nil => from } if !from.is_a?(Hash)
|
115
|
+
to = { nil => to } if !to.is_a?(Hash)
|
116
|
+
to.merge(from)
|
117
|
+
elsif key == 'id'
|
118
|
+
merged_id = filter_and_join(from, '_')
|
119
|
+
if to && merged_id
|
120
|
+
merged_id = "#{to}_#{merged_id}"
|
121
|
+
elsif to || merged_id
|
122
|
+
merged_id ||= to
|
123
|
+
end
|
124
|
+
merged_id
|
125
|
+
elsif key == 'class'
|
126
|
+
merged_class = filter_and_join(from, ' ')
|
127
|
+
if to && merged_class
|
128
|
+
merged_class = (merged_class.split(' ') | to.split(' ')).sort.join(' ')
|
129
|
+
elsif to || merged_class
|
130
|
+
merged_class ||= to
|
131
|
+
end
|
132
|
+
merged_class
|
133
|
+
else
|
134
|
+
from
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def build_data_keys(data_hash, hyphenate, attr_name="data")
|
139
|
+
Hash[data_hash.map do |name, value|
|
140
|
+
if name == nil
|
141
|
+
[attr_name, value]
|
142
|
+
elsif hyphenate
|
143
|
+
["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
|
144
|
+
else
|
145
|
+
["#{attr_name}-#{name}", value]
|
146
|
+
end
|
147
|
+
end]
|
148
|
+
end
|
149
|
+
|
150
|
+
def flatten_data_attributes(data, key, join_char, seen = [])
|
151
|
+
return {key => data} unless data.is_a?(Hash)
|
152
|
+
|
153
|
+
return {key => nil} if seen.include? data.object_id
|
154
|
+
seen << data.object_id
|
155
|
+
|
156
|
+
data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
|
157
|
+
joined = key == '' ? k : [key, k].join(join_char)
|
158
|
+
hash.merge! flatten_data_attributes(v, joined, join_char, seen)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'haml/attribute_parser'
|
2
|
+
|
3
|
+
module Haml
|
4
|
+
class AttributeCompiler
|
5
|
+
# @param type [Symbol] :static or :dynamic
|
6
|
+
# @param key [String]
|
7
|
+
# @param value [String] Actual string value for :static type, value's Ruby literal for :dynamic type.
|
8
|
+
class AttributeValue < Struct.new(:type, :key, :value)
|
9
|
+
# @return [String] A Ruby literal of value.
|
10
|
+
def to_literal
|
11
|
+
case type
|
12
|
+
when :static
|
13
|
+
Haml::Util.inspect_obj(value)
|
14
|
+
when :dynamic
|
15
|
+
value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Key's substring before a hyphen. This is necessary because values with the same
|
20
|
+
# base_key can conflict by Haml::AttributeBuidler#build_data_keys.
|
21
|
+
def base_key
|
22
|
+
key.split('-', 2).first
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a script to render attributes on runtime.
|
27
|
+
#
|
28
|
+
# @param attributes [Hash]
|
29
|
+
# @param object_ref [String,:nil]
|
30
|
+
# @param attributes_hashes [Array<String>]
|
31
|
+
# @return [String] Attributes rendering code
|
32
|
+
def self.runtime_build(attributes, object_ref, attributes_hashes)
|
33
|
+
"_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{attributes_hashes.join(', ')})"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param options [Haml::Options]
|
37
|
+
def initialize(options)
|
38
|
+
@is_html = [:html4, :html5].include?(options[:format])
|
39
|
+
@attr_wrapper = options[:attr_wrapper]
|
40
|
+
@escape_attrs = options[:escape_attrs]
|
41
|
+
@hyphenate_data_attrs = options[:hyphenate_data_attrs]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns Temple expression to render attributes.
|
45
|
+
#
|
46
|
+
# @param attributes [Hash]
|
47
|
+
# @param object_ref [String,:nil]
|
48
|
+
# @param attributes_hashes [Array<String>]
|
49
|
+
# @return [Array] Temple expression
|
50
|
+
def compile(attributes, object_ref, attributes_hashes)
|
51
|
+
if object_ref != :nil || !AttributeParser.available?
|
52
|
+
return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, attributes_hashes)]
|
53
|
+
end
|
54
|
+
|
55
|
+
parsed_hashes = attributes_hashes.map do |attribute_hash|
|
56
|
+
unless (hash = AttributeParser.parse(attribute_hash))
|
57
|
+
return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, attributes_hashes)]
|
58
|
+
end
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
attribute_values = build_attribute_values(attributes, parsed_hashes)
|
62
|
+
AttributeBuilder.verify_attribute_names!(attribute_values.map(&:key))
|
63
|
+
|
64
|
+
values_by_base_key = attribute_values.group_by(&:base_key)
|
65
|
+
[:multi, *values_by_base_key.keys.sort.map { |base_key|
|
66
|
+
compile_attribute_values(values_by_base_key[base_key])
|
67
|
+
}]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Returns array of AttributeValue instnces from static attributes and dynamic attributes_hashes. For each key,
|
73
|
+
# the values' order in returned value is preserved in the same order as Haml::Buffer#attributes's merge order.
|
74
|
+
#
|
75
|
+
# @param attributes [{ String => String }]
|
76
|
+
# @param parsed_hashes [{ String => String }]
|
77
|
+
# @return [Array<AttributeValue>]
|
78
|
+
def build_attribute_values(attributes, parsed_hashes)
|
79
|
+
[].tap do |attribute_values|
|
80
|
+
attributes.each do |key, static_value|
|
81
|
+
attribute_values << AttributeValue.new(:static, key, static_value)
|
82
|
+
end
|
83
|
+
parsed_hashes.each do |parsed_hash|
|
84
|
+
parsed_hash.each do |key, dynamic_value|
|
85
|
+
attribute_values << AttributeValue.new(:dynamic, key, dynamic_value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Compiles attribute values with the same base_key to Temple expression.
|
92
|
+
#
|
93
|
+
# @param values [Array<AttributeValue>] `base_key`'s results are the same. `key`'s result may differ.
|
94
|
+
# @return [Array] Temple expression
|
95
|
+
def compile_attribute_values(values)
|
96
|
+
if values.map(&:key).uniq.size == 1
|
97
|
+
compile_attribute(values.first.key, values)
|
98
|
+
else
|
99
|
+
runtime_build(values)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# @param values [Array<AttributeValue>]
|
104
|
+
# @return [Array] Temple expression
|
105
|
+
def runtime_build(values)
|
106
|
+
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
107
|
+
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
108
|
+
end.join(', ')
|
109
|
+
[:dynamic, "_hamlout.attributes({ #{hash_content} }, nil)"]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Renders attribute values statically.
|
113
|
+
#
|
114
|
+
# @param values [Array<AttributeValue>]
|
115
|
+
# @return [Array] Temple expression
|
116
|
+
def static_build(values)
|
117
|
+
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
118
|
+
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
119
|
+
end.join(', ')
|
120
|
+
|
121
|
+
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
|
122
|
+
code = "::Haml::AttributeBuilder.build_attributes"\
|
123
|
+
"(#{arguments.map { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
|
124
|
+
[:static, eval(code).to_s]
|
125
|
+
end
|
126
|
+
|
127
|
+
# @param key [String]
|
128
|
+
# @param values [Array<AttributeValue>]
|
129
|
+
# @return [String]
|
130
|
+
def merged_value(key, values)
|
131
|
+
if values.size == 1
|
132
|
+
values.first.to_literal
|
133
|
+
else
|
134
|
+
"::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&:to_literal).join(', ')})"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param str [String]
|
139
|
+
# @return [String]
|
140
|
+
def frozen_string(str)
|
141
|
+
"#{Haml::Util.inspect_obj(str)}.freeze"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Compiles attribute values for one key to Temple expression that generates ` key='value'`.
|
145
|
+
#
|
146
|
+
# @param key [String]
|
147
|
+
# @param values [Array<AttributeValue>]
|
148
|
+
# @return [Array] Temple expression
|
149
|
+
def compile_attribute(key, values)
|
150
|
+
if values.all? { |v| Temple::StaticAnalyzer.static?(v.to_literal) }
|
151
|
+
return static_build(values)
|
152
|
+
end
|
153
|
+
|
154
|
+
case key
|
155
|
+
when 'id', 'class'
|
156
|
+
compile_id_or_class_attribute(key, values)
|
157
|
+
else
|
158
|
+
compile_common_attribute(key, values)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @param id_or_class [String] "id" or "class"
|
163
|
+
# @param values [Array<AttributeValue>]
|
164
|
+
# @return [Array] Temple expression
|
165
|
+
def compile_id_or_class_attribute(id_or_class, values)
|
166
|
+
var = unique_name
|
167
|
+
[:multi,
|
168
|
+
[:code, "#{var} = (#{merged_value(id_or_class, values)})"],
|
169
|
+
[:case, var,
|
170
|
+
['Hash, Array', runtime_build([AttributeValue.new(:dynamic, id_or_class, var)])],
|
171
|
+
['false, nil', [:multi]],
|
172
|
+
[:else, [:multi,
|
173
|
+
[:static, " #{id_or_class}=#{@attr_wrapper}"],
|
174
|
+
[:escape, @escape_attrs, [:dynamic, var]],
|
175
|
+
[:static, @attr_wrapper]],
|
176
|
+
]
|
177
|
+
],
|
178
|
+
]
|
179
|
+
end
|
180
|
+
|
181
|
+
# @param key [String] Not "id" or "class"
|
182
|
+
# @param values [Array<AttributeValue>]
|
183
|
+
# @return [Array] Temple expression
|
184
|
+
def compile_common_attribute(key, values)
|
185
|
+
var = unique_name
|
186
|
+
[:multi,
|
187
|
+
[:code, "#{var} = (#{merged_value(key, values)})"],
|
188
|
+
[:case, var,
|
189
|
+
['Hash', runtime_build([AttributeValue.new(:dynamic, key, var)])],
|
190
|
+
['true', true_value(key)],
|
191
|
+
['false, nil', [:multi]],
|
192
|
+
[:else, [:multi,
|
193
|
+
[:static, " #{key}=#{@attr_wrapper}"],
|
194
|
+
[:escape, @escape_attrs, [:dynamic, var]],
|
195
|
+
[:static, @attr_wrapper]],
|
196
|
+
]
|
197
|
+
],
|
198
|
+
]
|
199
|
+
end
|
200
|
+
|
201
|
+
def true_value(key)
|
202
|
+
if @is_html
|
203
|
+
[:static, " #{key}"]
|
204
|
+
else
|
205
|
+
[:static, " #{key}=#{@attr_wrapper}#{key}#{@attr_wrapper}"]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def unique_name
|
210
|
+
@unique_name ||= 0
|
211
|
+
"_haml_attribute_compiler#{@unique_name += 1}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|