Skip to content

Commit

Permalink
fix embedded engine rendering, raise an error if the engine is unknown
Browse files Browse the repository at this point in the history
  • Loading branch information
little-bobby-tables authored and doomspork committed Jun 9, 2017
1 parent c8e4bc5 commit 5db4235
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 52 deletions.
50 changes: 20 additions & 30 deletions lib/slime/parser/embedded_engine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Slime.Parser.EmbeddedEngine do
@type parser_tag :: binary | {:eex | binary, Keyword.t}
@callback render(binary, Keyword.t) :: parser_tag

import Slime.Compiler, only: [compile: 1]
import Slime.Parser.TextBlock, only: [render_content: 2]

@engines %{
javascript: Slime.Parser.EmbeddedEngine.Javascript,
Expand All @@ -16,37 +16,22 @@ defmodule Slime.Parser.EmbeddedEngine do
}
|> Map.merge(Application.get_env(:slime, :embedded_engines, %{}))
|> Enum.into(%{}, fn ({key, value}) -> {to_string(key), value} end)
@registered_engines Map.keys(@engines)

def render_with_engine(engine, line_contents) when is_list(line_contents) do
lines = Enum.map(line_contents, &compile/1)
embedded_text = case lines do
[] -> ""
[line | _] ->
strip_indent = indent(line)
lines
|> Enum.map(&strip_line(&1, strip_indent))
|> Enum.join("\n")
end
def parse(engine, lines) when engine in @registered_engines do
embedded_text = render_content(lines, 0)

render_with_engine(engine, embedded_text)
{:ok, render_with_engine(engine, embedded_text)}
end

def render_with_engine(engine, embedded_text) do
keep_lines = Application.get_env(:slime, :keep_lines)
embedded_text = if keep_lines do
"\n" <> embedded_text
else
embedded_text
end
apply(@engines[engine], :render, [embedded_text, [keep_lines: keep_lines]])
def parse(engine, _) do
{:error, ~s(Unknown embedded engine "#{engine}")}
end

defp indent(line) do
String.length(line) - String.length(String.lstrip(line))
end
defp render_with_engine(engine, text) do
keep_lines = Application.get_env(:slime, :keep_lines)
text = if keep_lines, do: ["\n" | text], else: text

defp strip_line(line, strip_indent) do
String.slice(line, min(strip_indent, indent(line))..-1)
apply(@engines[engine], :render, [text, [keep_lines: keep_lines]])
end
end

Expand All @@ -57,7 +42,7 @@ defmodule Slime.Parser.EmbeddedEngine.Javascript do

@behaviour Slime.Parser.EmbeddedEngine

def render(text, _options), do: {"script", children: [text]}
def render(text, _options), do: {"script", children: text}
end

defmodule Slime.Parser.EmbeddedEngine.Css do
Expand All @@ -68,7 +53,7 @@ defmodule Slime.Parser.EmbeddedEngine.Css do
@behaviour Slime.Parser.EmbeddedEngine

def render(text, _options) do
{"style", attributes: [type: "text/css"], children: [text]}
{"style", attributes: [type: "text/css"], children: text}
end
end

Expand All @@ -83,13 +68,18 @@ defmodule Slime.Parser.EmbeddedEngine.Elixir do

def render(text, options) do
newlines = if options[:keep_lines] do
count = text |> String.split("\n") |> length |> Kernel.-(1)
count = Enum.count(text, &Kernel.==(&1, "\n"))
[String.duplicate("\n", count)]
else
[]
end

%EExNode{content: text, children: newlines}
eex = Enum.map_join(text, fn
({:eex, interpolation}) -> ~S"#{" <> interpolation <> "}"
(text) -> text
end)

