process_tree_dictionary v1.0.1 ProcessTreeDictionary

Implements a dictionary that is scoped to a process tree by replacing the group leader with a process that:

  • Maintains a dictionary of state
  • Forwards all unrecognized messages to the original group leader so that IO still works

Any process can be the root of its own process tree by starting a ProcessTreeDictionary.

The Erlang docs provide a summary of what a group leader is:

Every process is a member of some process group and all groups have a group leader. All I/O from the group is channeled to the group leader. When a new process is spawned, it gets the same group leader as the spawning process.

Since every new process inherits the group leader from its parent, a process can start a ProcessTreeDictionary in place of its existing group leader, and every descendant process will inherit it, allowing them to access the state of the same ProcessTreeDictionary.

Note that all functions provided by this module rely upon side effects. Since referential transparency is a primary value of Elixir, Erlang, and functional programming in general, and none of the functions provided by this module are referentially transparent, we recommend you limit your usage of this module to specialized situations, such as for building test fakes to stand-in for stateful modules.

Important caveat: if any processes in your tree start an application with Application.start, Application.ensure_started, or Application.ensure_all_started, the started application processes will not be a part of the process tree, because OTP manages application starts for you. If you need to access the ProcessTreeDictionary from the started processes, you’ll need to start the supervisor of the application yourself. For more info, see the Erlang docs.

Summary

Functions

Starts a ProcessTreeDictionary if this process does not already have access to one

Gets the value for the given key from the dictionary

Puts the given value into the dictionary under the given key

Updates the key in the dictionary using the given function

Types

simple_key :: String.t | atom | integer

Functions

ensure_started()

Specs

ensure_started :: :ok

Starts a ProcessTreeDictionary if this process does not already have access to one.

If a ProcessTreeDictionary has already been started in this process or in a parent process (prior to this process being spawned), this will be a no-op.

Examples

iex> ProcessTreeDictionary.ensure_started()
:ok
get(key, default \\ nil)

Specs

get(key :: key, default :: any) :: any

Gets the value for the given key from the dictionary.

Returns the default value if the ProcessTreeDictionary has not been started or if the dictionary does not contain key. The key can be a simple key (such as a string, atom, or integer) or a key path, expressed as a list.

Examples

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put(:language, "Elixir")
iex> ProcessTreeDictionary.get(:language)
"Elixir"

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put([MyApp, :meta, :language], "Elixir")
iex> ProcessTreeDictionary.get([MyApp, :meta, :language])
"Elixir"
put(key, value)

Specs

put(key :: key, value :: any) :: :ok

Puts the given value into the dictionary under the given key.

The key can be a simple key (such as a string, atom, or integer) or a key path, expressed as a list.

Raises an error if a ProcessTreeDictionary has not been started.

Examples

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put(:language, "Elixir")
:ok

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put([MyApp, :meta, :language], "Elixir")
:ok
update!(key, fun)

Specs

update!(key :: key, (any -> any)) :: any

Updates the key in the dictionary using the given function.

The key can be a simple key (such as a string, atom, or integer) or a key path, expressed as a list.

If the key does not exist, raises a KeyError.

Examples

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put(:language, "Elixir")
iex> ProcessTreeDictionary.update!(:language, &String.downcase/1)
iex> ProcessTreeDictionary.get(:language)
"elixir"

iex> ProcessTreeDictionary.ensure_started()
iex> ProcessTreeDictionary.put([MyApp, :meta, :language], "Elixir")
iex> ProcessTreeDictionary.update!([MyApp, :meta, :language], &String.downcase/1)
iex> ProcessTreeDictionary.get([MyApp, :meta, :language])
"elixir"