mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
[Haml] Allow :class and :id attributes to accept arrays, which will be joined.
This commit is contained in:
parent
6324cfe058
commit
782f44f553
8 changed files with 141 additions and 0 deletions
|
@ -66,6 +66,40 @@ won't do any indentation of their arguments.
|
||||||
Their `#to_s` method will be called to convert them to strings.
|
Their `#to_s` method will be called to convert them to strings.
|
||||||
Previously, this only worked for attributes other than `class`.
|
Previously, this only worked for attributes other than `class`.
|
||||||
|
|
||||||
|
### `:class` and `:id` Attributes Accept Ruby Arrays
|
||||||
|
|
||||||
|
In an attribute hash, the `:class` attribute now accepts an Array
|
||||||
|
whose elements will be converted to strings and joined with `" "`.
|
||||||
|
Likewise, the `:id` attribute now accepts an Array
|
||||||
|
whose elements will be converted to strings and joined with `"_"`.
|
||||||
|
The array will first be flattened and any elements that do not test as true
|
||||||
|
will be stripped out. For example:
|
||||||
|
|
||||||
|
.column{:class => [@item.type, @item == @sortcol && [:sort, @sortdir]] }
|
||||||
|
|
||||||
|
could render as any of:
|
||||||
|
|
||||||
|
class="column numeric sort ascending"
|
||||||
|
class="column numeric"
|
||||||
|
class="column sort descending"
|
||||||
|
class="column"
|
||||||
|
|
||||||
|
depending on whether `@item.type` is `"numeric"` or `nil`,
|
||||||
|
whether `@item == @sortcol`,
|
||||||
|
and whether `@sortdir` is `"ascending"` or `"descending"`.
|
||||||
|
|
||||||
|
A single value can still be specified.
|
||||||
|
If that value evaluates to false it is ignored;
|
||||||
|
otherwise it gets converted to a string.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.item{:class => @item.is_empty? && "empty"}
|
||||||
|
|
||||||
|
could render as either of:
|
||||||
|
|
||||||
|
class="item"
|
||||||
|
class="item empty"
|
||||||
|
|
||||||
### More Powerful `:autoclose` Option
|
### More Powerful `:autoclose` Option
|
||||||
|
|
||||||
The {file:HAML_REFERENCE.md#attributes_option `:attributes`} option
|
The {file:HAML_REFERENCE.md#attributes_option `:attributes`} option
|
||||||
|
|
|
@ -310,6 +310,50 @@ is compiled to:
|
||||||
|
|
||||||
<script src='javascripts/script_9' type='text/javascript'></script>
|
<script src='javascripts/script_9' type='text/javascript'></script>
|
||||||
|
|
||||||
|
#### `:class` and `:id` Attributes
|
||||||
|
{#class-and-id-attributes}
|
||||||
|
|
||||||
|
The `:class` and `:id` attributes can also be specified as a Ruby array
|
||||||
|
whose elements will be joined together.
|
||||||
|
A `:class` array is joined with `" "`
|
||||||
|
and an `:id` array is joined with `"_"`.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
%div{:id => [@item.type, @item.number], :class => [@item.type, @item.urgency]}
|
||||||
|
|
||||||
|
is equivalent to:
|
||||||
|
|
||||||
|
%div{:id => "#{@item.type}_#{@item.number}", :class => "#{@item.type} #{@item.urgency}"}
|
||||||
|
|
||||||
|
The array will first be flattened
|
||||||
|
and any elements that do not test as true will be removed.
|
||||||
|
The remaining elements will be converted to strings.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
%div{:class => [@item.type, @item == @sortcol && [:sort, @sortdir]] } Contents
|
||||||
|
|
||||||
|
could render as any of:
|
||||||
|
|
||||||
|
<div class="column numeric sort ascending">Contents</div>
|
||||||
|
<div class="column numeric">Contents</div>
|
||||||
|
<div class="column sort descending">Contents</div>
|
||||||
|
<div class="column">Contents</div>
|
||||||
|
|
||||||
|
depending on whether `@item.type` is `"numeric"` or `nil`,
|
||||||
|
whether `@item == @sortcol`,
|
||||||
|
and whether `@sortdir` is `"ascending"` or `"descending"`.
|
||||||
|
|
||||||
|
If a single value is specified and it evaluates to false it is ignored;
|
||||||
|
otherwise it gets converted to a string.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.item{:class => @item.is_empty? && "empty"}
|
||||||
|
|
||||||
|
could render as either of:
|
||||||
|
|
||||||
|
class="item"
|
||||||
|
class="item empty"
|
||||||
|
|
||||||
#### HTML-style Attributes: `()`
|
#### HTML-style Attributes: `()`
|
||||||
|
|
||||||
Haml also supports a terser, less Ruby-specific attribute syntax
|
Haml also supports a terser, less Ruby-specific attribute syntax
|
||||||
|
@ -472,6 +516,22 @@ is compiled to:
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
These shortcuts can be combined with long-hand attributes;
|
||||||
|
the two values will be merged together
|
||||||
|
as though they were all placed in an array
|
||||||
|
(see [the documentation on `:class` and `:id` attributes](#class-and-id-attributes)).
|
||||||
|
For example:
|
||||||
|
|
||||||
|
%div#Article.article.entry{:id => @article.number, :class => @article.visibility}
|
||||||
|
|
||||||
|
is equivalent to
|
||||||
|
|
||||||
|
%div{:id => ['Article', @article.number], :class => ['article', 'entry', @article.visibility]} Gabba Hey
|
||||||
|
|
||||||
|
and could compile to:
|
||||||
|
|
||||||
|
<div class="article entry visible" id="Article_27">Gabba Hey</div>
|
||||||
|
|
||||||
#### Implicit Div Elements
|
#### Implicit Div Elements
|
||||||
|
|
||||||
Because divs are used so often, they're the default elements.
|
Because divs are used so often, they're the default elements.
|
||||||
|
|
|
@ -244,12 +244,14 @@ RUBY
|
||||||
# @param from [{String => #to_s}] The attribute hash to merge from
|
# @param from [{String => #to_s}] The attribute hash to merge from
|
||||||
# @return [{String => String}] `to`, after being merged
|
# @return [{String => String}] `to`, after being merged
|
||||||
def self.merge_attrs(to, from)
|
def self.merge_attrs(to, from)
|
||||||
|
from['id'] = Precompiler.filter_and_join(from['id'], '_') if from['id']
|
||||||
if to['id'] && from['id']
|
if to['id'] && from['id']
|
||||||
to['id'] << '_' << from.delete('id').to_s
|
to['id'] << '_' << from.delete('id').to_s
|
||||||
elsif to['id'] || from['id']
|
elsif to['id'] || from['id']
|
||||||
from['id'] ||= to['id']
|
from['id'] ||= to['id']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
from['class'] = Precompiler.filter_and_join(from['class'], ' ') if from['class']
|
||||||
if to['class'] && from['class']
|
if to['class'] && from['class']
|
||||||
# Make sure we don't duplicate class names
|
# Make sure we don't duplicate class names
|
||||||
from['class'] = (from['class'].to_s.split(' ') | to['class'].split(' ')).sort.join(' ')
|
from['class'] = (from['class'].to_s.split(' ') | to['class'].split(' ')).sort.join(' ')
|
||||||
|
|
|
@ -545,6 +545,9 @@ END
|
||||||
result = attributes.collect do |attr, value|
|
result = attributes.collect do |attr, value|
|
||||||
next if value.nil?
|
next if value.nil?
|
||||||
|
|
||||||
|
value = filter_and_join(value, ' ') if attr == :class
|
||||||
|
value = filter_and_join(value, '_') if attr == :id
|
||||||
|
|
||||||
if value == true
|
if value == true
|
||||||
next " #{attr}" if is_html
|
next " #{attr}" if is_html
|
||||||
next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
|
next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
|
||||||
|
@ -568,6 +571,11 @@ END
|
||||||
result.compact.sort.join
|
result.compact.sort.join
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.filter_and_join(value, separator)
|
||||||
|
value = [value] unless value.is_a?(Array)
|
||||||
|
return value.flatten.collect {|item| item ? item.to_s : nil}.compact.join(separator)
|
||||||
|
end
|
||||||
|
|
||||||
def prerender_tag(name, self_close, attributes)
|
def prerender_tag(name, self_close, attributes)
|
||||||
attributes_string = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
|
attributes_string = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
|
||||||
"<#{name}#{attributes_string}#{self_close && xhtml? ? ' /' : ''}>"
|
"<#{name}#{attributes_string}#{self_close && xhtml? ? ' /' : ''}>"
|
||||||
|
|
|
@ -137,6 +137,29 @@ MESSAGE
|
||||||
assert_equal("<p class='3'>foo</p>", render("%p{:class => 1+2} foo").chomp)
|
assert_equal("<p class='3'>foo</p>", render("%p{:class => 1+2} foo").chomp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_class_attr_with_array
|
||||||
|
assert_equal("<p class='a b'>foo</p>\n", render("%p{:class => %w[a b]} foo")) # basic
|
||||||
|
assert_equal("<p class='a b css'>foo</p>\n", render("%p.css{:class => %w[a b]} foo")) # merge with css
|
||||||
|
assert_equal("<p class='b css'>foo</p>\n", render("%p.css{:class => %w[css b]} foo")) # merge uniquely
|
||||||
|
assert_equal("<p class='a b c d'>foo</p>\n", render("%p{:class => [%w[a b], %w[c d]]} foo")) # flatten
|
||||||
|
assert_equal("<p class='a b'>foo</p>\n", render("%p{:class => [:a, :b] } foo")) # stringify
|
||||||
|
assert_equal("<p class=''>foo</p>\n", render("%p{:class => [nil, false] } foo")) # strip falsey
|
||||||
|
assert_equal("<p class='a'>foo</p>\n", render("%p{:class => :a} foo")) # single stringify
|
||||||
|
assert_equal("<p>foo</p>\n", render("%p{:class => false} foo")) # single falsey
|
||||||
|
assert_equal("<p class='a b html'>foo</p>\n", render("%p(class='html'){:class => %w[a b]} foo")) # html attrs
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_id_attr_with_array
|
||||||
|
assert_equal("<p id='a_b'>foo</p>\n", render("%p{:id => %w[a b]} foo")) # basic
|
||||||
|
assert_equal("<p id='css_a_b'>foo</p>\n", render("%p#css{:id => %w[a b]} foo")) # merge with css
|
||||||
|
assert_equal("<p id='a_b_c_d'>foo</p>\n", render("%p{:id => [%w[a b], %w[c d]]} foo")) # flatten
|
||||||
|
assert_equal("<p id='a_b'>foo</p>\n", render("%p{:id => [:a, :b] } foo")) # stringify
|
||||||
|
assert_equal("<p id=''>foo</p>\n", render("%p{:id => [nil, false] } foo")) # strip falsey
|
||||||
|
assert_equal("<p id='a'>foo</p>\n", render("%p{:id => :a} foo")) # single stringify
|
||||||
|
assert_equal("<p>foo</p>\n", render("%p{:id => false} foo")) # single falsey
|
||||||
|
assert_equal("<p id='html_a_b'>foo</p>\n", render("%p(id='html'){:id => %w[a b]} foo")) # html attrs
|
||||||
|
end
|
||||||
|
|
||||||
def test_dynamic_attributes_with_no_content
|
def test_dynamic_attributes_with_no_content
|
||||||
assert_equal(<<HTML, render(<<HAML))
|
assert_equal(<<HTML, render(<<HAML))
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -198,6 +198,16 @@ HAML
|
||||||
assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'"))
|
assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_haml_tag_with_class_array
|
||||||
|
assert_equal("<p class='a b'>foo</p>\n", render("- haml_tag :p, 'foo', :class => %w[a b]"))
|
||||||
|
assert_equal("<p class='a b c d'>foo</p>\n", render("- haml_tag 'p.c.d', 'foo', :class => %w[a b]"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_haml_tag_with_id_array
|
||||||
|
assert_equal("<p id='a_b'>foo</p>\n", render("- haml_tag :p, 'foo', :id => %w[a b]"))
|
||||||
|
assert_equal("<p id='c_a_b'>foo</p>\n", render("- haml_tag 'p#c', 'foo', :id => %w[a b]"))
|
||||||
|
end
|
||||||
|
|
||||||
def test_haml_tag_non_autoclosed_tags_arent_closed
|
def test_haml_tag_non_autoclosed_tags_arent_closed
|
||||||
assert_equal("<p></p>\n", render("- haml_tag :p"))
|
assert_equal("<p></p>\n", render("- haml_tag :p"))
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,9 @@ testtest
|
||||||
</br>
|
</br>
|
||||||
<p class='article bar foo' id='article_1'>Blah</p>
|
<p class='article bar foo' id='article_1'>Blah</p>
|
||||||
<p class='article foo' id='article_1'>Blah</p>
|
<p class='article foo' id='article_1'>Blah</p>
|
||||||
|
<p class='article bar baz foo' id='article_1'>Blah</p>
|
||||||
<p class='article quux qux' id='article_1'>Blump</p>
|
<p class='article quux qux' id='article_1'>Blump</p>
|
||||||
|
<p class='article' id='foo_bar_baz_article_1'>Whee</p>
|
||||||
Woah inner quotes
|
Woah inner quotes
|
||||||
<p class='dynamic_quote' dyn='3' quotes="single '"></p>
|
<p class='dynamic_quote' dyn='3' quotes="single '"></p>
|
||||||
<p class='dynamic_self_closing' dyn='3' />
|
<p class='dynamic_self_closing' dyn='3' />
|
||||||
|
|
|
@ -70,7 +70,9 @@
|
||||||
Nested content
|
Nested content
|
||||||
%p.foo{:class => true ? 'bar' : 'baz'}[@article] Blah
|
%p.foo{:class => true ? 'bar' : 'baz'}[@article] Blah
|
||||||
%p.foo{:class => false ? 'bar' : ''}[@article] Blah
|
%p.foo{:class => false ? 'bar' : ''}[@article] Blah
|
||||||
|
%p.foo{:class => %w[bar baz]}[@article] Blah
|
||||||
%p.qux{:class => 'quux'}[@article] Blump
|
%p.qux{:class => 'quux'}[@article] Blump
|
||||||
|
%p#foo{:id => %w[bar baz]}[@article] Whee
|
||||||
== #{"Woah inner quotes"}
|
== #{"Woah inner quotes"}
|
||||||
%p.dynamic_quote{:quotes => "single '", :dyn => 1 + 2}
|
%p.dynamic_quote{:quotes => "single '", :dyn => 1 + 2}
|
||||||
%p.dynamic_self_closing{:dyn => 1 + 2}/
|
%p.dynamic_self_closing{:dyn => 1 + 2}/
|
||||||
|
|
Loading…
Add table
Reference in a new issue