Skip to content

Commit

Permalink
Switching to a manual parsing setup for spt
Browse files Browse the repository at this point in the history
  • Loading branch information
clone1018 committed May 9, 2017
1 parent bc0071f commit c8cb63b
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 123 deletions.
51 changes: 37 additions & 14 deletions lib/simplates/pagination.ex
Expand Up @@ -10,31 +10,46 @@ defmodule Simplates.Pagination do

import Simplates.Simplate, only: [config: 1]

@script_regex ~r/^\<script\>(?P<raw>.+?)^\<\/script\>/sim
@template_regex ~r/^\<template\>(?P<raw>.+?)^\<\/template\>/sim
def parse_pages(raw) do
blocks = Simplates.Parser.parse(raw) |> parse_blocks

def parse_pages(raw) do
raw = "<root>" <> raw <> "</root>"
script = parse_scripts(Floki.find(raw, "root > script"))
templates = parse_templates(Floki.find(raw, "root > template"))
script = parse_scripts(blocks[:script_blocks])
templates = parse_templates(blocks[:template_blocks])

%{code: script, templates: templates}
end

defp parse_blocks(blocks) do
Enum.reduce(blocks, %{}, fn(b, acc) ->
{tag, _, _} = Floki.parse(b)

type = cond do
tag == "script" -> :script_blocks
tag == "template" -> :template_blocks
end

b = String.trim(b)

Map.put(acc, type, Map.get(acc, type, []) ++ [b])
end)
end

defp parse_scripts(raw_script) when length(raw_script) == 1 do
# For now you can only have one script
{_, _, children_nodes} = raw_script |> hd()
page_content = Floki.raw_html(children_nodes)
page_content = hd(raw_script)
|> String.split("\n")
|> Enum.drop(1)
|> Enum.drop(-1)
|> Enum.join("\n")

%Simplates.Page{
raw: page_content,
# Is this the right place to compile?
raw: page_content,
compiled: Simplates.Renderers.CodeRenderer.compile(page_content),
renderer: Simplates.Renderers.CodeRenderer,
content_type: nil
}
end
defp parse_scripts([]) do
defp parse_scripts(nil) do
%Simplates.Page{
raw: "",
compiled: Simplates.Renderers.CodeRenderer.compile(""),
Expand All @@ -49,12 +64,20 @@ defmodule Simplates.Pagination do
Map.put(acc, template.content_type, template)
end)
end
defp parse_templates([]) do
defp parse_templates(nil) do
{}
end

defp parse_template({_, _, children_nodes} = html_tree) do
page_content = Floki.raw_html(children_nodes) |> String.trim_leading() |> String.trim_trailing()
defp parse_template(raw_template) do
header = raw_template |> String.split("\n") |> hd()

html_tree = Floki.parse(header <> "</template>")

page_content = raw_template
|> String.split("\n")
|> Enum.drop(1)
|> Enum.drop(-1)
|> Enum.join("\n")

{renderer_found, renderer} = attr_or_default(:renderer, Floki.attribute([html_tree], "via"), config(:default_renderer))
# content type will be fixed in simplates, due to bound simplates
Expand Down
42 changes: 42 additions & 0 deletions lib/simplates/parser.ex
@@ -0,0 +1,42 @@
defmodule Simplates.Parser do
@moduledoc """
Handles blocking out a file into blocks of templates/scripts
Then it's up to specline to the paginator/specline
"""

@block_regex ~r/(^\<template(.*)\>|^\<script\>)|(^\<\/template\>|^\<\/script\>)/im

def parse(input) do
input |> String.trim() |> String.split("\n") |> find_block()
end

def find_block(lines) do
find_block(lines, 0, [], [])
end

def find_block(remaining, found, in_block, blocks) when found == 2 do
in_block = Enum.join(in_block, "\n")
find_block(remaining, 0, [], blocks ++ [in_block])
end

def find_block([], _found, _in_block, blocks) do
blocks
end

def find_block(remaining, found, in_block, blocks) do
line = hd(remaining)
regex_matches = Regex.match?(@block_regex, line)

in_block = in_block ++ [line]

found =
case regex_matches do
true -> found + 1
false -> found
end

find_block(Enum.drop(remaining, 1), found, in_block, blocks)
end

end
84 changes: 0 additions & 84 deletions test.exs

This file was deleted.

4 changes: 3 additions & 1 deletion test/simplates/fake-www/index.spt
@@ -1 +1,3 @@
<template>Greetings, program!</template>
<template>
Greetings, program!
</template>
33 changes: 22 additions & 11 deletions test/simplates/pagination_test.exs
Expand Up @@ -4,34 +4,41 @@ defmodule Simplates.PaginationTest do
alias Simplates.Pagination, as: Pagination

test "single page adds one code pages" do
pages = Pagination.parse_pages("<template>Hello, world! I have no code!</template>")
pages = Pagination.parse_pages("<template>\nHello, world! I have no code!\n</template>")

