Idiomatic way to start Phoenix dependencies

I have defined a simple Plug module that requires a Supervised GenServer process to be spawned in the Plug’s init callback using the :simple_one_for_one strategy. Problem is, it seems that the supervisors aren’t started until after the init callback is invoked. Is there an idiomatic way to start the Plug’s supervisor’s prior to Phoenix starting?

Currently, I’m invoking Application.ensure_started but it feels like there’s a cleaner, more idiomatic way to solve this.

Thanks!

All of that applications that you need in production should be listed in :applications key in def applications in your mix.exs file.

If you are using Elixir v1.4 or later though, you can get rid of the :applications key, as Mix will automatically inflect that for you.

2 Likes

Thanks for the quick response, Jose!

I just want to make sure I understand your reply correctly.

On init, the Plug calls { :ok, pid } = Supervisor.start_child(<SUPERVISOR NAME>, []). I’m running 1.5.1 on dev mode with the following start command:

PORT=$PORT iex -S mix phx.server

so I shouldn’t need to declare the Plug dependency in :applications, correct?

If you are running elxiir 1.5 or newer and have a list of :applications, then you already de-activated the automatic inference of applications taht needs to be started.

If you target versions older than 1.5, you need to stick to manually add it to the :applications list. If though, you want to support only elixir 1.5 and newer, you should convert your current :applications list into the newer way of providing a :extra_applications list. Usually this contains only :logger or other libraries which are bundled with elixir or erlang, and all dependencies, that are not specified as runtime: false will be added to the internal :applications list.

eg.

def application do
  [mod: ...,
   applications: [:logger, :foo]]
end

def deps do
  [
    {:foo, ">= 1.0.0"},
    {:bar, ">= 0.1.0"}
  ]
end

can be converted to:

def application do
  [mod: ...,
   extra_applications: [:logger]]
end

def deps do
  [
    {:foo, ">= 1.0.0"},
    {:bar, ">= 0.1.0", runtime: false}
  ]
end
1 Like

Interesting. I’m still having trouble getting the dependency’s supervisor to start. Here is my application.ex.

defmodule PlugTest.Mixfile do
   use Mix.Project

   def project do
     [
       app: :plug_test,
       version: "0.0.1",
       elixir: "~> 1.5",
       elixirc_paths: elixirc_paths(Mix.env),
       compilers: [:phoenix, :gettext] ++ Mix.compilers,
       start_permanent: Mix.env == :prod,
       deps: deps()
     ]
   end

   # Configuration for the OTP application.
   #
   # Type `mix help compile.app` for more information.
   def application do
     [
       mod: {PlugTest.Application, []},
       extra_applications: [:logger, :runtime_tools]
     ]
   end

   # Specifies which paths to compile per environment.
   defp elixirc_paths(:test), do: ["lib", "test/support"]
   defp elixirc_paths(_),     do: ["lib"]

   # Specifies your project dependencies.
   #
   # Type `mix help deps` for examples and options.
   defp deps do
     [
       # the supervised dependency
       {:pluggy, path: "../pluggy"},

       {:phoenix, "~> 1.3.0"},
       {:phoenix_pubsub, "~> 1.0"},
       {:phoenix_html, "~> 2.10"},
       {:phoenix_live_reload, "~> 1.0", only: :dev},
       {:gettext, "~> 0.11"},
       {:cowboy, "~> 1.0"},
     ]
   end
 end

And I start it with { :ok, registry } = Supervisor.start_child(<SUPERVISOR NAME>, []).

Finally, the compilation error I’m getting:

== Compilation error in file lib/plug_test_web/endpoint.ex == ** (exit) exited in: GenServer.call(Pluggy.RegistrySupervisor, {:start_child, []}, :infinity).

Any thoughts?