Hello,
Probably related with macros (since I am learning them bit a bit), but let’s see if I can get some insight in what is going on.
I installed the decorator
library (Elixir function decorators — decorator v1.4.0). Then, using a hello-world Phoenix project, I have a generic print decorator, but I would like to have a specific one for %Plug.Conn{}
, and it does not work.
PageController:
@decorate print_conn()
def index_specific(%Plug.Conn{} = conn, _params) do
IO.inspect(conn)
render(conn, "index.html")
end
@decorate print()
def index_generic(conn, _params) do
IO.inspect(conn)
render(conn, "index.html")
end
The decorators from HwDecorator:
defmodule HwDecorator do
use Decorator.Define, [print: 0, print_conn: 0]
# at compile-time it does not know if is Plug.Conn
# def print(body, %{args: [%Plug.Conn{} = conn, _params]} = context) do
# so I assume we can work only with arity/2
# alternative, to mark with print_conn
def print(body, %{args: [conn, _params]} = context) do
quote do
IO.puts("Func Conn begin: #{Atom.to_string(unquote(context.name))}")
unquote(body)
IO.puts("Func Conn end: #{Atom.to_string(unquote(context.name))}")
end
end
def print(body, context) do
x = 3
quote do
# IO.inspect(unquote(context), label: "context") <---- WHY:2
IO.puts("Func begin: #{unquote(x)} #{Atom.to_string(unquote(context.name))}")
unquote(body)
IO.puts("Func end: #{Atom.to_string(unquote(context.name))}")
end
end
# This does not work:
# def print_conn(body, %{args: [%Plug.Conn{} = conn, _params]} = context) do. # WHY:1
def print_conn(body, %{args: [conn, _params]} = context) do
quote do
IO.puts("Func Conn type begin: #{Atom.to_string(unquote(context.name))}")
unquote(body)
IO.puts("Func Conn type end: #{Atom.to_string(unquote(context.name))}")
end
end
end
The function receives:
[{:=, [line: 8], [
{:%, [line: 8], [
{:__aliases__, [line: 8], [:Plug, :Conn]}, {:%{}, [line: 8], []}
]},
{:conn, [line: 8], nil}
]},
{:_params, [line: 8], nil}
]
So, I assume it is not matching since %Plug.Conn{} = conn
is not the same as all those tuples from above.
I have 2 questions.
-
WHY:1: What can be done to achieve using types in the decorator functions? Like, for instance, matching Plug.Conn with the first param? I annotate the type in
index_specific
in case it helps, since I imagineindex_generic
does not know it is Plug.Conn at compile time (where I suspect the macros/decorators are being executed).
== Compilation error in file lib/hw_phoenix_web/controllers/page_controller.ex ==
** (FunctionClauseError) no function clause matching in HwDecorator.print_conn/2
The following arguments were given to HwDecorator.print_conn/2:
# 1
{:__block__, [], [{{:., [line: 9], [{:__aliases__, [line: 9], [:IO]}, :inspect]}, [line: 9], [{:conn, [line: 9], nil}]}, {:render, [line: 10], [{:conn, [line: 10], nil}, "index.html"]}]}
# 2
%Decorator.Decorate.Context{args: [{:=, [line: 8], [{:%, [line: 8], [{:__aliases__, [line: 8], [:Plug, :Conn]}, {:%{}, [line: 8], []}]}, {:conn, [line: 8], nil}]}, {:_params, [line: 8], nil}], arity: 2, module: HwPhoenixWeb.PageController, name: :index}
lib/hw_phoenix/decorator.ex:28: HwDecorator.print_conn/2
(decorator 1.4.0) lib/decorator/decorate.ex:164: Decorator.Decorate.apply_decorator/3
(elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
(decorator 1.4.0) lib/decorator/decorate.ex:128: Decorator.Decorate.decorate/4
(elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
(decorator 1.4.0) expanding macro: Decorator.Decorate.before_compile/1
lib/hw_phoenix_web/controllers/page_controller.ex:1: HwPhoenixWeb.PageController (module)
-
WHY:2: Why the
context
variable cannot be used directly there? It is just an struct. I don’t understand whycontext.args
orcontext.name
work, but notcontext
.
== Compilation error in file lib/hw_phoenix_web/controllers/page_controller.ex ==
** (CompileError) lib/hw_phoenix_web/controllers/page_controller.ex: invalid quoted expression: %Decorator.Decorate.Context{args: [{:conn, [line: 14], nil}, {:_params, [line: 14], nil}], arity: 2, module: HwPhoenixWeb.PageController, name: :index_two}
Please make sure your quoted expressions are made of valid AST nodes. If you would like to introduce a value into the AST, such as a four-element tuple or a map, make sure to call Macro.escape/1 before