Skip to content

Commit

Permalink
Merge pull request #26 from doomspork/attribute_merging
Browse files Browse the repository at this point in the history
Add attribute merging support
  • Loading branch information
doomspork committed Aug 12, 2015
2 parents 4740a60 + 62ff832 commit 9adb126
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 5 deletions.
7 changes: 6 additions & 1 deletion lib/slim_fast/parser.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule SlimFast.Parser do
alias SlimFast.Parser.AttributesKeyword

@blank ""
@content "|"
@comment "/"
Expand All @@ -22,6 +24,8 @@ defmodule SlimFast.Parser do
@tabsize 2
@soft_tab String.duplicate(" ", @tabsize)

@merge_attrs %{class: " "}

def parse_lines(lines) do
parse_lines(Enum.map(lines, &use_soft_tabs/1), [])
end
Expand Down Expand Up @@ -171,8 +175,9 @@ defmodule SlimFast.Parser do

additional = parts["attrs"] |> html_attributes
children = parts["tail"] |> String.lstrip |> inline_children
attributes = AttributesKeyword.merge(basics ++ additional, @merge_attrs)

{tag, attributes: basics ++ additional, children: children}
{tag, attributes: attributes, children: children}
end

defp parse_tag(input) do
Expand Down
46 changes: 46 additions & 0 deletions lib/slim_fast/parser/attributes_keyword.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule SlimFast.Parser.AttributesKeyword do
@doc """
Merges multiply attributes values for keys specified in merge_rules.
Attribute value may be given by string, list, or {:eex, args} node
`merge_rules` should me an `%{attribute_name: joining_character}` map
## Examples
iex> SlimFast.Parser.AttributesKeyword.merge(
...> [class: "a", class: ["b", "c"], class: "d"],
...> %{class: " "}
...> )
[class: "a b c d"]
iex> SlimFast.Parser.AttributesKeyword.merge(
...> [class: "a", class: ["b", "c"], class: {:eex, content: "d"}],
...> %{class: " "}
...> )
[class: {:eex, content: ~S("a b c \#{d}"), inline: true}]
"""
def merge(keyword_list, merge_rules) do
Enum.reduce(merge_rules, keyword_list, fn ({attr, join}, result) ->
case Keyword.get_values(result, attr) do
[] -> result
values -> Keyword.put(result, attr, merge_attribute_values(values, join))
end
end)
end

defp merge_attribute_values(values, join_by) do
result = join_attribute_values(values, join_by)
if Enum.any?(values, &dynamic_value?/1) do
{:eex, content: ~s("#{result}"), inline: true}
else
result
end
end

defp dynamic_value?({:eex, _}), do: true
defp dynamic_value?(_), do: false

defp join_attribute_values(values, join_by) do
values |> Enum.map(&attribute_val/1) |> List.flatten |> Enum.join(join_by)
end

defp attribute_val({:eex, args}), do: "\#{" <> args[:content] <> "}"
defp attribute_val(value), do: value
end
36 changes: 36 additions & 0 deletions test/parser/attributes_keyword_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule ParserAttributesKeywordTest do
use ExUnit.Case
doctest SlimFast.Parser.AttributesKeyword

test "handles multiple eex nodes" do
result = SlimFast.Parser.AttributesKeyword.merge(
[class: "a", class: {:eex, content: "b"}, class: {:eex, content: "c"}],
%{class: " "}
)
assert result == [class: {:eex, content: ~S("a #{b} #{c}"), inline: true}]
end

test "supports custom delimiter" do
result = SlimFast.Parser.AttributesKeyword.merge(
[class: "a", class: "b"],
%{class: "--"}
)
assert result == [class: "a--b"]
end

test "leaves unspecified attributes as is" do
result = SlimFast.Parser.AttributesKeyword.merge(
[class: "a", id: "id", class: "b", id: "id1"],
%{class: " "}
)
assert result == [class: "a b", id: "id", id: "id1"]
end

test "handles all attributes specified in merge rules" do
result = SlimFast.Parser.AttributesKeyword.merge(
[class: "a", id: "id", class: "b", id: "id1"],
%{class: " ", id: "+"}
)
assert result == [id: "id+id1", class: "a b"]
end
end
8 changes: 4 additions & 4 deletions test/parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ defmodule ParserTest do

test "parses simple nesting" do
parsed = ["#id.class", "\tp", "\t| Hello World"] |> Parser.parse_lines
assert parsed == [{0, {:div, attributes: [class: ["class"], id: "id"], children: []}}, {2, {:p, attributes: [], children: []}}, {2, "Hello World"}]
assert parsed == [{0, {:div, attributes: [class: "class", id: "id"], children: []}}, {2, {:p, attributes: [], children: []}}, {2, "Hello World"}]

parsed = ["#id.class","\tp Hello World"] |> Parser.parse_lines
assert parsed == [{0, {:div, attributes: [class: ["class"], id: "id"], children: []}}, {2, {:p, attributes: [], children: ["Hello World"]}}]
assert parsed == [{0, {:div, attributes: [class: "class", id: "id"], children: []}}, {2, {:p, attributes: [], children: ["Hello World"]}}]
end

test "parses css classes with dashes" do
{_, {:div, opts}} = ".my-css-class test"
|> Parser.parse_line

assert opts == [attributes: [class: ["my-css-class"]], children: ["test"]]
assert opts == [attributes: [class: "my-css-class"], children: ["test"]]
end

test "parses attributes" do
Expand Down Expand Up @@ -82,7 +82,7 @@ defmodule ParserTest do

test "parses final newline properly" do
parsed = ["#id.class", "\tp", "\t| Hello World", ""] |> Parser.parse_lines
assert parsed == [{0, {:div, attributes: [class: ["class"], id: "id"], children: []}}, {2, {:p, attributes: [], children: []}}, {2, "Hello World"}]
assert parsed == [{0, {:div, attributes: [class: "class", id: "id"], children: []}}, {2, {:p, attributes: [], children: []}}, {2, "Hello World"}]
end

test "parses html comments" do
Expand Down
4 changes: 4 additions & 0 deletions test/renderer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,8 @@ defmodule RendererTest do

assert render(slim) == ~s(<div style="display: none"></div>)
end

test "render tags with attrbiute merging" do
assert render(~s(.class-one class="class-two")) == ~s(<div class="class-one class-two"></div>)
end
end

0 comments on commit 9adb126

Please sign in to comment.