defmodule Orbit.View do
@moduledoc """
Render Gemtext content.
A "view" is any 1-arity function that accepts a map of assigns and returns a string of rendered Gemtext.
The `~G` sigil is used to precompile EEx templates as strings when an `assigns` variable or argument is
in scope.
Views can be defined as functions, or embedded from `.gmi.eex` files into view module via `embed_templates/1`.
## Usage
`use Orbit.View` injects the following in the module:
import Orbit.Gemtext, only: [sigil_G: 2]
import Orbit.View
require EEx
## Example
defmodule MyApp.MyView do
use Orbit.View
embed_templates "my_view/*"
def list(assigns) do
~G\"\"\"
<%= for item <- @items do %>
* <%= item %>
<% end %>
\"\"\"
end
end
"""
defmacro __using__(_) do
quote do
import Orbit.Gemtext, only: [sigil_G: 2]
import Orbit.View
require EEx
end
end
@doc false
defmacro render(view) do
quote do
unquote(view).(%{})
end
end
@doc false
defmacro render(view, do: block) do
quote do
unquote(view).(%{inner_content: unquote(block)})
end
end
@doc false
defmacro render(view, assigns) do
quote do
unquote(view).(Enum.into(unquote(assigns), %{}))
end
end
@doc """
Renders a view within another view.
The `assigns` and `block` arguments are optional.
If a block is passed, its contents are renderd and set to `@inner_content` assign of the view.
## Examples
<%= render &my_view/1 %>
<%= render &my_view/1, title: @title %>
<%= render &my_component/1 do %>
inner content
<% end %>
"""
defmacro render(view, assigns, _block = [do: block]) do
quote do
unquote(view).(Map.put(Enum.into(unquote(assigns), %{}), :inner_content, unquote(block)))
end
end
@doc """
Define view functions from external files.
Every file found in the wildcard `path` is compiled as EEx and injected as a view function into the view
module, using the file basename as the function name. For example, `index.gmi.eex` will be defined as
`def index(assigns)`.
"""
defmacro embed_templates(path) when is_binary(path) do
absolute_path = Path.join(Path.dirname(__CALLER__.file), path)
absolute_path
|> Path.wildcard()
|> Enum.map(fn template_path ->
template_name =
template_path
|> Path.basename()
|> String.split(".")
|> hd()
|> String.to_atom()
quote do
EEx.function_from_file(:def, unquote(template_name), unquote(template_path), [:assigns])
end
end)
end
end