Skip to content

Commit

Permalink
Merge pull request #4 from doomspork/update-rendering
Browse files Browse the repository at this point in the history
Update rendering flow
  • Loading branch information
doomspork committed Aug 4, 2015
2 parents b13f504 + 359fa9a commit e6ad608
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 108 deletions.
26 changes: 6 additions & 20 deletions lib/slim_fast.ex
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
57 changes: 57 additions & 0 deletions lib/slim_fast/compiler.ex
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
75 changes: 28 additions & 47 deletions lib/slim_fast/renderer.ex
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

33 changes: 33 additions & 0 deletions test/compiler_test.exs
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
45 changes: 23 additions & 22 deletions test/renderer_test.exs
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
20 changes: 1 addition & 19 deletions test/slim_fast_test.exs
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

0 comments on commit e6ad608

Please sign in to comment.