hamlit 2.9.5-java → 2.13.0-java
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +17 -11
- data/CHANGELOG.md +52 -0
- data/Gemfile +2 -8
- data/LICENSE.txt +26 -23
- data/REFERENCE.md +13 -4
- data/benchmark/dynamic_merger/benchmark.rb +25 -0
- data/benchmark/dynamic_merger/hello.haml +50 -0
- data/benchmark/dynamic_merger/hello.string +50 -0
- data/bin/bench +3 -3
- data/bin/update-haml +125 -0
- data/ext/hamlit/hamlit.c +0 -1
- data/hamlit.gemspec +3 -1
- data/lib/hamlit/attribute_builder.rb +2 -2
- data/lib/hamlit/attribute_compiler.rb +3 -3
- data/lib/hamlit/compiler/children_compiler.rb +18 -4
- data/lib/hamlit/compiler/comment_compiler.rb +8 -5
- data/lib/hamlit/compiler/tag_compiler.rb +0 -4
- data/lib/hamlit/dynamic_merger.rb +67 -0
- data/lib/hamlit/engine.rb +5 -6
- data/lib/hamlit/filters/escaped.rb +1 -1
- data/lib/hamlit/filters/markdown.rb +1 -0
- data/lib/hamlit/filters/plain.rb +0 -4
- data/lib/hamlit/filters/preserve.rb +1 -1
- data/lib/hamlit/filters/text_base.rb +1 -1
- data/lib/hamlit/filters/tilt_base.rb +1 -1
- data/lib/hamlit/html.rb +8 -0
- data/lib/hamlit/parser.rb +6 -2
- data/lib/hamlit/parser/haml_attribute_builder.rb +164 -0
- data/lib/hamlit/parser/haml_buffer.rb +20 -130
- data/lib/hamlit/parser/haml_compiler.rb +1 -553
- data/lib/hamlit/parser/haml_error.rb +29 -25
- data/lib/hamlit/parser/haml_escapable.rb +1 -0
- data/lib/hamlit/parser/haml_generator.rb +1 -0
- data/lib/hamlit/parser/haml_helpers.rb +41 -59
- data/lib/hamlit/parser/{haml_xss_mods.rb → haml_helpers/xss_mods.rb} +20 -15
- data/lib/hamlit/parser/haml_options.rb +53 -66
- data/lib/hamlit/parser/haml_parser.rb +103 -71
- data/lib/hamlit/parser/haml_temple_engine.rb +123 -0
- data/lib/hamlit/parser/haml_util.rb +10 -40
- data/lib/hamlit/rails_template.rb +1 -1
- data/lib/hamlit/string_splitter.rb +10 -78
- data/lib/hamlit/template.rb +1 -1
- data/lib/hamlit/temple_line_counter.rb +31 -0
- data/lib/hamlit/version.rb +1 -1
- metadata +58 -24
- data/lib/hamlit/hamlit.su +0 -0
- data/lib/hamlit/parser/MIT-LICENSE +0 -20
- data/lib/hamlit/parser/README.md +0 -30
data/ext/hamlit/hamlit.c
CHANGED
data/hamlit.gemspec
CHANGED
@@ -26,10 +26,11 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.required_ruby_version = '>= 2.1.0'
|
27
27
|
end
|
28
28
|
|
29
|
-
spec.add_dependency 'temple', '>= 0.8.
|
29
|
+
spec.add_dependency 'temple', '>= 0.8.2'
|
30
30
|
spec.add_dependency 'thor'
|
31
31
|
spec.add_dependency 'tilt'
|
32
32
|
|
33
|
+
spec.add_development_dependency 'benchmark_driver'
|
33
34
|
spec.add_development_dependency 'bundler'
|
34
35
|
spec.add_development_dependency 'coffee-script'
|
35
36
|
spec.add_development_dependency 'erubi'
|
@@ -41,5 +42,6 @@ Gem::Specification.new do |spec|
|
|
41
42
|
spec.add_development_dependency 'rake-compiler'
|
42
43
|
spec.add_development_dependency 'sass'
|
43
44
|
spec.add_development_dependency 'slim'
|
45
|
+
spec.add_development_dependency 'string_template'
|
44
46
|
spec.add_development_dependency 'unindent'
|
45
47
|
end
|
@@ -47,7 +47,7 @@ module Hamlit::AttributeBuilder
|
|
47
47
|
when value.is_a?(String)
|
48
48
|
# noop
|
49
49
|
when value.is_a?(Array)
|
50
|
-
value = value.flatten.select { |v| v }.map(&:to_s).
|
50
|
+
value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
|
51
51
|
when value
|
52
52
|
value = value.to_s
|
53
53
|
else
|
@@ -67,7 +67,7 @@ module Hamlit::AttributeBuilder
|
|
67
67
|
classes << value.to_s
|
68
68
|
end
|
69
69
|
end
|
70
|
-
escape_html(escape_attrs, classes.map(&:to_s).
|
70
|
+
escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
|
71
71
|
end
|
72
72
|
|
73
73
|
def build_data(escape_attrs, quote, *hashes)
|
@@ -17,7 +17,7 @@ module Hamlit
|
|
17
17
|
if node.value[:object_ref] != :nil || !Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
18
18
|
return runtime_compile(node)
|
19
19
|
end
|
20
|
-
node.value[:
|
20
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
|
21
21
|
hash = AttributeParser.parse(attribute_str)
|
22
22
|
return runtime_compile(node) unless hash
|
23
23
|
hashes << hash
|
@@ -28,11 +28,11 @@ module Hamlit
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def runtime_compile(node)
|
31
|
-
attrs =
|
31
|
+
attrs = []
|
32
32
|
attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
|
33
33
|
|
34
34
|
args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect].push(node.value[:object_ref]) + attrs
|
35
|
-
[:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')})"]]
|
35
|
+
[:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
|
36
36
|
end
|
37
37
|
|
38
38
|
def static_compile(static_hash, dynamic_hashes)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'hamlit/temple_line_counter'
|
3
|
+
|
2
4
|
module Hamlit
|
3
5
|
class Compiler
|
4
6
|
class ChildrenCompiler
|
@@ -14,7 +16,7 @@ module Hamlit
|
|
14
16
|
node.children.each do |n|
|
15
17
|
rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
|
16
18
|
insert_newlines!(temple, n)
|
17
|
-
temple <<
|
19
|
+
temple << moving_lineno(n) { block.call(n) }
|
18
20
|
temple << :whitespace if insert_whitespace?(n)
|
19
21
|
end
|
20
22
|
rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
|
@@ -27,19 +29,31 @@ module Hamlit
|
|
27
29
|
(node.line - @lineno).times do
|
28
30
|
temple << [:newline]
|
29
31
|
end
|
32
|
+
|
30
33
|
@lineno = node.line
|
34
|
+
end
|
31
35
|
|
36
|
+
def moving_lineno(node, &block)
|
37
|
+
# before: As they may have children, we need to increment lineno before compilation.
|
32
38
|
case node.type
|
33
39
|
when :script, :silent_script
|
34
40
|
@lineno += 1
|
35
|
-
when :filter
|
36
|
-
@lineno += (node.value[:text] || '').split("\n").size
|
37
41
|
when :tag
|
38
|
-
node.value[:
|
42
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_hash|
|
39
43
|
@lineno += attribute_hash.count("\n")
|
40
44
|
end
|
41
45
|
@lineno += 1 if node.children.empty? && node.value[:parse]
|
42
46
|
end
|
47
|
+
|
48
|
+
temple = block.call # compile
|
49
|
+
|
50
|
+
# after: filter may not have children, and for some dynamic filters we can't predict the number of lines.
|
51
|
+
case node.type
|
52
|
+
when :filter
|
53
|
+
@lineno += TempleLineCounter.count_lines(temple)
|
54
|
+
end
|
55
|
+
|
56
|
+
temple
|
43
57
|
end
|
44
58
|
|
45
59
|
def confirm_whitespace(temple)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Hamlit
|
2
3
|
class Compiler
|
3
4
|
class CommentCompiler
|
@@ -25,11 +26,13 @@ module Hamlit
|
|
25
26
|
condition = $1
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
content =
|
30
|
+
if node.children.empty?
|
31
|
+
[:static, " #{node.value[:text]} "]
|
32
|
+
else
|
33
|
+
yield(node)
|
34
|
+
end
|
35
|
+
[:html, :condcomment, condition, content, node.value[:revealed]]
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
@@ -55,10 +55,6 @@ module Hamlit
|
|
55
55
|
|
56
56
|
# We should handle interpolation here to escape only interpolated values.
|
57
57
|
def compile_interpolated_plain(node)
|
58
|
-
unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
59
|
-
return [:multi, [:escape, node.value[:escape_interpolation], [:dynamic, "%Q[#{node.value[:value]}]"]], [:newline]]
|
60
|
-
end
|
61
|
-
|
62
58
|
temple = [:multi]
|
63
59
|
StringSplitter.compile(node.value[:value]).each do |type, value|
|
64
60
|
case type
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hamlit
|
3
|
+
# Compile [:multi, [:static, 'foo'], [:dynamic, 'bar']] to [:dynamic, '"foo#{bar}"']
|
4
|
+
class DynamicMerger < Temple::Filter
|
5
|
+
def on_multi(*exps)
|
6
|
+
exps = exps.dup
|
7
|
+
result = [:multi]
|
8
|
+
buffer = []
|
9
|
+
|
10
|
+
until exps.empty?
|
11
|
+
type, arg = exps.first
|
12
|
+
if type == :dynamic && arg.count("\n") == 0
|
13
|
+
buffer << exps.shift
|
14
|
+
elsif type == :static && exps.size > (count = arg.count("\n")) &&
|
15
|
+
exps[1, count].all? { |e| e == [:newline] }
|
16
|
+
(1 + count).times { buffer << exps.shift }
|
17
|
+
elsif type == :newline && exps.size > (count = count_newline(exps)) &&
|
18
|
+
exps[count].first == :static && count == exps[count].last.count("\n")
|
19
|
+
(count + 1).times { buffer << exps.shift }
|
20
|
+
else
|
21
|
+
result.concat(merge_dynamic(buffer))
|
22
|
+
buffer = []
|
23
|
+
result << compile(exps.shift)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
result.concat(merge_dynamic(buffer))
|
27
|
+
|
28
|
+
result.size == 2 ? result[1] : result
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def merge_dynamic(exps)
|
34
|
+
# Merge exps only when they have both :static and :dynamic
|
35
|
+
unless exps.any? { |type,| type == :static } && exps.any? { |type,| type == :dynamic }
|
36
|
+
return exps
|
37
|
+
end
|
38
|
+
|
39
|
+
strlit_body = String.new
|
40
|
+
exps.each do |type, arg|
|
41
|
+
case type
|
42
|
+
when :static
|
43
|
+
strlit_body << arg.dump.sub!(/\A"/, '').sub!(/"\z/, '').gsub('\n', "\n")
|
44
|
+
when :dynamic
|
45
|
+
strlit_body << "\#{#{arg}}"
|
46
|
+
when :newline
|
47
|
+
# newline is added by `gsub('\n', "\n")`
|
48
|
+
else
|
49
|
+
raise "unexpected type #{type.inspect} is given to #merge_dynamic"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
[[:dynamic, "%Q\0#{strlit_body}\0"]]
|
53
|
+
end
|
54
|
+
|
55
|
+
def count_newline(exps)
|
56
|
+
count = 0
|
57
|
+
exps.each do |exp|
|
58
|
+
if exp == [:newline]
|
59
|
+
count += 1
|
60
|
+
else
|
61
|
+
return count
|
62
|
+
end
|
63
|
+
end
|
64
|
+
return count
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/hamlit/engine.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
require 'temple'
|
3
3
|
require 'hamlit/parser'
|
4
4
|
require 'hamlit/compiler'
|
5
|
+
require 'hamlit/html'
|
5
6
|
require 'hamlit/escapable'
|
6
7
|
require 'hamlit/force_escapable'
|
7
|
-
require 'hamlit/
|
8
|
-
require 'hamlit/string_splitter'
|
8
|
+
require 'hamlit/dynamic_merger'
|
9
9
|
|
10
10
|
module Hamlit
|
11
11
|
class Engine < Temple::Engine
|
@@ -25,15 +25,14 @@ module Hamlit
|
|
25
25
|
use Parser
|
26
26
|
use Compiler
|
27
27
|
use HTML
|
28
|
-
|
29
|
-
|
30
|
-
filter :StaticAnalyzer
|
31
|
-
end
|
28
|
+
filter :StringSplitter
|
29
|
+
filter :StaticAnalyzer
|
32
30
|
use Escapable
|
33
31
|
use ForceEscapable
|
34
32
|
filter :ControlFlow
|
35
33
|
filter :MultiFlattener
|
36
34
|
filter :StaticMerger
|
35
|
+
use DynamicMerger
|
37
36
|
use :Generator, -> { options[:generator] }
|
38
37
|
end
|
39
38
|
end
|
@@ -12,7 +12,7 @@ module Hamlit
|
|
12
12
|
|
13
13
|
def compile_text(text)
|
14
14
|
if ::Hamlit::HamlUtil.contains_interpolation?(text)
|
15
|
-
[:dynamic, ::Hamlit::HamlUtil.
|
15
|
+
[:dynamic, ::Hamlit::HamlUtil.unescape_interpolation(text)]
|
16
16
|
else
|
17
17
|
[:static, text]
|
18
18
|
end
|
data/lib/hamlit/filters/plain.rb
CHANGED
@@ -14,10 +14,6 @@ module Hamlit
|
|
14
14
|
|
15
15
|
def compile_plain(text)
|
16
16
|
string_literal = ::Hamlit::HamlUtil.unescape_interpolation(text)
|
17
|
-
unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
|
18
|
-
return [[:escape, false, [:dynamic, string_literal]]]
|
19
|
-
end
|
20
|
-
|
21
17
|
StringSplitter.compile(string_literal).map do |temple|
|
22
18
|
type, str = temple
|
23
19
|
case type
|
@@ -12,7 +12,7 @@ module Hamlit
|
|
12
12
|
|
13
13
|
def compile_text(text)
|
14
14
|
if ::Hamlit::HamlUtil.contains_interpolation?(text)
|
15
|
-
[:dynamic, ::Hamlit::HamlUtil.
|
15
|
+
[:dynamic, ::Hamlit::HamlUtil.unescape_interpolation(text)]
|
16
16
|
else
|
17
17
|
[:static, text]
|
18
18
|
end
|
@@ -6,7 +6,7 @@ module Hamlit
|
|
6
6
|
text = node.value[:text].rstrip.gsub(/^/, prefix)
|
7
7
|
if ::Hamlit::HamlUtil.contains_interpolation?(node.value[:text])
|
8
8
|
# original: Haml::Filters#compile
|
9
|
-
text = ::Hamlit::HamlUtil.
|
9
|
+
text = ::Hamlit::HamlUtil.unescape_interpolation(text).gsub(/(\\+)n/) do |s|
|
10
10
|
escapes = $1.size
|
11
11
|
next s if escapes % 2 == 0
|
12
12
|
"#{'\\' * (escapes - 1)}\n"
|
@@ -35,7 +35,7 @@ module Hamlit
|
|
35
35
|
|
36
36
|
def dynamic_compile(node, name, indent_width: 0)
|
37
37
|
# original: Haml::Filters#compile
|
38
|
-
text = ::Hamlit::HamlUtil.
|
38
|
+
text = ::Hamlit::HamlUtil.unescape_interpolation(node.value[:text]).gsub(/(\\+)n/) do |s|
|
39
39
|
escapes = $1.size
|
40
40
|
next s if escapes % 2 == 0
|
41
41
|
"#{'\\' * (escapes - 1)}\n"
|
data/lib/hamlit/html.rb
CHANGED
@@ -10,5 +10,13 @@ module Hamlit
|
|
10
10
|
end
|
11
11
|
super(opts)
|
12
12
|
end
|
13
|
+
|
14
|
+
# This dispatcher supports Haml's "revealed" conditional comment.
|
15
|
+
def on_html_condcomment(condition, content, revealed = false)
|
16
|
+
on_html_comment [:multi,
|
17
|
+
[:static, "[#{condition}]>#{'<!-->' if revealed}"],
|
18
|
+
content,
|
19
|
+
[:static, "#{'<!--' if revealed}<![endif]"]]
|
20
|
+
end
|
13
21
|
end
|
14
22
|
end
|
data/lib/hamlit/parser.rb
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
# Hamlit::Parser uses original Haml::Parser to generate Haml AST.
|
3
3
|
# hamlit/parser/haml_* are modules originally in haml gem.
|
4
4
|
|
5
|
+
require 'hamlit/parser/haml_attribute_builder'
|
5
6
|
require 'hamlit/parser/haml_error'
|
6
7
|
require 'hamlit/parser/haml_util'
|
8
|
+
require 'hamlit/parser/haml_helpers'
|
7
9
|
require 'hamlit/parser/haml_buffer'
|
8
10
|
require 'hamlit/parser/haml_compiler'
|
9
11
|
require 'hamlit/parser/haml_parser'
|
10
|
-
require 'hamlit/parser/haml_helpers'
|
11
12
|
require 'hamlit/parser/haml_options'
|
13
|
+
require 'hamlit/parser/haml_escapable'
|
14
|
+
require 'hamlit/parser/haml_generator'
|
15
|
+
require 'hamlit/parser/haml_temple_engine'
|
12
16
|
|
13
17
|
module Hamlit
|
14
18
|
class Parser
|
@@ -29,7 +33,7 @@ module Hamlit
|
|
29
33
|
template = Hamlit::HamlUtil.check_haml_encoding(template) do |msg, line|
|
30
34
|
raise Hamlit::Error.new(msg, line)
|
31
35
|
end
|
32
|
-
HamlParser.new(
|
36
|
+
HamlParser.new(HamlOptions.new(@options)).call(template)
|
33
37
|
rescue ::Hamlit::HamlError => e
|
34
38
|
error_with_lineno(e)
|
35
39
|
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hamlit
|
4
|
+
module HamlAttributeBuilder
|
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
|
+
Hamlit::HamlHelpers.escape_once_without_haml_xss(value.to_s)
|
40
|
+
elsif escape_attrs
|
41
|
+
Hamlit::HamlHelpers.html_escape_without_haml_xss(value.to_s)
|
42
|
+
else
|
43
|
+
value.to_s
|
44
|
+
end
|
45
|
+
" #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
|
46
|
+
end
|
47
|
+
result.compact!
|
48
|
+
result.sort!
|
49
|
+
result.join
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String, nil]
|
53
|
+
def filter_and_join(value, separator)
|
54
|
+
return '' if (value.respond_to?(:empty?) && value.empty?)
|
55
|
+
|
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)
|
61
|
+
else
|
62
|
+
value = value ? value.to_s : nil
|
63
|
+
end
|
64
|
+
!value.nil? && !value.empty? && value
|
65
|
+
end
|
66
|
+
|
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])
|
83
|
+
end
|
84
|
+
to
|
85
|
+
end
|
86
|
+
|
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
|
97
|
+
|
98
|
+
def verify_attribute_names!(attribute_names)
|
99
|
+
attribute_names.each do |attribute_name|
|
100
|
+
if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
|
101
|
+
raise HamlInvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
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 = (to.split(' ') | merged_class.split(' ')).join(' ')
|
130
|
+
elsif to || merged_class
|
131
|
+
merged_class ||= to
|
132
|
+
end
|
133
|
+
merged_class
|
134
|
+
else
|
135
|
+
from
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
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]
|
145
|
+
else
|
146
|
+
["#{attr_name}-#{name}", value]
|
147
|
+
end
|
148
|
+
end]
|
149
|
+
end
|
150
|
+
|
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)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|