View Source ChildsPlay (ChildsPlay v0.1.0)
Make building a list of children a Supervisor manages a problem of the
past.
We've all been there before. Your new project starts out with a clean Supervision tree of only a few children. Make sure this is running, make sure that is running. Life is good. But eventually rules start to pop up that make it so the list of children you start gets more and more complex.
Some of the greatest hits of the projects I've been involved in.
"Don't start the email server in Dev. We use a mocked version there."
"If its a cronjob node we don't want to start up consumers."
This leads to all sorts of variations on how to filter and tranform the list of children for our Supervisors. Most lead to bloated descriptions and confusing logic. We can turn it into childs play....
defmodule MyApp.Application do
use Application
# importing not required - just cleaner
import ChildsPlay
def start(_type, _args) do
[
# Add regular children you know and love (possibly more than the others)
MyApp.Repo,
given(
Application.get_env(:my_app, :cronjob_node?, false),
ConsumerSupervisor
),
given(
System.get_env("ENV") == "PROD",
[
MyApp.MailServer,
MyApp.MetricsServer
]
),
]
|> build()
|> Supervisor.start_link(strategy: :one_for_one)
end
end
building-child-lists
Building child lists
The core function at work is given/2 which takes a boolean (or a predicate
function) and a single child or a list of children to start. When the value is
true the children will be rendered.
The core data structure at play here is just a list, the trick that
ChildsPlay adds is that now nested lists get flattened. What this means is
that we could go as far as doing this if we wanted to.
defmodule MyApp.Application do
use Application
import ChildsPlay
def start(_type, _args) do
[
given(
System.get_env("ENV") == "PROD",
[
MyApp.MailServer,
given(
Application.get_env(:my_app, :cronjob_node?, false),
[
MyApp.Consumer1,
MyApp.Consumer2
]
)
]
),
]
|> build()
|> Supervisor.start_link(strategy: :one_for_one)
end
endWith great power comes great responsibility. Always favor whatever is more declarative and childs play for all!
Link to this section Summary
Functions
If condition is true, or predicate returning true allow children.
Link to this section Types
@type children() :: [ [] | Supervisor.child_spec() | {module(), term()} | module() | (old_erlang_child_spec :: :supervisor.child_spec()) ]
@type predicate() :: (() -> boolean())
Link to this section Functions
If condition is true, or predicate returning true allow children.
[
ChildsPlay.given(true, Agent),
ChildsPlay.given(false, Agent),
ChildsPlay.given(true, [Worker1, Worker2]),
ChildsPlay.given(fn -> false end, [Worker3, Worker4])
]
|> ChildsPlay.build()
|> Supervisor.start_link(strategy: :one_for_one)