plymio_vekil v0.1.0 Plymio.Vekil.Forom.Proxy View Source

The module implements the Plymio.Vekil.Forom protocol and manages a proxy.

A proxy forom holds a reference to another proxy in its :forom field.

When a proxy forom is produced or realised the proxy in the :forom fields is used, together with the vekil, in a call to Plymio.Vekil.proxy_fetch/2 and the forom returned by the fetch is produced or realised.

If the proxy form does not have a vekil an error result is returned.

In many examples the proxy is an atom but that is not a constraint: its type (e.g. atom, string, whatever) is defined and decided by the vekil. (So, for example, looking up an atom in a vekil where the proxies (“keys”) are strings will never succeed). The vekils used in the doctests below use atom proxies.

See Plymio.Vekil.Forom for the definitions of the protocol functions.

See Plymio.Vekil for an explanation of the test environment.

Module State

See Plymio.Vekil.Forom for the common fields.

The default :produce_default is an empty list.

The default :realise_default is the unset value (Plymio.Fontais.the_unset_value/0).

The module’s state is held in a struct with the following field(s):

FieldAliasesPurpose
:foromholds the proxy

Link to this section Summary

Functions

new/1 takes an optional opts and creates a new forom returning {:ok, forom}

new!/1 calls new/1 and, if the result is {:ok, instance} returns the instance

normalise/1 creates a new forom from its argument unless the argument is already one

produce/2 takes a forom and an optional opts

realise/2 takes a forom and an optional opts, calls produce/2, and then gets (Keyword.get_values/2) the :forom key values

update!/2 calls update/2 and, if the result is {:ok, instance} returns the instance

Link to this section Types

Link to this type t() View Source
t() :: %Plymio.Vekil.Forom.Proxy{
  forom: term(),
  produce_default: term(),
  protocol_impl: term(),
  protocol_name: term(),
  proxy: term(),
  realise_default: term(),
  seen: term(),
  vekil: term()
}

Link to this section Functions

Link to this function new(opts \\ []) View Source
new(any()) :: {:ok, t()} | {:error, error()}

new/1 takes an optional opts and creates a new forom returning {:ok, forom}.

Examples

iex> {:ok, forom} = new()
...> match?(%FOROMPROXY{}, forom)
true

Plymio.Vekil.Utility.forom?/1 returns true if the value implements Plymio.Vekil.Forom

iex> {:ok, forom} = new()
...> forom |> Plymio.Vekil.Utility.forom?
true

The proxy is passed using the :forom key:

iex> {:ok, forom1} = FOROMFORM.new(forom: :x_add_1)
...> {:ok, forom} = new(forom: forom1)
...> forom |> Plymio.Vekil.Utility.forom?
true
Link to this function new!(opts \\ []) View Source
new!(any()) :: t() | no_return()

new!/1 calls new/1 and, if the result is {:ok, instance} returns the instance.

Link to this function normalise(value) View Source
normalise(any()) :: {:ok, struct()} | {:error, error()}

normalise/1 creates a new forom from its argument unless the argument is already one.

For a proxy forom normalise/1 offers a small way to reduce boilerplate.

Examples

Here the argument is an atom and a proxy forom is created. Note a vekil is needed to resolve the proxy.

iex> {:ok, %FOROMPROXY{} = forom} = :x_mul_x |> normalise
...> realise_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:ok, {form, _}} = forom |> FOROMPROT.realise(realise_opts)
...> form |> harnais_helper_test_forms!(binding: [x: 3])
{9, ["x = x * x"]}

The function accepts any value as the proxy; it is only when the proxy is accessed by a vekil (e.g. Plymio.Vekil.proxy_fetch/2) can the type of the proxy be known (e.g. an atom) and invalid ones caught.

iex> {:ok, %FOROMPROXY{} = forom} = "maybe a proxy" |> normalise
...> realise_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:error, error} = forom |> FOROMPROT.realise(realise_opts)
...> error |> Exception.message
"proxy invalid, got: maybe a proxy"
Link to this function produce(forom, opts \\ []) View Source
produce(t(), opts()) :: {:ok, {product(), t()}} | {:error, error()}

produce/2 takes a forom and an optional opts.

The value in the :forom field is used, together with the essential vekil, in a call to Plymio.Vekil.proxy_fetch/2 and the forom returned by the fetch. is produced.

Examples

iex> {:ok, forom} = new(forom: :x_add_1)
...> produce_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:ok, {product, _}} = forom |> FOROMPROT.produce(produce_opts)
...> product |> Keyword.get_values(:forom)
...> |> harnais_helper_test_forms!(binding: [x: 7])
{8, ["x = x + 1"]}

A variation of the above example showing that the product has multiple :forom keys, one for each of the constituent, terminal forom in the x_funs list forom.

iex> {:ok, forom} = new(forom: :x_funs)
...> produce_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:ok, {product, _}} = forom |> FOROMPROT.produce(produce_opts)
...> [:forom] = product |> Keyword.keys |> Enum.uniq
...> 3 = product |> Keyword.keys |> length
...> product |> Keyword.get_values(:forom)
...> |> harnais_helper_test_forms!(binding: [x: 7])
{63, ["x = x + 1", "x = x * x", "x = x - 1"]}

A proxy forom can reference another proxy which is itself a proxy forom proxy forom and so on:

iex> {:ok, %VEKILFORM{} = vekil} = [dict: [
...>    p1: :p2,
...>    p2: :p3,
...>    p3: "The End"
...> ]] |> VEKILFORM.new()
...> {:ok, forom} = new(forom: :p1)
...> produce_opts = [vekil: vekil]
...> {:ok, {product, _}} = forom |> FOROMPROT.produce(produce_opts)
...> product |> Keyword.get_values(:forom)
["The End"]

There must be a valid vekil:

iex> {:ok, forom} = new(forom: :x_add_1)
...> produce_opts = [vekil: :invalid_vekil]
...> {:error, error}= forom |> FOROMPROT.produce(produce_opts)
...> error |> Exception.message
"vekil invalid, got: :invalid_vekil"

iex> {:ok, forom} = new(forom: :not_a_proxy)
...> {:error, error}= forom |> FOROMPROT.produce
...> error |> Exception.message
"vekil is unset"

An empty forom does not produce any :forom keys:

iex> {:ok, forom} = new()
...> {:ok, {product, _}} = forom |> FOROMPROT.produce
...> product |> Keyword.get_values(:forom)
[]
Link to this function realise(forom, opts \\ []) View Source
realise(t(), opts()) :: {:ok, {any(), t()}} | {:error, error()}

realise/2 takes a forom and an optional opts, calls produce/2, and then gets (Keyword.get_values/2) the :forom key values.

The example are reqorked ones from produce/2

Examples

iex> {:ok, forom} = new(forom: :x_funs)
...> realise_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:ok, {forms, _}} = forom |> FOROMPROT.realise(realise_opts)
...> 3 = forms |> length
...> forms |> harnais_helper_test_forms!(binding: [x: 7])
{63, ["x = x + 1", "x = x * x", "x = x - 1"]}

iex> {:ok, %VEKILFORM{} = vekil} = [dict: [
...>    p1: :p2,
...>    p2: :p3,
...>    p3: "The End"
...> ]] |> VEKILFORM.new()
...> {:ok, forom} = new(forom: :p1)
...> realise_opts = [vekil: vekil]
...> {:ok, {values, _}} = forom |> FOROMPROT.realise(realise_opts)
...> values
["The End"]
Link to this function update(t, opts \\ []) View Source
update(t(), opts()) :: {:ok, t()} | {:error, error()}

update/2 implements Plymio.Vekil.Forom.update/2.

Examples

iex> {:ok, forom} = new(forom: :x_add_1)
...> {:ok, forom} = forom |> FOROMPROT.update(forom: :x_mul_x)
...> realise_opts = [vekil: vekil_helper_form_vekil_example1()]
...> {:ok, {form, _}} = forom |> FOROMPROT.realise(realise_opts)
...> form |> harnais_helper_test_forms!(binding: [x: 3])
{9, ["x = x * x"]}
Link to this function update!(t, opts \\ []) View Source
update!(t(), any()) :: t() | no_return()

update!/2 calls update/2 and, if the result is {:ok, instance} returns the instance.