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

Merge id and class with attributes on runtime

Fixes #4.
This commit is contained in:
Takashi Kokubun 2015-04-11 18:15:37 +09:00
parent 20e54364f7
commit 7d3056b89a
6 changed files with 188 additions and 18 deletions

View file

@ -10,18 +10,18 @@ module Hamlit
class Attribute
include Concerns::AttributeBuilder
def self.build(quote, attributes)
def self.build(quote, attributes, base = {})
builder = self.new(quote)
builder.build(attributes)
builder.build(attributes, base)
end
def initialize(quote)
@quote = quote
end
def build(attributes)
def build(attributes, base)
result = ''
flatten_attributes(attributes).each do |key, value|
merge_attributes(base, attributes).each do |key, value|
if value == true
result += " #{key}"
next
@ -32,5 +32,27 @@ module Hamlit
end
result
end
private
def merge_attributes(base, target)
result = {}
base = flatten_attributes(base)
target = flatten_attributes(target)
(base.keys | target.keys).each do |key|
if base[key] && target[key]
case key
when :id
result[key] = [base[key], target[key]].compact.join('_')
else
result[key] = [base[key], target[key]].compact.join(' ')
end
else
result[key] = base[key].nil? ? target[key] : base[key]
end
end
result
end
end
end

View file

@ -1,5 +1,6 @@
require 'hamlit/compilers/new_attribute'
require 'hamlit/compilers/old_attribute'
require 'hamlit/compilers/runtime_attribute'
require 'hamlit/concerns/escapable'
require 'hamlit/concerns/included'
@ -9,6 +10,7 @@ module Hamlit
extend Concerns::Included
include Compilers::NewAttribute
include Compilers::OldAttribute
include Compilers::RuntimeAttribute
included do
include Concerns::Escapable
@ -21,6 +23,13 @@ module Hamlit
attrs = join_ids(attrs)
attrs = combine_classes(attrs)
attrs = pull_class_first(attrs)
if has_runtime_attribute?(attrs) && has_attr?(attrs, 'class', 'id')
attrs = merge_runtime_attributes(attrs)
return [:html, :attrs, *escape_attribute_values(attrs)]
end
attrs = attrs.map { |a| compile(a) }
[:html, :attrs, *escape_attribute_values(attrs)]
end
@ -72,9 +81,13 @@ module Hamlit
[[:html, :attr, 'id', [:multi, *insert_static(values, '_')]]] + (attrs - id_attrs)
end
def filter_attrs(attrs, target)
class_attrs = attrs.select do |html, attr, name, content|
name == target
def has_attr?(attrs, *targets)
filter_attrs(attrs, *targets).any?
end
def filter_attrs(attrs, *targets)
attrs.select do |html, attr, name, content|
targets.include?(name)
end
end

View file

@ -1,4 +1,3 @@
require 'hamlit/attribute'
require 'hamlit/concerns/attribute_builder'
require 'hamlit/concerns/balanceable'
require 'hamlit/concerns/ripperable'
@ -31,7 +30,8 @@ module Hamlit
[:html, :attr, key, [:dynamic, value]]
end
rescue RuntimeBuild
return runtime_build(str)
# Give up static compilation when given string is invalid as ruby hash
[[:runtime, str]]
end
private
@ -48,7 +48,7 @@ module Hamlit
end
end
# Give up static copmile when variables are detected.
# Give up static compilation when variables are detected.
def assert_static_value!(value)
tokens = Ripper.lex(value)
tokens.each do |(row, col), type, str|
@ -114,13 +114,6 @@ module Hamlit
raise SyntaxError unless type == :on_op && str == '=>'
end
def runtime_build(str)
str = str.gsub(/(\A\{|\}\Z)/, '')
quote = options[:attr_quote].inspect
code = "::Hamlit::Attribute.build(#{quote}, #{str})"
[[:dynamic, code]]
end
def split_hash(str)
columns = HashParser.assoc_columns(str)
columns = reject_nested_columns(str, columns)

View file

