Owen
Owen

Reputation: 7098

calling a plug inside __before_compile__ does nothing

I am trying to get my head around the elixir module lifecycle and one thing that I have noticed is that calling a plug macro in the before_compile block doesn't execute the plug when the calling the controller. It was my understanding that this block executes right before compilation so the plug macro would also be compiled in the module.

For given this:

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

With a method in a phoinex controller:

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

This will not print hello, where as having the same quoted block in a using block will. What am I missing?

Chris

Upvotes: 1

Views: 240

Answers (1)

YellowApple
YellowApple

Reputation: 961

Per the relevant documentation on Module and compilation callbacks, __before_compile__ needs to invoked in the target module with @before_compile MyApp.MyModule (where MyApp.MyModule is the name of your module).

If you're trying to make __before_compile__ happen in another module that uses your module, you can define __using__ and __before_compile__, like so:

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

The Sugar web development framework has a relatively simple real-world demonstration of how this works in its controller code; when you call use Sugar.Controller in a controller module (say, MyApp.Controllers.Main), the Sugar.Controller.__using__/1 macro is invoked, which in turn causes @before_compile Sugar.Controller to be invoked in the context of MyApp.Controllers.Main, thus doing a bunch of behind-the-scenes Plug-related magic (mostly implementing the Plug behavior, but that's a bit beside the point here).

Phoenix also does similar similar things with its controllers, though with a bit of indirection; Phoenix.Controller.Pipeline is another good demonstration of this pattern (albeit more complex).


On an unrelated note, I've had very mixed results with getting IO.inspect or IO.puts (or IO.anything, for that matter) to actually show up in this sort of situation. You might want to try require Logger; Logger.info "hello" instead.

Upvotes: 1

Related Questions