defmodule Grains do @moduledoc """ Grains describes data flow as a graph with interchangeable parts. To accomplish that grains divides the graph (`Recipe`) from the implementation of the nodes (`Grains`). Together these parts can be used to make a `Bread` which describes how the graph translates to processes. Each process has a symbolic short name, which is resolved to the registered name of the process via a map (`%Bread{}.process_map`). """ alias Grains.Bread alias Grains.Recipe alias Grains.Supervisor defstruct [:map] @doc """ This function describes the default implementations for auxiliary grains, for example the periodic timer. Currently these default grains are provided: iex> Grains.default_grains() %{periodic: Grains.Timer} """ def default_grains() do %{ periodic: Grains.Timer } end def new(map) do %__MODULE__{map: map} end @doc """ Merges to grain maps, works the same as Map.merge/2. Seconds grains override first in case of a name clash. """ def merge(%__MODULE__{map: a}, %__MODULE__{map: b}) do a |> Map.merge(b) |> new() end defdelegate new_recipe(name, new), to: Grains.Recipe, as: :new defdelegate merge_recipes(name, a, b), to: Grains.Recipe, as: :merge defdelegate start_supervised(recipe, grains, args \\ []), to: Grains.Supervisor, as: :start_link @spec make_name(Recipe.t, atom, atom) :: atom def make_name(recipe = %Recipe{}, bread_id, short_name) do [recipe.name, bread_id, short_name] |> Module.concat() end @spec make_bread_name(Recipe.t, atom()) :: atom() def make_bread_name(recipe = %Recipe{}, bread_id) do [recipe.name, bread_id] |> Module.concat() end @spec get_name(Supervisor.t, atom) :: atom() def get_name(supervisor, short_name) do Module.concat([Supervisor.get_root_name(supervisor), short_name]) end @spec concat_name(Bread.t, atom) :: atom def concat_name(bread = %Bread{}, grain) do Module.concat([bread.name, grain]) end @doc """ Description of a periodic grain. """ def periodic(grain, period, opts \\ %{with_timestamps: false}) do {:periodic, grain, Map.put(opts, :period, period)} end end