@ -0,0 +1,58 @@
require 'hamlit/attribute'
require 'hamlit/concerns/string_interpolation'
# This module compiles :runtime sexp. It is a special version of
# old-style attribute which is built on runtime.
module Hamlit
module Compilers
module RuntimeAttribute
include Concerns::StringInterpolation
# This is used for compiling only runtime attribute in Compilers::Attribute.
def on_runtime(str)
compile_runtime_attribute(str)
end
# Given html attrs, merge classes and ids to :dynamic_attribute.
def merge_runtime_attributes(attrs)
merge_targets = filter_attrs(attrs, 'id', 'class')
dynamic_attr = attrs.find { |exp, *args| exp == :runtime }
attrs -= merge_targets
attrs.delete(dynamic_attr)
base = decompile_temple_attrs(merge_targets)
[compile_runtime_attribute(dynamic_attr.last, base), *attrs]
end
private
def compile_runtime_attribute(str, base = nil)
str = str.gsub(/(\A\{|\}\Z)/, '')
quote = options[:attr_quote].inspect
code = "::Hamlit::Attribute.build(#{[quote, str, base].compact.join(', ')})"
[:dynamic, code]
end
def has_runtime_attribute?(attrs)
attrs.any? do |exp, *args|
exp == :runtime
end
end
# Given attrs in temple ast, return an attribute as hash literal.
def decompile_temple_attrs(attrs)
literal = '{'
attrs.each do |html, attr, name, (type, value)|
case type
when :static
literal += ":#{name}=>#{string_literal(value)},"
when :dynamic
literal += ":#{name}=>#{value},"
end
end
literal.gsub(/,\Z/, '') + '}'
end
end
end
end

View file

@ -41,5 +41,43 @@ describe Hamlit::Engine do
HTML
end
end
describe 'element class with attribute class' do
it 'does not generate double classes' do
assert_render(<<-HAML, <<-HTML)
.item(class='first')
HAML
<div class='first item'></div>
HTML
end
it 'does not generate double classes for a variable' do
assert_render(<<-HAML, <<-HTML)
- val = 'val'
.element(class=val)
HAML
<div class='element val'></div>
HTML
end
end
describe 'element id with attribute id' do
it 'concatenates ids with underscore' do
assert_render(<<-HAML, <<-HTML)
#item(id='first')
HAML
<div id='item_first'></div>
HTML
end
it 'concatenates ids with underscore for a variable' do
assert_render(<<-HAML, <<-HTML)
- val = 'first'
#item(id=val)
HAML
<div id='item_first'></div>
HTML
end
end
end
end

View file

@ -160,7 +160,7 @@ describe Hamlit::Engine do
end
end
describe 'element class with attributes class' do
describe 'element class with attribute class' do
it 'does not generate double classes' do
assert_render(<<-HAML, <<-HTML)
.item{ class: 'first' }
@ -177,6 +177,52 @@ describe Hamlit::Engine do
<div class='element val'></div>
HTML
end
it 'does not generate double classes for hash attributes' do
assert_render(<<-HAML, <<-HTML)
- hash = { class: 'val' }
.element{ hash }
HAML
<div class='element val'></div>
HTML
end
end
describe 'element id with attribute id' do
it 'does not generate double ids' do
assert_render(<<-HAML, <<-HTML)
#item{ id: 'first' }
HAML
<div id='item_first'></div>
HTML
end
it 'does not generate double ids for a variable' do
assert_render(<<-HAML, <<-HTML)
- val = 'first'
#item{ id: val }
HAML
<div id='item_first'></div>
HTML
end
it 'does not generate double ids for hash attributes' do
assert_render(<<-HAML, <<-HTML)
- hash = { id: 'first' }
#item{ hash }
HAML
<div id='item_first'></div>
HTML
end
it 'does not generate double ids and classes for hash attributes' do
assert_render(<<-HAML, <<-HTML)
- hash = { id: 'first', class: 'foo' }
#item.bar{ hash }
HAML
<div class='bar foo' id='item_first'></div>
HTML
end
end
end
end