Last active
September 11, 2024 13:28
-
-
Save aportnov/09126ffbf95c41bdea676eaaa8ff3c5e to your computer and use it in GitHub Desktop.
Elixir: Compiled EEx templates in Plug application
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
defmodule MyModule.Router do | |
@moduledoc """ | |
Http Entry Point | |
""" | |
use Plug.Router | |
use Plug.ErrorHandler | |
plug Plug.Logger | |
plug :match | |
plug :dispatch | |
use MyModule.View | |
get "/hello/:name" do | |
send_resp(conn, 200, render("sample/template", name: name)) | |
end | |
match _ do | |
send_resp(conn, 404, "Not found") | |
end | |
defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do | |
send_resp(conn, conn.status, "Something went wrong") | |
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
@moduledoc """ | |
Compilation Notes: | |
EEx expects @var notation to be defined inside :assigns keyword list, | |
so by doing var!(assigns) to a function params, it turns that into expected :assigns variable inside EEx generated AST | |
def unquote(name)(var!(assigns)) do | |
doing: | |
_ = var!(assigns) | |
basically clears warnings, in cases where no @var_name variables are used in the template | |
""" | |
defmodule MyModule.Template do | |
@templates "web/templates" | |
defmacro __using__(_options) do | |
quote do | |
require EEx | |
import unquote(__MODULE__) | |
@before_compile unquote(__MODULE__) | |
end | |
end | |
defmacro __before_compile__(_env) do | |
templates = find_templates | |
ast = templates |> Enum.map(fn(path) -> compile(path) end) | |
quote do | |
unquote(ast) | |
end | |
end | |
def view_name(@templates <> path), do: view_name(path) | |
def view_name("/" <> path), do: view_name(path) | |
def view_name(path) do | |
path | |
|> String.downcase | |
|> String.replace("/", "_") | |
|> String.replace(".eex", "") | |
|> String.to_atom | |
end | |
defp find_templates(), do: find_templates(@templates) | |
defp find_templates(root) do | |
Path.wildcard("#{root}/**/*.eex") | |
end | |
defp compile(path) do | |
name = view_name(path) | |
block = EEx.compile_file(path) | |
quote do | |
@file unquote(path) | |
@external_resource unquote(path) | |
def unquote(name)(var!(assigns)) do | |
_ = var!(assigns) | |
unquote(block) | |
end | |
end | |
end | |
end | |
defmodule MyModule.View do | |
use MyModule.Template | |
def render(path, assigns) do | |
name = view_name(path) | |
apply(__MODULE__, name, [assigns]) | |
end | |
defmacro __using__(_options) do | |
quote do | |
import unquote(__MODULE__) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment