Plug, nested macros and lots of confusion

Hi everyone!

I am stuck on getting a macro to generate a plug route. I’ve tried many different combinations, read Phoenix and Plug’s code to figure out how to do it, but there is something I am missing.

If I have:

defmodule MyApp.Helper do
  defmacro __using__([]) do
    quote do
      use Plug.Router

      plug(:match)
      plug(:dispatch)

      get("/hello", do: Plug.Conn.send_resp(conn, 200, "hello"))
    end
  end
end

And:

defmodule MyApp.Router do
  use MyApp.Helper

  match(_, do: send_404(conn))
end

conn in __using__ is undefined, and compilation fails, which lets me think get was somehow not recognized?

Hello,

It’s all about metaprogramming.

Imaging you got

defmodule MyMacro do
  defmacro __using__(name) do
    quote do
      def unquote(name)(), do: "Hello #{unquote(name)}"
    end
  end
end

This code mean : everytime I’ll use MyMacro it will add the code in the quote.

So if you use it like :

defmodule Tuto do
  use MyMacro, :foo
  use MyMacro, :bar
end

It’s like you have been write

defmodule Tuto do
  def foo(), do: "Hello foo"
  def bar(), do: "Hello bar"
end

Now we’ll try to understand the

get("/hello", do: Plug.Conn.send_resp(conn, 200, "hello"))

In fact get is a macro. We could easy imagine a macro like this one

defmodule MyMacro
  defmacro get(path, do: block) do
    quote do
      def call(conn = %{path: unquote(path)}) do
        unquote(block)
      end
    end
  end
end

So when you use the macro get like this

def MyModule do
  get("/hello", do: Plug.Conn.send_resp(conn, 200, "hello"))
end

The generate code will looks like :

def MyModule do
  def call(conn = %{path: "/hello"}) do
    Plug.Conn.send_resp(conn, 200, "hello")
  end
end

Everything is just example, the real macro get isn’t like that

5 Likes

Thanks!

In the end, I stumbled on this discussion and I made 2+2 with your explanation