What is the proper way to pass arguments to a dependent application?

I have a library application X that is setup to take in arguments via its start() function, and right now I’m successfully having these set via the mod keyword in mix.exs.

I am now writing another application (Y) that is using application X but it needs the ability to customize the arguments that it starts application X with (including renaming arguments Y has into what X expects.

I can’t find any documentation on how I can have application Y start application X with specific arguments. Applications.start() does not seem to take in arguments.

The only idea I have is having my start function in Y explicitly call X.start(type, args) and add that supervisor as a child to Y’s supervision tree.

Is that the correct way to go or am I missing something more obvious?

After playing around a bit and reading whatever docs I could find (not much though), I did come up with a solution (though I’m not totally sure yet if it’s optimal).

Essentially instead of relying on the mod: keyword in mix.exs I am instead using environment variables in the mix.exs. In my X.start() function I will then turn the environment variables I am expecting into a keyword list I can then pass into the module expecting it.

I can then create a X.start_app() function that will take the values as function parameters and turn them into environment variables for application X (that way the exact variable names and whatnot are encapsulated within application X). After that I can just Application.ensure_all_started(:x) and theoretically that issue is solved.

I’m not sure if this is the most optimal solution but I think it solves my immediate need.

Hi,
Well It depends on the generation time of the config.

  • If you can define the config options at compile time , you can put them in config.exs of the final application using config :x, key: value[to set] and Application.get_env(:x, :key) [to fetch]. See also the remark at the and
  • if the config state is only known at startup time you could use [Application.start/2] (http://elixir-lang.org/docs/stable/elixir/Application.html#module-application-module-callback) and start the application using Supervisor. EDIT: See following reply
  • If You need runtime config, or multiple applications using X with startup time config then creating an Agent is the way to go

See also this question for a related problem I had a while ago, regarding the non-usability of config.exs from dependent packages.

This is the use case I am going to (one time initial startup) but your suggestion doesn’t work.

That doesn’t work because Application.start/2 doesn’t allow you to pass in arguments when starting an application (even though your implemented start function takes in arguments).

At the end of the day I realized I was designing this wrong and instead of making this an application it should instead just be a behavior that gets implemented anyway.

You are right. this is wrong:

In fact it might be better to rely on Application.get_env/put_env alone as the Application can be restarted At the Supervisors (dis)/grace.

As far as i understand it these are the processes in chronological start order:

  • Proc 1, the root supervisor calls X.start/2
  • X.start/2 starts the supervisor via Supervisor.start_link/2 (Proc 2)
  • The supervisor starts the dependency children (Procs 3-n)

Now Proc 2 should be able to restart Procs 3-n according to it’s strategy which is oblivious to the arguments within the Application domain.

EDIT: Indeed no use case is obvious to me, in which the arguments in mix.exs mod:{X,args} are better suited than config :app, args in config.exs. Perhaps someone else finds one?

I was wondering the same question when finally I remembered that ExUnit has and very elegant way to handle this same case.

1 Like