Skip to content

Commit 357d0f9

Browse files
authored
perf: speed up page and post extensions (#64)
Closes #48
1 parent df1658f commit 357d0f9

File tree

9 files changed

+154
-132
lines changed

9 files changed

+154
-132
lines changed

lib/mix/tasks/tableau.build.ex

+4-3
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@ defmodule Mix.Tasks.Tableau.Build do
5252

5353
mods = :code.all_available()
5454
graph = Tableau.Graph.new(mods)
55+
5556
File.mkdir_p!(out)
5657

5758
pages =
58-
for mod <- Graph.vertices(graph), {:ok, :page} == Tableau.Graph.Node.type(mod) do
59-
{mod, Map.new(mod.__tableau_opts__() || [])}
59+
for mod <- Graph.vertices(graph), {:ok, :page} == Tableau.Graph.Nodable.type(mod) do
60+
{mod, Map.new(Tableau.Graph.Nodable.opts(mod) || [])}
6061
end
6162

6263
{page_mods, pages} = Enum.unzip(pages)
@@ -65,7 +66,7 @@ defmodule Mix.Tasks.Tableau.Build do
6566

6667
for mod <- page_mods do
6768
content = Tableau.Document.render(graph, mod, token)
68-
permalink = mod.__tableau_permalink__()
69+
permalink = Tableau.Graph.Nodable.permalink(mod)
6970
dir = Path.join(out, permalink)
7071

7172
File.mkdir_p!(dir)

lib/tableau/document.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defmodule Tableau.Document do
1111
quote do
1212
case unquote(inner_content) do
1313
[{module, page_assigns, assigns} | rest] ->
14-
module.template(Map.merge(assigns, %{page: page_assigns, inner_content: rest}))
14+
Tableau.Graph.Nodable.template(module, Map.merge(assigns, %{page: page_assigns, inner_content: rest}))
1515

1616
[] ->
1717
nil
@@ -32,9 +32,9 @@ defmodule Tableau.Document do
3232
raise "Failed to find layout path for #{inspect(module)}"
3333
end
3434

35-
page_assigns = Map.new(module.__tableau_opts__() || [])
35+
page_assigns = Map.new(Tableau.Graph.Nodable.opts(module) || [])
3636
mods = for mod <- mods, do: {mod, page_assigns, assigns}
3737

38-
root.template(Map.merge(assigns, %{inner_content: mods, page: page_assigns}))
38+
Tableau.Graph.Nodable.template(root, Map.merge(assigns, %{inner_content: mods, page: page_assigns}))
3939
end
4040
end

lib/tableau/extensions/common.ex

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
defmodule Tableau.Extension.Common do
2+
@moduledoc false
3+
4+
def paths(wildcard) do
5+
wildcard |> Path.wildcard() |> Enum.sort()
6+
end
7+
8+
def entries(paths, parser_module, builder, opts) do
9+
Enum.flat_map(paths, fn path ->
10+
parsed_contents = parse_contents!(path, File.read!(path), parser_module)
11+
build_entry(builder, path, parsed_contents, opts)
12+
end)
13+
end
14+
15+
defp build_entry(builder, path, {_attrs, _body} = parsed_contents, opts) do
16+
build_entry(builder, path, [parsed_contents], opts)
17+
end
18+
19+
defp build_entry(builder, path, parsed_contents, opts) when is_list(parsed_contents) do
20+
converter_module = Keyword.get(opts, :html_converter)
21+
22+
Enum.map(parsed_contents, fn {attrs, body} ->
23+
body =
24+
case converter_module do
25+
module -> module.convert(path, body, attrs, opts)
26+
end
27+
28+
builder.build(path, attrs, body)
29+
end)
30+
end
31+
32+
defp parse_contents!(path, contents, parser_module) do
33+
parser_module.parse(path, contents)
34+
end
35+
end

lib/tableau/extensions/page_extension.ex

+2-58
Original file line numberDiff line numberDiff line change
@@ -47,64 +47,8 @@ defmodule Tableau.PageExtension do
4747

4848
use Tableau.Extension, key: :pages, type: :pre_build, priority: 100
4949

50-
{:ok, config} =
51-
Tableau.PageExtension.Config.new(Map.new(Application.compile_env(:tableau, Tableau.PageExtension, %{})))
52-
53-
@config config
54-
5550
def run(token) do
56-
:global.trans(
57-
{:create_pages_module, make_ref()},
58-
fn ->
59-
Module.create(
60-
Tableau.PageExtension.Pages,
61-
quote do
62-
use NimblePublisher,
63-
build: __MODULE__.Page,
64-
from: "#{unquote(@config.dir)}/**/*.md",
65-
as: :pages,
66-
parser: Tableau.PageExtension.Pages.Page,
67-
html_converter: Tableau.PageExtension.Pages.HTMLConverter
68-
69-
def pages(_opts \\ []) do
70-
@pages
71-
end
72-
end,
73-
Macro.Env.location(__ENV__)
74-
)
75-
76-
for {mod, _, _} <- :code.all_available(),
77-
mod = Module.concat([to_string(mod)]),
78-
{:ok, :page} == Tableau.Graph.Node.type(mod),
79-
mod.__tableau_opts__()[:__tableau_page_extension__] do
80-
:code.purge(mod)
81-
:code.delete(mod)
82-
end
83-
84-
pages =
85-
for page <- apply(Tableau.PageExtension.Pages, :pages, []) do
86-
{:module, _module, _binary, _term} =
87-
[:"#{System.unique_integer()}"]
88-
|> Module.concat()
89-
|> Module.create(
90-
quote do
91-
use Tableau.Page, unquote(Macro.escape(Keyword.new(page)))
92-
93-
@external_resource unquote(page.file)
94-
def template(_assigns) do
95-
unquote(page.body)
96-
end
97-
end,
98-
Macro.Env.location(__ENV__)
99-
)
100-
101-
page
102-
end
103-
104-
{:ok, Map.put(token, :pages, pages)}
105-
end,
106-
[Node.self()],
107-
:infinity
108-
)
51+
token = put_in(token.pages, Tableau.PageExtension.Pages.pages())
52+
{:ok, token}
10953
end
11054
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
defmodule Tableau.PageExtension.Pages do
2+
@moduledoc false
3+
alias Tableau.Extension.Common
4+
5+
@config Map.new(Application.compile_env(:tableau, Tableau.PageExtension, %{}))
6+
7+
def __tableau_type__, do: :pages
8+
9+
def pages(opts \\ []) do
10+
opts
11+
|> pages2()
12+
|> Enum.map(fn page ->
13+
%{
14+
type: :page,
15+
parent: page.layout,
16+
permalink: page.permalink,
17+
template: page.body,
18+
opts: page
19+
}
20+
end)
21+
end
22+
23+
def pages2(opts \\ []) do
24+
{:ok, config} =
25+
Tableau.PageExtension.Config.new(@config)
26+
27+
opts = Keyword.put_new(opts, :html_converter, Tableau.PageExtension.Pages.HTMLConverter)
28+
29+
config.dir
30+
|> Path.join("**/*.md")
31+
|> Common.paths()
32+
|> Common.entries(Tableau.PageExtension.Pages.Page, Tableau.PageExtension.Pages.Page, opts)
33+
end
34+
end

lib/tableau/extensions/post_extension.ex

+2-66
Original file line numberDiff line numberDiff line change
@@ -53,72 +53,8 @@ defmodule Tableau.PostExtension do
5353

5454
use Tableau.Extension, key: :posts, type: :pre_build, priority: 100
5555

56-
{:ok, config} =
57-
Tableau.PostExtension.Config.new(Map.new(Application.compile_env(:tableau, Tableau.PostExtension, %{})))
58-
59-
@config config
60-
6156
def run(token) do
62-
:global.trans(
63-
{:create_posts_module, make_ref()},
64-
fn ->
65-
Module.create(
66-
Tableau.PostExtension.Posts,
67-
quote do
68-
use NimblePublisher,
69-
build: __MODULE__.Post,
70-
from: "#{unquote(@config.dir)}/**/*.md",
71-
as: :posts,
72-
parser: Tableau.PostExtension.Posts.Post,
73-
html_converter: Tableau.PostExtension.Posts.HTMLConverter
74-
75-
def posts(_opts \\ []) do
76-
@posts
77-
|> Enum.sort_by(& &1.date, {:desc, DateTime})
78-
|> then(fn posts ->
79-
if unquote(@config.future) do
80-
posts
81-
else
82-
Enum.reject(posts, &DateTime.after?(&1.date, DateTime.utc_now()))
83-
end
84-
end)
85-
end
86-
end,
87-
Macro.Env.location(__ENV__)
88-
)
89-
90-
for {mod, _, _} <- :code.all_available(),
91-
mod = Module.concat([to_string(mod)]),
92-
{:ok, :page} == Tableau.Graph.Node.type(mod),
93-
mod.__tableau_opts__()[:__tableau_post_extension__] do
94-
:code.purge(mod)
95-
:code.delete(mod)
96-
end
97-
98-
posts =
99-
for post <- apply(Tableau.PostExtension.Posts, :posts, []) do
100-
{:module, _module, _binary, _term} =
101-
[:"#{System.unique_integer()}"]
102-
|> Module.concat()
103-
|> Module.create(
104-
quote do
105-
use Tableau.Page, unquote(Macro.escape(Keyword.new(post)))
106-
107-
@external_resource unquote(post.file)
108-
def template(_assigns) do
109-
unquote(post.body)
110-
end
111-
end,
112-
Macro.Env.location(__ENV__)
113-
)
114-
115-
post
116-
end
117-
118-
{:ok, Map.put(token, :posts, posts)}
119-
end,
120-
[Node.self()],
121-
:infinity
122-
)
57+
token = put_in(token.posts, Tableau.PostExtension.Posts.posts())
58+
{:ok, token}
12359
end
12460
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
defmodule Tableau.PostExtension.Posts do
2+
@moduledoc false
3+
alias Tableau.Extension.Common
4+
5+
@config Map.new(Application.compile_env(:tableau, Tableau.PostExtension, %{}))
6+
7+
def __tableau_type__, do: :pages
8+
9+
def pages(opts \\ []) do
10+
opts
11+
|> posts()
12+
|> Enum.map(fn post ->
13+
%{
14+
type: :page,
15+
parent: post.layout,
16+
permalink: post.permalink,
17+
template: post.body,
18+
opts: post
19+
}
20+
end)
21+
end
22+
23+
def posts(opts \\ []) do
24+
{:ok, config} =
25+
Tableau.PostExtension.Config.new(@config)
26+
27+
opts = Keyword.put_new(opts, :html_converter, Tableau.PostExtension.Posts.HTMLConverter)
28+
29+
config.dir
30+
|> Path.join("**/*.md")
31+
|> Common.paths()
32+
|> Common.entries(Tableau.PostExtension.Posts.Post, Tableau.PostExtension.Posts.Post, opts)
33+
|> Enum.sort_by(& &1.date, {:desc, DateTime})
34+
|> then(fn posts ->
35+
if config.future do
36+
posts
37+
else
38+
Enum.reject(posts, &DateTime.after?(&1.date, DateTime.utc_now()))
39+
end
40+
end)
41+
end
42+
end

lib/tableau/graph.ex

+11-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,17 @@ defmodule Tableau.Graph do
1010
Node.type(mod) != :error,
1111
reduce: graph do
1212
graph ->
13-
{:ok, parent} = Node.parent(mod)
14-
Graph.add_edge(graph, mod, parent)
13+
case Node.type(mod) do
14+
{:ok, :pages} ->
15+
for page <- mod.pages(), reduce: graph do
16+
graph ->
17+
Graph.add_edge(graph, page, page.parent)
18+
end
19+
20+
_ ->
21+
{:ok, parent} = Node.parent(mod)
22+
Graph.add_edge(graph, mod, parent)
23+
end
1524
end
1625
end
1726
end

lib/tableau/graph/nodable.ex

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defprotocol Tableau.Graph.Nodable do
2+
@moduledoc false
3+
def template(nodable, assigns)
4+
def type(nodable)
5+
def permalink(nodable)
6+
def opts(nodable)
7+
end
8+
9+
defimpl Tableau.Graph.Nodable, for: Map do
10+
def template(nodable, _assigns), do: nodable.template
11+
def type(nodable), do: {:ok, nodable.type}
12+
def opts(nodable), do: nodable.opts
13+
def permalink(nodable), do: nodable.permalink
14+
end
15+
16+
defimpl Tableau.Graph.Nodable, for: Atom do
17+
def template(nodable, assigns), do: nodable.template(assigns)
18+
def type(nodable), do: Tableau.Graph.Node.type(nodable)
19+
def opts(nodable), do: nodable.__tableau_opts__()
20+
def permalink(nodable), do: nodable.__tableau_permalink__()
21+
end

0 commit comments

Comments
 (0)