Packages

SuperWorker is a powerful Elixir library for working with supervisors and background jobs. It provides a much simpler approach than traditional supervisors. This library is currently under development and is unstable, so it is not recommended for production use.

Current section

Files

Jump to
super_worker lib supervisor worker worker.ex
Raw

lib/supervisor/worker/worker.ex

defmodule SuperWorker.Supervisor.Worker do
@moduledoc """
Documentation for `SuperWorker.Supervisor.Worker`.
"""
@default_restart_strategy :transient
alias SuperWorker.Supervisor.{Validator, Constants}
@enforce_keys [:id, :fun]
defstruct [
# worker id, unique in supervisor.
:id,
# name of worker, is atom for register process to communicate directly without lookup pid.
:name,
# restart strategy of worker. Affected by the supervisor & parent restart strategy.
restart_strategy: @default_restart_strategy,
# type of worker. :standalone, :group, :chain
type: :standalone,
# anonymous function {:fun, fun} or {function, module, arguments} of worker.
fun: nil,
# number of workers in chain.
num_workers: 1,
# parent(group/chain) id.
parent: nil,
# order in chain.
order: nil
]
@type t :: %__MODULE__{
id: any(),
name: atom() | nil,
restart_strategy: atom(),
type: :standalone | :group | :chain,
fun:
nil
| {:fun, fun()}
| {module(), atom(), [any()]}
| {:gen_server, {module(), atom(), [any()]}},
num_workers: non_neg_integer(),
parent: :standalone | {atom(), any()},
order: non_neg_integer() | nil
}
@type from_config_result :: {:ok, t()} | {:error, term()}
@spec from_config(keyword()) :: from_config_result()
def from_config(options) do
case Keyword.get(options, :type) do
:standalone ->
params = Constants.Types.standalone_worker_params()
validate_with_params(options, params)
:group ->
params = Constants.Types.group_worker_params()
validate_with_params(options, params)
:chain ->
params = Constants.Types.chain_worker_params()
validate_with_params(options, params)
invalid_type ->
{:error, "invalid type: #{inspect(invalid_type)}"}
end
end
defp validate_with_params(options, params) do
with {:ok, options} <-
Validator.normalize_options(options, params),
{:ok, options} <- default_options(options),
{:ok, options} <- validate_restart_strategy(options),
{:ok, options} <- validate_options(options),
{:ok, options} <- map_to_struct(options) do
{:ok, options}
end
end
@spec default_restart_strategy() :: atom()
def default_restart_strategy() do
@default_restart_strategy
end
defp validate_restart_strategy(opts) do
if opts.restart_strategy in Constants.Strategies.standalone_restart_strategies() do
{:ok, opts}
else
{:error, "Invalid standalone restart strategy, #{inspect(opts.restart_strategy)}"}
end
end
defp validate_options(options) do
errors =
Enum.reduce(options, [], fn
{:order, value}, acc ->
if not is_integer(value) or value < 0 do
[{:error, {:invalid, {:order, value}}} | acc]
else
acc
end
{:name, value}, acc ->
if is_atom(value) do
acc
else
[{:error, {:invalid, {:name, value}}} | acc]
end
{:fun, value}, acc ->
case value do
{module, function, args}
when is_atom(module) and is_atom(function) and is_list(args) ->
acc
{:fun, fun} when is_function(fun) ->
acc
{:gen_server, {module, function, args}}
when is_atom(module) and is_atom(function) and is_list(args) ->
acc
_ ->
[{:error, {:invalid, {:fun, value}}} | acc]
end
_other, acc ->
acc
end)
if errors != [] do
{:error, Enum.reverse(errors)}
else
{:ok, options}
end
end
defp default_options(options) do
options =
if Map.has_key?(options, :id) do
options
else
Map.put(options, :id, SuperWorker.Supervisor.Utils.random_id())
end
options =
if Map.has_key?(options, :restart_strategy) do
options
else
Map.put(options, :restart_strategy, default_restart_strategy())
end
{:ok, options}
end
defp map_to_struct(options) when is_map(options) do
{:ok, struct(__MODULE__, options)}
end
end