%EExNode{content: eex, children: newlines}
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/slime/parser/text_block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Slime.Parser.TextBlock do
defp insert_line_spacing(lines, text_indent) do
concat_lines(lines,
fn({line_indent, line_contents}, content) ->
leading_space = String.duplicate(" ", line_indent - text_indent)
leading_space = String.duplicate(" ", max(0, line_indent - text_indent))
case leading_space do
"" -> ["\n" | line_contents ++ content]
_ -> ["\n" | [leading_space | line_contents ++ content]]
Expand Down
32 changes: 18 additions & 14 deletions lib/slime/parser/transform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ defmodule Slime.Parser.Transform do
alias Slime.Parser.Nodes.InlineHTMLNode
alias Slime.Parser.Nodes.DoctypeNode

alias Slime.TemplateSyntaxError

@default_tag Application.get_env(:slime, :default_tag, "div")
@sort_attrs Application.get_env(:slime, :sort_attrs, true)
@merge_attrs Application.get_env(:slime, :merge_attrs, %{"class" => " "})
Expand Down Expand Up @@ -142,24 +144,26 @@ defmodule Slime.Parser.Transform do
end
end

def transform(:text_block_line, [space, content], _index) do
{indent_size(space), content}
def transform(:embedded_engine, [engine, _, content], index) do
case EmbeddedEngine.parse(engine, content[:lines]) do
{:ok, {tag, content}} ->
%HTMLNode{name: tag,
attributes: (content[:attributes] || []),
children: content[:children]}
{:ok, content} -> content
{:error, message} ->
{{:line, line_number}, {:column, column}} = index
raise TemplateSyntaxError, message: message,
line: "", line_number: line_number, column: column
end
end

def transform(:embedded_engine, [engine, _, content], _index) do
lines = content[:lines]
case EmbeddedEngine.render_with_engine(engine, lines) do
{tag, content} -> %HTMLNode{name: tag,
attributes: (content[:attributes] || []),
children: content[:children]}
content -> content
end
def transform(:embedded_engine_lines, [first_line, rest], _index) do
[first_line | Enum.map(rest, fn ([_, lines]) -> lines end)]
end

def transform(:embedded_engine_lines, input, _index) do
[line, rest] = input
lines = Enum.map(rest, fn ([_, lines]) -> lines end)
[line | lines]
def transform(:indented_text_line, [space, content], _index) do
{indent_size(space), content}
end

def transform(:inline_html, [_, content, children], _index) do
Expand Down
9 changes: 5 additions & 4 deletions src/slime_parser.peg.eex
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ code_comment <- '/' text_block;

verbatim_text <- indent:space? type:[|'] content:text_block;

text_block <- text_block_line (crlf
text_block <- indented_text_line (crlf
indent lines:text_block_nested_lines dedent)?;
text_block_nested_lines <- text_block_line (crlf (
text_block_nested_lines <- indented_text_line (crlf (
indent lines:text_block_nested_lines dedent / lines:text_block_nested_lines
))*;
text_block_line <- space? text_item*;

embedded_engine <- tag_name ':' (crlf indent lines:embedded_engine_lines dedent);
embedded_engine_lines <- text_item* (crlf text_item*)*;
embedded_engine_lines <- indented_text_line (crlf indented_text_line)*;

indented_text_line <- space? text_item*;

tag_name <- [a-zA-Z0-9_-]+;
attribute_name <- [a-zA-Z0-9_@:-]+;
Expand Down
19 changes: 16 additions & 3 deletions test/rendering/embedded_engine_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,16 @@ defmodule RenderEmbeddedEngineTest do
end

test "render embedded elixir" do
slime = """
slime = ~S"""
elixir:
a =
[1, 2, 3]
|> Enum.map(&(&1 * &1))
= Enum.join(a, ",")
b = "test"
c = " and #{b}"
= Enum.join(a, ",") <> c
"""
assert render(slime) == ~s(1,4,9)
assert render(slime) == ~s(1,4,9 and test)
end

test "render embedded eex" do
Expand All @@ -108,6 +110,17 @@ defmodule RenderEmbeddedEngineTest do
assert render(slime) == ~s(Test: test)
end

test "raises an error for unknown engines" do
assert_raise Slime.TemplateSyntaxError,
~r/Unknown embedded engine \"textile\"/, fn ->
render """
p
textile:
*Textile* is _easy_ to read and _easy_ to write
"""
end
end

defmodule TestEngine do
@moduledoc false
@behaviour Slime.Parser.EmbeddedEngine
Expand Down

0 comments on commit 5db4235

Please sign in to comment.