【问题标题】:Passing a macro as a Plug option fails to compile将宏作为 Plug 选项传递无法编译
【发布时间】: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。

标签: elixir phoenix-framework


【解决方案1】:

您根本不需要宏或包装器,只需注入一个函数:

defmodule MacroTest do
  defmacro __using__(_) do
    quote do
      require Logger

      def execute(data) do
        Logger.log(:info, data)
      end
    end
  end
end

和:

defmodule MyApp.Router do
  use MyApp.Web, :router
  use MacroTest

  pipeline :api do
    plug Plug.AccessLog,
      format: :combined,
      formatters: [ Plug.AccessLog.DefaultFormatter ],
      fun: &__MODULE__.execute/1
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2013-12-01
    • 2021-10-10
    • 2015-09-26
    相关资源
    最近更新 更多