【发布时间】:2018-06-19 15:30:03
【问题描述】:
在我的 Phoenix 应用程序中,我正在使用我编写的几个宏来为日志数据添加一些额外的自定义格式 - 但是,当使用 @ 987654321@ 行为,我将负责 HTTP 访问日志记录的宏直接添加到我的 router.ex 管道中的 plug。
首先,这是我尝试使用的宏的简化版本:
defmodule MacroTest do
defmacro __using__(_) do
quote do
require Logger
defmacro execute(data) do
quote do: Logger.log(:info, unquote(data))
end
end
end
end
在我的router.ex 中,我想将execute/1 宏作为fun 选项提供给Plug.Accesslog:
defmodule MyApp.Router do
use MyApp.Web., :router
use MacroTest
pipeline :api do
plug Plug.AccessLog,
format: :combined,
formatters: [ Plug.AccessLog.DefaultFormatter ],
fun: &execute/1
end
end
当我像这样编译应用程序时,出现以下错误:
** (CompileError) web/router.ex:21: undefined function execute/1
(stdlib) lists.erl:1338: :lists.foreach/2
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:198: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6
但是,我发现如果我定义一个辅助函数并在那里调用宏,这将起作用:
defmodule MyApp.Router do
use MyApp.Web., :router
use MacroTest
def my_fun(data), do: execute(data)
pipeline :api do
plug Plug.AccessLog,
format: :combined,
formatters: [ Plug.AccessLog.DefaultFormatter ],
fun: &__MODULE__.my_fun/1
end
end
这是我可以在这里完成我需要的唯一方法,还是我可以进行更改以允许我将execute/1 直接传递给plug 而无需包装它?如果这是唯一的解决方案,那没什么大不了的,但如果可能的话,我宁愿避免使用辅助功能。虽然我无法真正解释为什么会发生这种情况,但我很欣赏将宏传递给另一个宏的潜在编译复杂性。
谢谢!
【问题讨论】:
-
我认为你可以做到
fun: fn data -> execute(data) end。 -
这行得通吗:
fun: &MyApp.Router.execute/1? -
@Dogbert 应用程序将在这种情况下编译,但一旦调用
fun,它就会抛出错误function MyApp.Router is undefined or private. Be sure to require MyApp.Router if you intend to invoke this macro.,指的是 Plug.AccessLog。