1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Rewrite AttributeCompiler

This commit is contained in:
Takashi Kokubun 2015-11-05 02:02:25 +09:00
parent 06304a2e8c
commit 4cd30ab662
3 changed files with 107 additions and 18 deletions

View file

@ -5,41 +5,94 @@ module Hamlit
class Compiler
class AttributeCompiler
def initialize(options = {})
@quote = options[:attr_quote].inspect.freeze
@quote = options[:attr_quote]
@format = options[:format]
@escape_attrs = options[:escape_attrs]
end
def compile(node)
[:html, :attrs].tap do |temple|
case
when !node.value[:attributes_hashes].empty?
compile_on_runtime!(temple, node)
when !node.value[:attributes].empty?
compile_static_attributes!(temple, node)
end
hashes = []
node.value[:attributes_hashes].each do |attribute_str|
hash = HashParser.parse(attribute_str)
return runtime_compile(node) unless hash
hashes << hash
end
static_compile(node.value[:attributes], hashes)
end
private
def compile_on_runtime!(temple, node)
def runtime_compile(node)
attrs = node.value[:attributes_hashes]
attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
temple << [:dynamic, "::Hamlit::AttributeBuilder.build(#{@quote}, #{@format.inspect}, #{attrs.join(', ')})"]
[:html,
:attrs,
[:dynamic,
'::Hamlit::AttributeBuilder.build' \
"(#{@quote.inspect}, #{@format.inspect}, #{attrs.join(', ')})",
],
]
end
def compile_static_attributes!(temple, node)
node.value[:attributes].sort_by(&:first).each do |name, value|
case
when value == true
temple << [:html, :attr, name, [:multi]]
when @escape_attrs
temple << [:html, :attr, name, [:escape, true, [:static, value]]]
def static_compile(static_hash, dynamic_hashes)
temple = [:html, :attrs]
keys = [*static_hash.keys, *dynamic_hashes.map(&:keys).flatten].uniq.sort
keys.each do |key|
case key
when 'id'.freeze
compile_id!(temple, key, static_hash, dynamic_hashes)
when 'class'.freeze
compile_class!(temple, key, static_hash, dynamic_hashes)
when 'data'.freeze
compile_data!(temple, key, static_hash, dynamic_hashes)
when *AttributeBuilder::BOOLEAN_ATTRIBUTES
compile_boolean!(temple, key, static_hash, dynamic_hashes)
else
temple << [:html, :attr, name, [:static, value]]
end
end
compile_common!(temple, key, static_hash, dynamic_hashes)
end
end
temple
end
def compile_id!(temple, key, static_hash, dynamic_hashes)
temple << build_attr(:static, key, static_hash[key]) if static_hash[key]
dynamic_hashes.each do |dynamic_hash|
temple << build_attr(:dynamic, key, dynamic_hash[key]) if dynamic_hash[key]
end
end
def compile_class!(temple, key, static_hash, dynamic_hashes)
temple << build_attr(:static, key, static_hash[key]) if static_hash[key]
dynamic_hashes.each do |dynamic_hash|
temple << build_attr(:dynamic, key, dynamic_hash[key]) if dynamic_hash[key]
end
end
def compile_data!(temple, key, static_hash, dynamic_hashes)
temple << build_attr(:static, key, static_hash[key]) if static_hash[key]
dynamic_hashes.each do |dynamic_hash|
temple << build_attr(:dynamic, key, dynamic_hash[key]) if dynamic_hash[key]
end
end
def compile_boolean!(temple, key, static_hash, dynamic_hashes)
value = build_attr(:static, key, static_hash[key]) if static_hash[key]
dynamic_hashes.each do |dynamic_hash|
value = build_attr(:dynamic, key, dynamic_hash[key]) if dynamic_hash[key]
end
temple << value
end
def compile_common!(temple, key, static_hash, dynamic_hashes)
value = build_attr(:static, key, static_hash[key]) if static_hash[key]
dynamic_hashes.each do |dynamic_hash|
value = build_attr(:dynamic, key, dynamic_hash[key]) if dynamic_hash[key]
end
temple << value
end
def build_attr(type, key, value)
[:html, :attr, key, [:escape, @escape_attrs, [type, value]]]
end
end
end

View file

@ -0,0 +1,32 @@
describe Hamlit::Engine do
include RenderAssertion
it { assert_inline(%Q[%div], %Q[<div></div>]) }
it { assert_inline(%Q[.bar.foo], %Q[<div class='bar foo'></div>]) }
it { assert_inline(%Q[.foo.bar], %Q[<div class='foo bar'></div>]) }
it { assert_inline(%Q[%div(class='bar foo')], %Q[<div class='bar foo'></div>]) }
it { assert_inline(%Q[%div(class='foo bar')], %Q[<div class='foo bar'></div>]) }
it { assert_inline(%Q[%div{ class: 'bar foo' }], %Q[<div class='bar foo'></div>]) }
it { assert_inline(%q[%a{ href: "'\"" }], %Q[<a href='&#39;&quot;'></a>]) }
it { assert_inline(%Q[%a{ href: '/search?foo=bar&hoge=<fuga>' }], %Q[<a href='/search?foo=bar&amp;hoge=&lt;fuga&gt;'></a>]) }
specify do
assert_render(<<-HAML, <<-HTML)
- new = 'new'
- old = 'old'
%span(foo='new'){ foo: 'old' }
%span{ foo: 'old' }(foo='new')
%span(foo=new){ foo: 'old' }
%span{ foo: 'old' }(foo=new)
%span(foo=new){ foo: old }
%span{ foo: old }(foo=new)
HAML
<span foo='old'></span>
<span foo='old'></span>
<span foo='old'></span>
<span foo='old'></span>
<span foo='old'></span>
<span foo='old'></span>
HTML
end
end

View file

@ -36,6 +36,10 @@ module RenderAssertion
assert_equal html, render(haml, options)
end
def assert_inline(haml, html)
assert_equal html + "\n", render(haml + "\n")
end
def render(text, options = {}, &block)
scope = options.delete(:scope) || Object.new
locals = options.delete(:locals) || {}