Does Phoenix have a Module Attribute syntax for Plug?

Hey there!

I don’t know if this already exists and if not, if it would be even possible to do. However, I’ve seen Module Attributes in some places and I’m wondering, if the following would make sense:

  @plug Plugs.Auth.Logout
  def logout(conn, _params) do
    redirect(conn, to: auth_path(conn, :login))
  end

This looks much nicer than having to write the following, it also allows to see which Plugs work on that specific function. Eventually it’s the same thing with @ in front and without the when guard.

  plug Plugs.Auth.Logout when action in [:logout]

  def logout(conn, _params) do
    redirect(conn, to: auth_path(conn, :login))
  end
1 Like

No, it’s not possible.
Module attributes does not support guards.

Macro: Plug.__ using __ adds another use for Plug.Builder. In this module there is a macro called: plug which you are currently using.

You cannot also use plug like you already pointed.
However you can pass options for your plug like:

plug MyPlug, action: :logout

and then process it like:

defmodule MyPlug do
  def init(opts) do
    opts
  end

  def call(conn, opts) do # add when here
    # do what you want if action is: logout
  end
  def call(conn, _opts), do: conn
end

Of course you can use your own module attributes as in any other module, but plug is a macro.

Also, from the developer’s point of view it’s much nicer if library authors are providing functions/macros (as opposed to requiring to use module attributes) because functions/macros can be documented.

P.S. plug macro, under the hood, actually uses @plugs module attribute :slight_smile: but that’s an implementation detail that user’s shouldn’t depend on. See: https://github.com/elixir-lang/plug/blob/v1.3.0/lib/plug/builder.ex#L151

Thanks for your answers!

What do you mean by that? I wouldn’t want to introduce new functionality, the idea is just to write regular plugs, but having a nicer way to chain them together. So plug Plugs.Auth.Logout when action in [:logout] could be written as @plug Plugs.Auth.Logout just above a logout function, so it’s obvious which function I want to use the plug for. Probably I just didn’t understand what you mean :smile:

I believe we considered something as above but we did not go with it because, while it is handful for the cases where you want to apply a plug per action, it is less useful when you want to apply it to all actions. Therefore we preferred a more general mechanism.

We could support both but given we already have a mechanism that works fairly well, we won’t gain much by supporting two of them.

Also, as @wojtekmach said, the plug approach is easier to document compared to @plug.

1 Like

Plug uses &Module.register_attribute/3 with:

accumulate: true

option, so if we can define multiple plugs in one module attribute without override previous.
However you should not use use this attribute, because:

  1. As it was already pointed: it’s not so easy to document as using macro.
  2. It’s not documented, so it can be changed when public API stays unchanged. For example private methods may use same module attribute structure, but with different name. You need to use plug macro and expect that all inside works until API break release and finally you are protected from that updates by selected version in your deps option in MixFile configuration until you changed it manually.

I see, thanks!

All right, so having a syntax which doesn’t support multiple plugs wouldn’t even make sense, it just looked nice :smiley: Thanks again for your responses!