···
2
+
title: "Let's talk about `application/0`"
3
+
date: 2019-07-26T11:36:01+02:00
5
+
Have you ever thought about that one simple function in your `mix.exs`? It
6
+
comes out as quite powerful and useful place for storing configuration and
15
+
When you start your new Elixir project via `mix new my_awesome_project` you will
16
+
end with something like this in `mix.exs`:
19
+
defmodule MyAwesomeProject.Mixfile do
24
+
name: :my_awesome_project,
32
+
extra_applications: [:logger]
44
+
And in most cases you will focus on `deps/0`, sometimes on `config/0`, but the
45
+
`application/0` you will almost never touch, and if you do, you probably will only
46
+
need it to add new entry in `:extra_applications` or sometimes
47
+
`:included_applications`. Except for, that this function interns are terra
48
+
incognita, a place where you never look unless you are forced to, like "Read
49
+
it later" list in Safari. This is sad, as it is quite powerful and useful
52
+
However first things first.
54
+
## What `application/0` is for?
56
+
In Erlang the running system is built from applications. Those applications are
57
+
like processes in your system managed by your system supervisor (SysV, systemd,
58
+
OpenRC, launchd, etc.). They are launched either during VM startup or on
59
+
direct user request via `Application.start/1-2` and its family. Starting
60
+
this application is described in `my_awesome_app.app` file which is commonly
61
+
generated by the build system from the template. In Rebar3 projects this
62
+
template is `src/appname.app.src` and in Elixir it's the return value of the
63
+
named `application/0` function. This generated file is known as [Application
64
+
Resource File][app file]. It is just tuple in form of `{:application,
65
+
:my_awesome_app, opts}` where `opts` is a keyword list of additional properties
66
+
(to be exact it's output of the `application/0` function).
68
+
Two of those optional fields are quite known in the Elixir community:
70
+
- `:mod` which is a tuple `{module(), term()}` containing the starting point
71
+
of our application; in most cases this is the module that will return main
72
+
supervisor of the application.
73
+
- `:applications` contains all applications required by our application; younger
74
+
Elixir developers possibly never seen that as since version 1.5 this field
75
+
is automatically filled by parsing `:deps`, though we still can add entries
76
+
there via `:extra_applications`
78
+
There are also few Elixir specific fields, sometimes used in larger projects:
80
+
- `:extra_applications` - those should be included in the release
81
+
and automatically started before running current application; but aren't in the
82
+
dependencies list because, for example, are in default distribution, for
83
+
example `logger` or `inets`.
84
+
- `:included_applications` - applications which should be included in the
85
+
release, but not automatically started on boot.
87
+
Wait, there is more!
89
+
Unfortunately not all of the highly useful keys are used/known in the community.
91
+
## Application environment
93
+
For some reason everyone calls it configuration, so if you are familiar with
94
+
`config/config.exs` and for some reason you decided to go [against the *Library
95
+
Guidelines*](guidelines) and you have decided to use application environment for
96
+
configuring your code (there are reason to do so, even when you publish your
97
+
code as a "library", see `lager` or `opencensus`) then you will soon find that
98
+
putting configuration in your's library `config/config.exs` do not matter much
99
+
in it's dependants. You have 2 possibilities how to solve that:
101
+
- Use `Application.get_env/3` and define your "default" as a 3rd argument.
102
+
- Use `:env` to set data that should be loaded to application environment by
105
+
Be wary that the second option works only for current application, so you
106
+
cannot configure other applications (for example `logger`) there. *But what is
107
+
the point?* You may ask, and I found one. If you want to use default
108
+
`sys.config` file for configuring your application then sometimes few pointless
109
+
configuration option can land there, like `:ecto_repos` variable which truly
110
+
doesn't matter much in production as it's only used by Ecto's Mix tasks. What
111
+
I do is to add new entry in `application/0` with:
115
+
ecto_repos: [MyAwesomeApp.Repo]
119
+
And call it a day. Now I can focus on keeping **real** configuration options in
120
+
`config/config.exs` and remove unneeded fields from there. But remember that you
121
+
still can override these values by setting them in `sys.config` if needed, so
122
+
this is pure win for me.
126
+
Application configuration also allows you to define additional pieces of code to
127
+
be run after your application started. For example imagine situation when you
128
+
want to send Slack notification that given node started and is ready to work.
129
+
You can do it via temporary task in `Supervisor.init/2` by defining child list
136
+
{Task, &send_slack_notification/0}
140
+
Alternatively you can use `:start_phases` in `application/0`:
148
+
And then define in your `Application` module function `start_phase/3`:
151
+
def start_phase(:notify, :normal, opts) do
152
+
:ok = send_slack_notification()
156
+
Where 1st argument will be the name of the phase, 2nd will be start type the
157
+
same as in [`Application.start/2` callback](https://hexdocs.pm/elixir/Application.html#c:start/2),
158
+
and 3rd is the value passed in `:start_phases`.
160
+
The awesome part there is that `start_phase/3` is called not only for current
161
+
application, but all of it's dependencies as well.
163
+
## Registered names
165
+
This is one of the things that had more sense in Erlang world than in Elixir,
166
+
but by being good citizen we should use it as well. This is a nice solution for
167
+
lack of namespacing in Erlang - it allowed release tools to detect collisions in
168
+
named processes. This is simple list of atoms that contain all names that are
169
+
globally registered by this application. Example form [Elixir's
170
+
Logger](https://github.com/elixir-lang/elixir/blob/ee9f38635e9a6c816adb575fc9431ded49be8032/lib/logger/mix.exs#L14):
173
+
registered: [Logger, Logger.BackendSupervisor, Logger.Supervisor, Logger.Watcher]
176
+
Unfortunately Phoenix do not use this field itself and do not suggest using one
177
+
in it's default project generator. But in general it's good practise.
181
+
There is much more in application configuration to what most Elixir code is
182
+
using, it is worth sometimes to read how your application is defined and ran.
183
+
For more information about generating application description file you can check
184
+
out [`mix help compile.app`](https://hexdocs.pm/mix/Mix.Tasks.Compile.App.html).
186
+
[app file]: http://www.erlang.org/doc/design_principles/applications.html#application-resource-file
187
+
[guidelines]: https://hexdocs.pm/elixir/library-guidelines.html