Do plugs always create compile time dependency?

I have a Users module:

defmodule Delete.Users do
  def get_user, do: nil
end

I have. a module plug:

defmodule DeleteWeb.UserPlug do
  def init(_), do: []

  def call(conn, _opts) do
    Map.put(conn, :user, Delete.Users.get_user())
  end
end

I have a controller:

defmodule DeleteWeb.PageController do
  use DeleteWeb, :controller

  # The Module Plug version creates an implicit compile time dependency with `Users` module:
  # plug(DeleteWeb.UserPlug)

  # The locally defined function plug does not create an implicit compile time dependency 
  # with `Users` module:
  # defp user_plug(conn, _), do: Map.put(conn, :user, Delete.Users.get_user())
  # plug(:user_plug)

  def index(conn, _params) do
    render(conn, "index.html")
  end
end

I have init_mode set in my config:

config :phoenix, :plug_init_mode, :runtime

With the above setup, using the module UserPlug, updating Users recompiles PageController.

As my app grows, I will re-use this plug in all/most of controllers (as well as other similar plugs). Is it possible to share this plug around my various controllers without creating compile time dependencies all over the place?

It was a bug. It has been fixed on Phoenix master and v1.4 branches.

4 Likes

Thanks @josevalim

By v1.4 branches I take that to mean all phoenix versions: 1.4.x but obviously that is wrong as I have the following dependency: {:phoenix, "~> 1.4.14"}.

When updating to {:phoenix, git: "https://github.com/phoenixframework/phoenix.git"} I can see that both the function plug or the module plug do not create these implicit compile time dependencies.


In case anyone else is coming across this, the following does still of course cause implicit compile time dependency:

defmodule DeleteWeb.FunctionPlugs do
  def user_plug(_, _), do: Delete.Users.get_user()
end
defmodule DeleteWeb.PageController do
  use DeleteWeb, :controller

  # plug(DeleteWeb.Plugs.User)

  # defp user_plug(_, _), do: Delete.Users.get_user()
  # plug(:user_plug)

  import DeleteWeb.FunctionPlugs, only: [user_plug: 2]
  plug(:user_plug)

  def index(conn, _params) do
    render(conn, "index.html")
  end
end

So shared plugs are better to be Module Plugs rather than imported function plugs.