Skip to content

Commit

Permalink
feat!: configure runtime attributes function (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhanberg committed Jun 13, 2023
1 parent 8a9e064 commit dc57221
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 24 deletions.
7 changes: 5 additions & 2 deletions guides/getting-started.md
Expand Up @@ -42,13 +42,16 @@ Temple works out of the box without any configuration, but here are a couple of

### Engine

By default, Temple uses the built in `EEx.SmartEngine`. If you want to use a different engine, this is as easy as setting the `:engine` configuration option.
By default, Temple uses the built in `Phoenix.HTML.Engine`. If you want to use a different engine, this is as easy as setting the `:engine` configuration option.

You can also configure the function that is used for runtime attributes. By default, Temple uses `Phoenix.HTML.attributes_escape/1`.

```elixir
# config/config.exs

config :temple,
engine: Phoenix.HTML.Engine
engine: EEx.SmartEngine,
attributes: {Temple, :attributes}
```

### Aliases
Expand Down
38 changes: 31 additions & 7 deletions lib/temple.ex
Expand Up @@ -96,16 +96,40 @@ defmodule Temple do
require Temple.Renderer

Temple.Renderer.compile(unquote(block))
|> then(fn
{:safe, template} ->
template

template ->
template
end)
end
end

@doc false
defdelegate engine, to: Temple.Renderer

@doc """
Compiles runtime attributes.
To use this function, you set it in application config.
By default, Temple uses `{Phoenix.HTML, :attributes_escape}`. This is useful if you want to use `EEx.SmartEngine`.
```elixir
config :temple,
engine: EEx.SmartEngine,
attributes: {Temple, :attributes}
```
> #### Note {: .info}
>
> This function does not do any HTML escaping
> #### Note {: .info}
>
> This function is used by the compiler and shouldn't need to be used directly.
"""
def attributes(attributes) do
for {key, value} <- attributes, into: "" do
case value do
true -> ~s| #{key}|
false -> ""
value -> ~s| #{key}="#{value}"|
end
end
end
end
18 changes: 15 additions & 3 deletions lib/temple/ast/utils.ex
@@ -1,6 +1,12 @@
defmodule Temple.Ast.Utils do
@moduledoc false

@attributes Application.compile_env(
:temple,
:attributes,
{Phoenix.HTML, :attributes_escape}
)

def snake_to_kebab(stringable),
do: stringable |> to_string() |> String.replace_trailing("_", "") |> String.replace("_", "-")

Expand Down Expand Up @@ -34,7 +40,7 @@ defmodule Temple.Ast.Utils do
[
{:expr,
quote do
Phoenix.HTML.attributes_escape(unquote(List.first(attrs)))
unquote(__MODULE__).__attributes__(unquote(List.first(attrs)))
end}
]
end
Expand All @@ -57,7 +63,7 @@ defmodule Temple.Ast.Utils do
def build_attr("rest!", {_, _, _} = value) do
expr =
quote do
Phoenix.HTML.attributes_escape(unquote(value))
unquote(__MODULE__).__attributes__(unquote(value))
end

[{:expr, expr}]
Expand All @@ -66,7 +72,7 @@ defmodule Temple.Ast.Utils do
def build_attr(name, {_, _, _} = value) do
expr =
quote do
Phoenix.HTML.attributes_escape([{unquote(name), unquote(value)}])
unquote(__MODULE__).__attributes__([{unquote(name), unquote(value)}])
end

[{:expr, expr}]
Expand Down Expand Up @@ -154,4 +160,10 @@ defmodule Temple.Ast.Utils do

ast
end

def __attributes__(attributes) do
{mod, func} = @attributes

apply(mod, func, [attributes])
end
end
2 changes: 1 addition & 1 deletion lib/temple/component.ex
Expand Up @@ -55,7 +55,7 @@ defmodule Temple.Component do
import Temple
@doc false
def component(func, assigns, _) do
{:safe, apply(func, [assigns])}
apply(func, [assigns])
end

defmacro inner_block(_name, do: do_block) do
Expand Down
25 changes: 17 additions & 8 deletions test/support/helpers.ex
@@ -1,13 +1,22 @@
defmodule Temple.Support.Helpers do
import ExUnit.Assertions

defmacro assert_html(expected, actual) do
quote do
assert unquote(expected) == Phoenix.HTML.safe_to_string(unquote(actual)), """
--- Expected ---
#{unquote(expected)}----------------
--- Actual ---
#{Phoenix.HTML.safe_to_string(unquote(actual))}--------------
"""
quote location: :keep do
unquote(__MODULE__).__assert_html__(unquote_splicing([expected, actual]))
end
end

def __assert_html__(expected, actual) do
actual = actual |> Phoenix.HTML.Engine.encode_to_iodata!() |> IO.iodata_to_binary()

assert expected == actual,
"""
--- Expected ---
#{expected}----------------
--- Actual ---
#{actual}--------------
"""
end
end
4 changes: 2 additions & 2 deletions test/temple/ast/utils_test.exs
Expand Up @@ -25,7 +25,7 @@ defmodule Temple.Ast.UtilsTest do

assert Macro.to_string(
quote do
Phoenix.HTML.attributes_escape([{"class", unquote(class_ast)}])
Temple.Ast.Utils.__attributes__([{"class", unquote(class_ast)}])
end
) == Macro.to_string(actual)
end
Expand Down Expand Up @@ -74,7 +74,7 @@ defmodule Temple.Ast.UtilsTest do

assert Macro.to_string(
quote do
Phoenix.HTML.attributes_escape(unquote(rest_ast))
Temple.Ast.Utils.__attributes__(unquote(rest_ast))
end
) == Macro.to_string(rest_actual)
end
Expand Down
8 changes: 7 additions & 1 deletion test/temple_test.exs
Expand Up @@ -14,7 +14,7 @@ defmodule TempleTest do
end
end
end
|> :erlang.iolist_to_binary()
|> Phoenix.HTML.safe_to_string()

# heex
expected = """
Expand All @@ -30,4 +30,10 @@ defmodule TempleTest do
assert expected == result
end
end

describe "attributes/1" do
test "compiles runtime attributes" do
assert ~s| disabled class="foo"| == attributes(disabled: true, checked: false, class: "foo")
end
end
end

0 comments on commit dc57221

Please sign in to comment.