-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from doomspork/update-rendering
Update rendering flow
- Loading branch information
Showing
6 changed files
with
148 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,11 @@ | ||
defmodule SlimFast do | ||
import SlimFast.Parser | ||
import SlimFast.Renderer | ||
import SlimFast.Tree | ||
use SlimFast.Renderer | ||
|
||
require EEx | ||
defmacro __using__([]) do | ||
quote do | ||
import unquote __MODULE__ | ||
|
||
def evaluate(input, binding \\ []) do | ||
input | ||
|> tokenize | ||
|> parse_lines | ||
|> build_tree | ||
|> render | ||
|> eval(binding) | ||
end | ||
|
||
defp eval(html, []), do: html | ||
defp eval(html, binding) do | ||
html |> EEx.eval_string(binding) | ||
end | ||
|
||
defp tokenize(input, delim \\ "\n") do | ||
String.split(input, delim) | ||
use SlimFast.Renderer | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
defmodule SlimFast.Compiler do | ||
@self_closing [:area, :br, :col, :doctype, :embed, :hr, :img, :input, :link, :meta] | ||
|
||
def compile(tree) do | ||
tree | ||
|> Enum.map(fn branch -> render_branch(branch) end) | ||
|> Enum.join | ||
end | ||
|
||
defp render_attribute(_, []), do: "" | ||
defp render_attribute(_, ""), do: "" | ||
defp render_attribute(name, value) do | ||
value = cond do | ||
is_binary(value) -> | ||
"\"" <> value <> "\"" | ||
is_list(value) -> | ||
"\"" <> Enum.join(value, " ") <> "\"" | ||
is_tuple(value) -> | ||
{_, attrs} = value | ||
"<%=" <> attrs[:content] <> "%>" | ||
true -> to_string(value) | ||
end | ||
|
||
to_string(name) <> "=" <> value | ||
end | ||
|
||
defp render_branch(%{type: :doctype, content: text}), do: text | ||
defp render_branch(%{type: :text, content: text}), do: text | ||
defp render_branch(%{} = branch) do | ||
opening = branch.attributes | ||
|> Enum.map(fn {k, v} -> render_attribute(k, v) end) | ||
|> Enum.join(" ") | ||
|> open(branch) | ||
|
||
closing = close(branch) | ||
opening <> compile(branch.children) <> closing | ||
end | ||
|
||
defp open(_, %{type: :eex, content: code, attributes: attrs}) do | ||
inline = if attrs[:inline], do: "=", else: "" | ||
"<%#{inline} #{code} %>" | ||
end | ||
|
||
defp open(attrs, %{type: type}) do | ||
type = String.rstrip("#{type} #{attrs}") | ||
"<#{type}>" | ||
end | ||
|
||
defp close(%{type: type}) when type in @self_closing, do: "" | ||
defp close(%{type: :eex, content: code}) do | ||
cond do | ||
Regex.match? ~r/(fn.*->|do:?)/, code -> "<% end %>" | ||
true -> "" | ||
end | ||
end | ||
defp close(%{type: type}), do: "</#{type}>" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,40 @@ | ||
defmodule SlimFast.Renderer do | ||
alias SlimFast.Tree.Branch | ||
|
||
@self_closing [:area, :br, :col, :doctype, :embed, :hr, :img, :input, :link, :meta] | ||
|
||
def render(tree) do | ||
tree | ||
|> Enum.map(fn branch -> render_branch(branch) end) | ||
|> Enum.join | ||
import SlimFast.Parser | ||
import SlimFast.Compiler | ||
import SlimFast.Tree | ||
|
||
def precompile(input) do | ||
input | ||
|> tokenize | ||
|> parse_lines | ||
|> build_tree | ||
|> compile | ||
end | ||
|
||
defp render_attribute(_, []), do: "" | ||
defp render_attribute(_, ""), do: "" | ||
defp render_attribute(name, value) do | ||
value = cond do | ||
is_binary(value) -> | ||
"\"" <> value <> "\"" | ||
is_list(value) -> | ||
"\"" <> Enum.join(value, " ") <> "\"" | ||
is_tuple(value) -> | ||
{_, attrs} = value | ||
"<%=" <> attrs[:content] <> "%>" | ||
true -> to_string(value) | ||
end | ||
|
||
to_string(name) <> "=" <> value | ||
def eval(html, []), do: html | ||
def eval(html, binding) do | ||
html |> EEx.eval_string(binding) | ||
end | ||
|
||
defp render_branch(%Branch{type: :doctype, content: text}), do: text | ||
defp render_branch(%Branch{type: :text, content: text}), do: text | ||
defp render_branch(%Branch{} = branch) do | ||
opening = branch.attributes | ||
|> Enum.map(fn {k, v} -> render_attribute(k, v) end) | ||
|> Enum.join(" ") | ||
|> render_open(branch) | ||
|
||
closing = render_close(branch) | ||
opening <> render(branch.children) <> closing | ||
def tokenize(input, delim \\ "\n") do | ||
String.split(input, delim) | ||
end | ||
|
||
defp render_open(_, %Branch{type: :eex, content: code, attributes: attrs}) do | ||
inline = if attrs[:inline], do: "=", else: "" | ||
"<%#{inline} #{code} %>" | ||
end | ||
defmacro __using__([]) do | ||
quote do | ||
import unquote __MODULE__ | ||
import SlimFast.Parser | ||
import SlimFast.Compiler | ||
import SlimFast.Tree | ||
|
||
defp render_open(attrs, %Branch{type: type}) do | ||
type = String.rstrip("#{type} #{attrs}") | ||
"<#{type}>" | ||
end | ||
require EEx | ||
|
||
defp render_close(%Branch{type: type}) when type in @self_closing, do: "" | ||
defp render_close(%Branch{type: :eex, content: code}) do | ||
cond do | ||
Regex.match? ~r/(fn.*->|do:?)/, code -> "<% end %>" | ||
true -> "" | ||
def render(slim, args \\ []) do | ||
slim | ||
|> precompile | ||
|> eval(args) | ||
end | ||
end | ||
end | ||
defp render_close(%Branch{type: type}), do: "</#{type}>" | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
defmodule CompilerTest do | ||
use ExUnit.Case, async: true | ||
|
||
alias SlimFast.Tree.Branch | ||
alias SlimFast.Compiler | ||
|
||
test "renders simple nesting" do | ||
tree = [%Branch{type: :div, | ||
attributes: [id: {:eex, content: "variable"}, class: ["class"]], | ||
children: [%Branch{type: :p, | ||
children: [%Branch{type: :text, | ||
children: [], | ||
content: "Hello World"}]}]}] | ||
|
||
expected = "<div id=<%=variable%> class=\"class\"><p>Hello World</p></div>" | ||
assert Compiler.compile(tree) == expected | ||
end | ||
|
||
test "renders doctype" do | ||
tree = [%Branch{type: :doctype, content: "<!DOCTYPE html>"}] | ||
assert Compiler.compile(tree) == "<!DOCTYPE html>" | ||
end | ||
|
||
test "renders eex" do | ||
tree = [%Branch{type: :title, | ||
children: [%Branch{type: :eex, | ||
content: "site_title", | ||
attributes: [inline: true]}]}] | ||
|
||
expected = "<title><%= site_title %></title>" | ||
assert Compiler.compile(tree) == expected | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,34 @@ | ||
defmodule RendererTest do | ||
use ExUnit.Case, async: true | ||
|
||
alias SlimFast.Tree.Branch | ||
alias SlimFast.Renderer | ||
use SlimFast.Renderer | ||
|
||
test "renders simple nesting" do | ||
tree = [%Branch{type: :div, | ||
attributes: [id: {:eex, content: "variable"}, class: ["class"]], | ||
children: [%Branch{type: :p, | ||
children: [%Branch{type: :text, | ||
children: [], | ||
content: "Hello World"}]}]}] | ||
@slim """ | ||
doctype html | ||
html | ||
head | ||
meta name="keywords" description="slim fast" | ||
title = site_title | ||
body | ||
#id.class | ||
ul | ||
= Enum.map [1, 2], fn x -> | ||
li = x | ||
""" | ||
|
||
expected = "<div id=<%=variable%> class=\"class\"><p>Hello World</p></div>" | ||
assert Renderer.render(tree) == expected | ||
end | ||
@html "<!DOCTYPE html><html><head><meta description=\"slim fast\" name=\"keywords\"><title>Website Title</title></head><body><div class=\"class\" id=\"id\"><ul><li>1</li><li>2</li></ul></div></body></html>" | ||
|
||
@eex "<!DOCTYPE html><html><head><meta description=\"slim fast\" name=\"keywords\"><title><%= site_title %></title></head><body><div class=\"class\" id=\"id\"><ul><%= Enum.map [1, 2], fn x -> %><li><%= x %></li><% end %></ul></div></body></html>" | ||
|
||
test "renders doctype" do | ||
tree = [%Branch{type: :doctype, content: "<!DOCTYPE html>"}] | ||
assert Renderer.render(tree) == "<!DOCTYPE html>" | ||
test "precompiles eex template" do | ||
assert precompile(@slim) == @eex | ||
end | ||
|
||
test "renders eex" do | ||
tree = [%Branch{type: :title, | ||
children: [%Branch{type: :eex, | ||
content: "site_title", | ||
attributes: [inline: true]}]}] | ||
test "evaluates eex templates" do | ||
assert eval(@eex, site_title: "Website Title") == @html | ||
end | ||
|
||
expected = "<title><%= site_title %></title>" | ||
assert Renderer.render(tree) == expected | ||
test "render html" do | ||
assert render(@slim, site_title: "Website Title") == @html | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,4 @@ | ||
defmodule SlimFastTest do | ||
use ExUnit.Case, async: true | ||
use ExUnit.Case | ||
|
||
@slim """ | ||
doctype html | ||
html | ||
head | ||
meta name="keywords" description="slim fast" | ||
title = site_title | ||
body | ||
#id.class | ||
ul | ||
= Enum.map [1, 2], fn x -> | ||
li = x | ||
""" | ||
|
||
@htmleex "<!DOCTYPE html><html><head><meta description=\"slim fast\" name=\"keywords\"><title>Website Title</title></head><body><div class=\"class\" id=\"id\"><ul><li>1</li><li>2</li></ul></div></body></html>" | ||
|
||
test "render html" do | ||
assert SlimFast.evaluate(@slim, site_title: "Website Title") == @htmleex | ||
end | ||
end |