将宏作为Plug选项传递无法编译

时间:2018-06-18 17:45:37

标签: elixir phoenix-framework

在我的凤凰应用程序中,我正在使用我编写的几个宏来为日志数据添加一些额外的自定义格式 - 但是,我在编译应用程序时遇到了一些问题正确地,当使用use行为时,我将负责HTTP访问日志记录的宏直接添加到plug管道中的router.ex

首先,这是我尝试使用的宏的简化版本:

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宏提供给 Plug.Accesslog 作为其fun选项:

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而不必将其包裹起来?如果这是唯一的解决方案,那不是什么大问题,但如果可能的话,我更愿意避免使用辅助功能。虽然我无法清楚地说明为什么会发生这种情况,但我很欣赏将宏传递到另一个宏的潜在编译复杂性。

谢谢!

1 个答案:

答案 0 :(得分: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