plymio_codi v0.3.1 Plymio.Codi.Pattern.Delegate View Source

The delegate patterns build Kernel.defdelegate/2 call(s).

Delegated functions can be built with, optionally, with a @doc, @since and/or @spec.

See Plymio.Codi for an overview and documentation terms

Note the delegated mfa: {module, function, arity} is validated i.e. the function must exist in the module with the given arity.

If :delegate_doc is not in the pattern opts, a default of :delegate is used. (It can be disabled by explicily setting :fun_doc to nil - not false).

Pattern: delegate

Valid keys in the cpo are:

KeyAliases
:delegate_module:to, :module, :fun_mod, :fun_module, :function_module
:delegate_name:as
:delegate_doc:doc, :fun_doc, :function_doc
:delegate_args:args, :fun_args, :function_args
:delegate_arity:arity, :fun_arity, :function_arity
:fun_name:name, :function_name
:spec_args
:spec_result:result, :fun_result, :function_result
:since

Examples

A simple case. Note the automatically generated :delegate-format @doc.

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_one, arity: 1, module: ModuleA],
...>   delegate: [name: :fun_due, arity: 2, module: ModuleA],
...>   delegate: [name: :fun_tre, arity: 3, module: ModuleA]
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["@doc \"Delegated to `ModuleA.fun_one/1`\"",
 "defdelegate(fun_one(var1), to: ModuleA)",
 "@doc \"Delegated to `ModuleA.fun_due/2`\"",
 "defdelegate(fun_due(var1, var2), to: ModuleA)",
 "@doc \"Delegated to `ModuleA.fun_tre/3`\"",
 "defdelegate(fun_tre(var1, var2, var3), to: ModuleA)"]

Here showing the auto-generated @doc disabled.

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_one, arity: 1, module: ModuleA, doc: nil],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["defdelegate(fun_one(var1), to: ModuleA)"]

This example shows explicit function arguments (:args) being given:

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_one, args: :opts, module: ModuleA, doc: nil],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["defdelegate(fun_one(opts), to: ModuleA)"]

Delegating to a different function name (:as):

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_3, as: :fun_tre, args: [:opts, :key, :value], module: ModuleA],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["@doc \"Delegated to `ModuleA.fun_tre/3`\"",
 "defdelegate(fun_3(opts, key, value), to: ModuleA, as: :fun_tre)"]

Here a @doc, @since, and @spec are generated. Note in the first example the :spec_args are explicily given as well as the :spec_result. In the second no :spec_args are given and the arity used.

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_one, arity: 1, module: ModuleA,
...>   since: "1.7.9", spec_args: :integer, spec_result: :tuple],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["@doc \"Delegated to `ModuleA.fun_one/1`\"",
 "@since \"1.7.9\"",
 "@spec fun_one(integer) :: tuple",
 "defdelegate(fun_one(var1), to: ModuleA)"]

iex> {:ok, {forms, _}} = [
...>   delegate: [name: :fun_one, arity: 1, module: ModuleA,
...>   since: "1.7.9", spec_result: :tuple],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["@doc \"Delegated to `ModuleA.fun_one/1`\"",
 "@since \"1.7.9\"",
 "@spec fun_one(any) :: tuple",
 "defdelegate(fun_one(var1), to: ModuleA)"]

Showing validation of the mfa:

iex> {:error, error} = [
...>   delegate: [name: :fun_one, arity: 2, module: ModuleZ],
...> ] |> produce_codi
...> error |> Exception.message
"mfa {ModuleZ, :fun_one, 2} module unknown"

iex> {:error, error} = [
...>   delegate: [name: :fun_1, arity: 2, module: ModuleA],
...> ] |> produce_codi
...> error |> Exception.message
"mfa {ModuleA, :fun_1, 2} function unknown"

iex> {:error, error} = [
...>   delegate: [name: :fun_one, arity: 2, module: ModuleA],
...> ] |> produce_codi
...> error |> Exception.message
"mfa {ModuleA, :fun_one, 2} arity unknown"

Pattern: delegate_module

The delegate_module pattern builds a delegate function for one or more functions in a module.

As with :delegate, @doc and/or @since can be generated at the same time.

Valid keys in the cpo are:

KeyAliases
:delegate_module:to :module, :fun_module, :fun_mod, :function_module
:delegate_doc:doc, :fun_doc, :function_doc
:take
:drop
:filter
:reject
:since

To determine which functions to delegate, the “function v arity” (fva) for the module is first obtained by calling e.g. ModuleA.__info__(:functions).

The delegate options can include :take, :drop, :filter or :reject keys to “edit” the fva..

The first two take zero, one or more function names and are used in a call to e.g. Keyword.take/2 with the fva.

The second two keys require an arity 1 function (predicate) passed a {fun,arity} tuple, returning true or false and is used with e.g. Enum.filter/2.

Note the fva edits are applied in order of occurence so :take-ing a function already :reject-ed will do nothing.

Here all functions in the module (ModuleA) are wanted with auto-generated @doc and @since:

iex> {:ok, {forms, _}} = [
...>   delegate_module: [module: ModuleA, since: "1.7.9"],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["@doc \"Delegated to `ModuleA.fun_due/2`\"",
 "@since \"1.7.9\"",
 "defdelegate(fun_due(var1, var2), to: ModuleA)",
 "@doc \"Delegated to `ModuleA.fun_one/1`\"",
 "@since \"1.7.9\"",
 "defdelegate(fun_one(var1), to: ModuleA)",
 "@doc \"Delegated to `ModuleA.fun_tre/3`\"",
 "@since \"1.7.9\"",
 "defdelegate(fun_tre(var1, var2, var3), to: ModuleA)"]

Here arity 2 funs are selected, and @doc is disabled.

iex> {:ok, {forms, _}} = [
...>   delegate_module: [
...>     module: ModuleA, doc: nil,
...>     filter: fn {_fun,arity} -> arity == 3 end],
...> ] |> produce_codi
...> forms |> harnais_helper_format_forms!
["defdelegate(fun_tre(var1, var2, var3), to: ModuleA)"]