assert pages.code.raw == ""
assert pages.templates[nil].raw == "Hello, world! I have no code!"
end

test "two page adds nothing" do
pages = Pagination.parse_pages("
<script>some_code = 3</script>
<template>Hello, world! I have SOME code!</template>")
<script>
some_code = 3
</script>
<template>
Hello, world! I have SOME code!
</template>")

assert String.trim(pages.code.raw) == "some_code = 3"
assert String.trim(pages.templates[nil].raw) == "Hello, world! I have SOME code!"
end

test "two page adds nothing with specline" do
pages = Pagination.parse_pages("
<script>some_code = 2</script>
<template type=\"media/type\" via=\"EEx\">
Hello, world! I have SOME code and a specline!
</script>")
<script>
some_code = 2
</script>
<template type=\"media/type\" via=\"EEx\">
Hello, world! I have SOME code and a specline!
</template>")

assert String.trim(pages.code.raw) == "some_code = 2"
assert String.trim(pages.templates["media/type"].raw) == "Hello, world! I have SOME code and a specline!"
end

test "parser ignores any non-root level tags" do
pages = Pagination.parse_pages(~s(<template>
pages = Pagination.parse_pages(~s(
<template>
This test ensures anything inside this template tag is ignored entirely by the parser.
<script>
Expand All @@ -43,14 +50,18 @@ defmodule Simplates.PaginationTest do
</template>
</template>))

IO.inspect(pages)

assert map_size(pages.templates) == 1
end

test "parser doesn't mess with htmlentities" do
pages = Pagination.parse_pages(~s(<template>\n&#x3C;h2&#x3E;this is a test&#x3C;/h2&#x3E;\n</template>))

assert pages.templates[nil].raw == "&#x3C;h2&#x3E;this is a test&#x3C;/h2&#x3E;"
end


test "parser handles two template tags" do
pages = Pagination.parse_pages(~s(<template type="text/html"></template><template type="text/plain"></template>))
pages = Pagination.parse_pages(~s(<template type="text/html">\n</template>\n<template type="text/plain">\n</template>))

assert map_size(pages.templates) == 2
assert pages.templates["text/html"]
Expand Down
28 changes: 15 additions & 13 deletions test/simplates/simplate_test.exs
Expand Up @@ -8,23 +8,25 @@ defmodule Simplates.SimplateTest do
end

test "create from file works" do
assert Simplate.create_from_file("test/simplates/fake-www/index.spt") == simple_simplate("<template>Greetings, program!</template>", "test/simplates/fake-www/index.spt")
assert Simplate.create_from_file("test/simplates/fake-www/index.spt") == simple_simplate("<template>\nGreetings, program!\n</template>", "test/simplates/fake-www/index.spt")
end

test "create from string works" do
assert Simplate.create_from_string("<template>Greetings, program!</template>") == simple_simplate("<template>Greetings, program!</template>", nil)
assert Simplate.create_from_string("<template>\nGreetings, program!\n</template>") == simple_simplate("<template>\nGreetings, program!\n</template>", nil)
end

@basic_simplate """
<script></script>
<script>
some_code = 5
</script>
<template type="text/plain">
Greetings, program!
</template>
<template type="text/plain">
Greetings, program!
</template>
<template type="text/html">
<h1>Greetings, program!</h1>
</template>
<template type="text/html">
<h1>Greetings, program!</h1>
</template>
"""

test "render is happy not to negotiate" do
Expand All @@ -38,7 +40,7 @@ defmodule Simplates.SimplateTest do
end

test "render is happy not to negotiate with defaults" do
res = Simplate.render(simple_simplate("<script></script> <template>Greetings, program!</template>", "index.spt"))
res = Simplate.render(simple_simplate("<script>\n</script>\n<template>\nGreetings, program!\n</template>", "index.spt"))
assert res.output == "Greetings, program!"
end

Expand All @@ -58,19 +60,19 @@ defmodule Simplates.SimplateTest do
end

test "create simplate sets default_content_type when bound simplate" do
simp = simple_simplate("<script></script> <template><h1>content</h1></template>", "index.html.spt")
simp = simple_simplate("<script>\n</script>\n<template>\n<h1>content</h1>\n</template>", "index.html.spt")
assert simp.default_content_type == "text/html"
end

test "create simplate sets content_type on template page for bound simplate" do
simp = simple_simplate("<script></script> <template>example</template>", "index.json.spt")
simp = simple_simplate("<script>\n</script>\n<template>\nexample\n</template>", "index.json.spt")

assert simp.templates["application/json"]
assert simp.templates["application/json"].content_type == "application/json"
end

test "render bound simplate properly uses simplate.default_content_type" do
res = Simplate.render(simple_simplate("<script></script> <template>example</template>", "index.json.spt"), "application/json")
res = Simplate.render(simple_simplate("<script>\n</script>\n<template>\nexample\n</template>", "index.json.spt"), "application/json")
assert res.content_type == "application/json"
end

Expand Down

0 comments on commit c8cb63b

Please sign in to comment.