Macro not expanding in `case` clause

Hello,

In my Phoenix controller, I have a case statement whose argument is a call to a service layer module. I’m trying to implement a standardized ServiceError struct for all service layer modules, and pattern match on the struct in case of a failure:

case PostService.read(id, :id) do
    {:ok, post} ->
        render(conn, "show.html", post: post)
    %ServiceError{cause: ErrorDefs.not_found} ->
        Helpers.render_not_found(conn)
end

Notice “ErrorDefs.not_found”. Initially I was simply passing atoms around, but that seemed hard to maintain in the long run. I want to use something that can be documented in a way that IDE tooling can work with. So basically, I have a module named ErrorDefs (which is require'd at the top of the controller), and ErrorDefs defines macros with arity of 0 and a simple atom as an output:

defmodule Project.ErrorDefs do
    defmacro not_found(), do: :not_found
end

Now, I get the following compilation error:
cannot invoke remote function ErrorDefs.not_found/0 inside match
which is strange for a few reasons. First, it’s a macro and not a function. Second, I wrote a small project just to test this out, and it worked fine in there (I’ll include below). previously I tried writing everything in ErrorDefs as inlined functions, but I got the same error and decided that macros would be more appropriate.

Is there something unusual about Phoenix controllers that would keep macros from expanding in this scenario?

Any help is appreciated! :slight_smile:

Test file:

defmodule Macros do
  defmacro singleton(), do: :hello
  defstruct [:key]
end

defmodule Example do
  require Macros
  def test do
    case %Macros{key: Macros.singleton} do
      # Compiles fine, running `Examples.test` returns `:ok`
      %Macros{key: Macros.singleton} ->
        :ok
      _ ->
        :error
    end
  end
end

Your test file works perfectly here, and there should be nothing special with phoenix controllers either.

Do you have your code somewhere?

So it turns out my namespacing was wrong –
I thought that require Project.ErrorDefs implicitly aliased ErrorDefs, which is not the case. Adding an alias directive to the top of the controller fixed the issue.

1 Like

Heh, I thought that might have been it but your example did not replicate that. ^.^;