在 __before_compile__ 中调用一个插件什么都不做

calling a plug inside __before_compile__ does nothing

我正在努力了解 elixir 模块的生命周期,我注意到的一件事是在 before_compile 块中调用插件宏不会'调用控制器时不执行插件。据我了解,该块在编译之前立即执行,因此插件宏也将在模块中编译。

鉴于此:

  defmacro __before_compile__(env) do
    quote do
     plug :say_hi
    end
  end

使用 phoinex 控制器中的方法:

def say_hi(conn, _) do
   IO.inspect("hello")
end

这不会打印 hello,因为在 using 块中有相同的引用块。我错过了什么?

克里斯

Per the relevant documentation on Module and compilation callbacks__before_compile__ 需要在目标模块中用 @before_compile MyApp.MyModule 调用(其中 MyApp.MyModule 是您的模块的名称)。

如果您试图让 __before_compile__ 发生在 use 属于您的模块的另一个模块中,您可以定义 __using__ __before_compile__,像这样:

defmodule MyApp.MyModule do
  defmacro __using__(_) do
    quote do
      @before_compile MyApp.MyModule
    end
  end

  defmacro __before_compile__(env) do
    quote do
      plug :say_hi
    end
  end
end

Sugar web development framework 在其控制器代码中有一个相对简单的真实世界演示,说明它是如何工作的;当您在控制器模块(例如,MyApp.Controllers.Main)中调用 use Sugar.Controller 时,会调用 Sugar.Controller.__using__/1 宏,这反过来会导致 @before_compile Sugar.Controller 在 [= 的上下文中被调用20=],因此在幕后做了一堆与插件相关的魔术(主要是实现 Plug 行为,但这有点离题了)。

Phoenix 也用它的控制器做了类似的事情,尽管有点间接; Phoenix.Controller.Pipeline 是这种模式的另一个很好的示范(尽管更复杂)。


顺便说一句,我得到的结果非常复杂,让 IO.inspectIO.puts(或 IO.任何东西,就此而言)实际出现在这种情况下的情况。您可能想尝试 require Logger; Logger.info "hello"