Hook behaviour (hook v0.6.0)
An interface to define and leverage runtime resolution.
In various module and function docs it is common to document options like so:
Options
:option1_name-type. # since no default is specified, this option is required:option2_name-type. Descriptive text. # since no default is specified, this option is required:option3_name-type.default. # since a default is specified, this option is not required:option4_name-type.default. Descriptive text. # since a default is specified, this option is not required
Compile time Configuration
These options can be set compile time to configure how m:Hook.hook/1 behaves. m:Hook.hook/1
will be referred to as "the macro".
mappings-keyword(). Mappings that the macro will resolve against.resolve_at-:compile_time | :run_time | :never. When:compile_timeor:run_time, the macro will resolve its term at the respective time. When:never, the macro will compile directly into its term with no attempt to resolve against mappings.top_level_module_allowlist-[module()]. When the macro's calling module's top-level-module is in this list, the:resolve_atoption will be respected. Otherwise, it will behave as ifresolve_at: :neverfor that instance of the macro. This allows developers to control whether or not individual instances of the macro should ever attempt resolution or not with a top-level-module granularity. The common value for this option is the top level module of your application so that any instance of the macro throughout your application's code will respect the:resolve_atoption, and any instance of the macro outside your application's code will behave as ifresolve_at: :never. An example of "code outside your application" is if any of your application's dependencies happen to also use the macro.
Groups
Mappings are defined under group/0s. Hook defaults the group to be the calling process
making process isolation of mappings frictionless. Any term can also be supplied as the group.
Putting :foo under :bar in the :red group, than fetching it from the :blue group will
fail.
Resolution
Starting with the calling process:
does the process define a mapping for the term
1.1. the term has been resolved
does the process define fallbacks
2.1. for each fallback in order: goto step 1
does the process have :"$ancestors"
3.1. for each ancestor in order: goto step 1
does the process have :"$callers"
3.1. for each caller in order: goto step 1
the term has not been resolved
Performance
In general, the thing to focus on with respect to performance is whether the function call is getting serialized through a GenServer or not. You will find that only calls that result in a write get serialized and those that only read do not.
Functions that get serialized through a GenServer:
callback/3,4fallback/1,2put/2,3resolve_callback/3
Functions that do not get serialized through a GenServer:
assert/0,1callbacks/0,1fetch!/1,2fetch/1,2get/1,2,3get_all/0,1hook/1
This means that a "hot path" that is resolving a term via these functions should remain performant outside of extreme cases.
Link to this section Summary
Functions
Asserts that all callbacks defined via Hook.callback/4 have been satisfied.
Defines a callback that can be consumed and asserted on.
Return group's callbacks.
Prepend group to the calling process' fallbacks.
Fetches the value for key for group.
Gets the value for key for group returning default if it is not defined.
Gets all values for group.
A macro that compiles into term itself or a Hook.fetch(term) call.
Puts value under key for group.
Puts all key value pairs under group.
Resolves a callback for the calling process and executes it with args.
Link to this section Types
callback_opt()
Specs
callback_opts()
Specs
callback_opts() :: [callback_opt()]
config()
Specs
count()
Specs
count() :: pos_integer() | :infinity
fun_key()
Specs
fun_key() :: {function_name :: atom(), arity :: non_neg_integer()}
group()
Specs
key()
Specs
key() :: any()
mappings()
Specs
value()
Specs
value() :: any()
Link to this section Functions
assert(group \\ self())
Asserts that all callbacks defined via Hook.callback/4 have been satisfied.
Returns :ok if the assertion holds, raises otherwise.
Infinity-callbacks and 0-callbacks are inherently satisfied.
callback(module, function_name, function, opts \\ [])
Defines a callback that can be consumed and asserted on.
Note: module will be defined under the calling process.
This function will raise when the specified callback is not a public function on module.
Options
:count - How many times the callback can be consumed.
:infinity- The callback can be consumed an infinite number of times. For a module, function, and arity, only a single infinity-callback will be defined at once, last write wins. Infinity-callbacks are always consumed after non-infinity-callbacks.0- The callback should never be consumed. Raises an error if it is. The callback is removed upon raising.
callbacks(group \\ self())
Return group's callbacks.
fallback(src_group \\ self(), dest_group)
Prepend group to the calling process' fallbacks.
NOTE: :global cannot be used as a src_group.
fetch(key, group \\ self())
Fetches the value for key for group.
fetch!(key, group \\ self())
See Hook.fetch/2.
get(key, default \\ nil, group \\ self())
Gets the value for key for group returning default if it is not defined.
get_all(group \\ self())
Gets all values for group.
A macro that compiles into term itself or a Hook.fetch(term) call.
Check the "Compile time Configuration" section for information about configuring this functionality.
put(key, value, group \\ self())
Puts value under key for group.
put_all(kvps, group \\ self())
Puts all key value pairs under group.
resolve_callback(module, fun_key, args)
Resolves a callback for the calling process and executes it with args.
Link to this section Callbacks
assert(group)
Specs
assert(group()) :: :ok
callback(module, function_name, function, callback_opts)
Specs
callback(module(), function_name :: atom(), (... -> any()), callback_opts()) :: :ok
callbacks(group)
Specs
fallback(dest, src)
Specs
fetch(key, group)
Specs
fetch!(any, group)
Specs
get(key, default, group)
Specs
get_all(group)
Specs
get_all(group()) :: {:ok, %{}} | :error
put(key, value, group)
Specs
put_all(mappings)
Specs
put_all(mappings()) :: :ok