plymio_codi v0.3.1 Plymio.Codi.Pattern.Proxy View Source
The proxy patterns manage the vekil.
See Plymio.Codi for an overview and documentation terms.
Pattern: proxy_fetch
The proxy_fetch pattern fetches the forom of one or more proxies in the vekil.
proxy_fetch maps directly to a Plymio.Vekil.proxy_fetch/2 call on
the vekil; all of the proxies must exist else an error result will be
returned.
Valid keys in the cpo are:
| Key | Aliases |
|---|---|
:proxy_name | :proxy_names, :proxy, :proxies |
Examples
A simple case fetching one proxy:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy: :add_1,
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end"]
If the proxy is not found, or there is no vekil, an error result will be returned.
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:error, error} = [
...> vekil: vekil_dict,
...> proxy: :add_11,
...> ] |> produce_codi
...> error |> Exception.message
"proxy invalid, got: :add_11"
iex> {:error, error} = [
...> proxy: :add_11,
...> ] |> produce_codi
...> error |> Exception.message
"vekil missing"
iex> vekil_dict = %{
...> # a map is not a valid form
...> add_1: %{a: 1},
...> }
...> {:error, error} = [
...> vekil: vekil_dict,
...> proxy: :add_1,
...> ] |> produce_codi
...> error |> Exception.message
"form invalid, got: %{a: 1}"
Multiple proxies can be given in a list:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> sqr_x: quote(do: def(sqr_x(x), do: x * x)),
...> sub_1: quote(do: def(sub_1(x), do: x - 1)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxies: [:add_1, :sqr_x, :sub_1]
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end",
"def(sqr_x(x)) do\n x * x\n end",
"def(sub_1(x)) do\n x - 1\n end"]
A proxy can be a list of other proxies:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> sqr_x: quote(do: def(sqr_x(x), do: x * x)),
...> sub_1: quote(do: def(sub_1(x), do: x - 1)),
...> all: [:add_1, :sqr_x, :sub_1],
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy: :all
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end",
"def(sqr_x(x)) do\n x * x\n end",
"def(sub_1(x)) do\n x - 1\n end"]
When the proxy is a list of proxies, infinite loops are caught:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> sqr_x: quote(do: def(sqr_x(x), do: x * x)),
...> sub_1: quote(do: def(sub_1(x), do: x - 1)),
...> all_loop: [:add_1, :sqr_x, :sub_1, :all_loop],
...> }
...> {:error, error} = [
...> vekil: vekil_dict,
...> proxy: :all_loop
...> ] |> produce_codi
...> error |> Exception.message
"proxy seen before, got: :all_loop"
It is more efficient to pre-create (ideally at compile time) the vekil:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> sqr_x: quote(do: def(sqr_x(x), do: x * x)),
...> sub_1: quote(do: def(sub_1(x), do: x - 1)),
...> all: [:add_1, :sqr_x, :sub_1],
...> }
...> {:ok, %Plymio.Vekil.Form{} = vekil} = [dict: vekil_dict] |>
...> Plymio.Vekil.Form.new
...> {:ok, {forms, _}} = [
...> vekil: vekil,
...> proxy: :all
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end",
"def(sqr_x(x)) do\n x * x\n end",
"def(sub_1(x)) do\n x - 1\n end"]
In this example a :forms_edit is given renaming all the x vars to a vars, changing “1” to “42” and
renaming the add_ function to incr_1.
renaming the vars in this example doesn’t change the logic
iex> postwalk_fun = fn
...> 1 -> 42
...> x -> x
...> end
...> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> sqr_x: quote(do: def(sqr_x(x), do: x * x)),
...> sub_1: quote(do: def(sub_1(x), do: x - 1)),
...> all: [:add_1, :sqr_x, :sub_1],
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy: [proxy: :all, forms_edit: [
...> postwalk: postwalk_fun,
...> rename_vars: [x: :a],
...> rename_funs: [add_1: :incr_1]]]
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(incr_1(a)) do\n a + 42\n end",
"def(sqr_x(a)) do\n a * a\n end",
"def(sub_1(a)) do\n a - 42\n end"]
Pattern: proxy_put
The proxy_put pattern puts one or more proxies and their forom, into the vekil.
proxy_put maps directly to a Plymio.Vekil.proxy_put/2 call on
the vekil.
If the vekil does not exist, a new Plymio.Vekil.Form will be created.
Valid keys in the cpo are:
| Key | Aliases |
|---|---|
:proxy_args |
Examples
A simple case puting one proxy and then fetching it:
iex> {:ok, {forms, _}} = [
...> proxy_put: [add_1: quote(do: def(add_1(x), do: x + 1))],
...> proxy_fetch: :add_1
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end"]
In this example the same proxy (:add_1) is fetched twice but the
proxy is updated between the two fetches.
iex> {:ok, {forms, _}} = [
...> proxy_put: [add_1: quote(do: x = x + 1)],
...> proxy_fetch: :add_1,
...> proxy_put: [add_1: quote(do: x = x + 40)],
...> proxy_fetch: :add_1
...> ] |> produce_codi
...> forms |> harnais_helper_test_forms!(binding: [x: 1])
{42, ["x = x + 1", "x = x + 40"]}
Here an existing proxy (:sqr_x) is overriden. Note the
“composite” proxy :all is resolved as late as possible and finds the updated :sqr_x:
iex> vekil_dict = %{
...> add_1: quote(do: x = x + 1),
...> sqr_x: quote(do: x = x * x),
...> sub_1: quote(do: x = x - 1),
...> all: [:add_1, :sqr_x, :sub_1],
...> }
...> {:ok, %Plymio.Vekil.Form{} = vekil} = [dict: vekil_dict] |>
...> Plymio.Vekil.Form.new
...> {:ok, {forms, _}} = [
...> vekil: vekil,
...> # change the :sqr_x proxy to cube instead
...> proxy_put: [sqr_x: quote(do: x = x * x * x)],
...> proxy: :all
...> ] |> produce_codi
...> forms |> harnais_helper_test_forms!(binding: [x: 7])
{511, ["x = x + 1", "x = x * x * x", "x = x - 1"]}
Pattern: proxy_delete
The proxy_delete pattern delete one or more proxies from the
vekil. It can be used to change the behaviour of a subsequent proxy_get to use the default.
No vekil and / or any unknown proxy are ridden out without causing an error.
proxy_delete maps directly to a Plymio.Vekil.proxy_delete/2 call on
the vekil.
Valid keys in the cpo are:
| Key | Aliases |
|---|---|
:proxy_name | :proxy_names, :proxy, :proxies |
Examples
A simple case of deleting a proxy and then fetching it:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:error, error} = [
...> vekil: vekil_dict,
...> proxy_delete: :add_1,
...> proxy_fetch: :add_1
...> ] |> produce_codi
...> error |> Exception.message
"proxy invalid, got: :add_1"
No vekil and / or unknown proxies are ridden out without causing an error:
iex> {:ok, {[], codi}} = [
...> proxy_delete: :add_1,
...> proxy_delete: :does_not_matter
...> ] |> produce_codi
...> match?(%Plymio.Codi{}, codi)
true
Pattern: proxy_get
The proxy_get pattern gets one or more proxies from the
vekil but with an optional default to be returned (as a forom) if the proxy is not found.
proxy_get maps directly to a Plymio.Vekil.proxy_get/2 or Plymio.Vekil.proxy_get/3 call on
Valid keys in the cpo are:
| Key | Aliases |
|---|---|
:proxy_name | :proxy_names, :proxy, :proxies |
:default |
Examples
Here the proxy exists in the vekil:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy_get: :add_1,
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_1(x)) do\n x + 1\n end"]
If the proxy does not exists, and there is no default, no forms are returned:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy_get: :add_2,
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
[]
Here a default is provided. Note the default is automatically
normalised to a forom and then realised.
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy_get: [proxy: :add_2, default: quote(do: def(add_42(x), do: x + 42))]
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_42(x)) do\n x + 42\n end"]
The default can be another proxy in the vekil:
iex> vekil_dict = %{
...> add_1: quote(do: def(add_1(x), do: x + 1)),
...> add_42: quote(do: def(add_42(x), do: x + 42)),
...> }
...> {:ok, {forms, _}} = [
...> vekil: vekil_dict,
...> proxy_get: [proxy_name: :add_2, default: :add_42]
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_42(x)) do\n x + 42\n end"]
If there is no vekil and no default, no forms are returned:
iex> {:ok, {forms, _}} = [
...> proxy_get: :add_2,
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
[]
No vekil but a default works as expected:
iex> {:ok, {forms, _}} = [
...> proxy_get: [proxy: :add_2, default: quote(do: def(add_42(x), do: x + 42))]
...> ] |> produce_codi
...> forms |> harnais_helper_show_forms!
["def(add_42(x)) do\n x + 42\n end"]
As many defaults as proxies are returned:
iex> {:ok, {forms, _}} = [
...> proxy_get: [
...> proxy: [:x_sub_1, :a_mul_x, :not_a_proxy, :some_other_thing],
...> default: quote(do: x = x + 1)]
...> ] |> produce_codi
...> forms |> harnais_helper_test_forms!(binding: [x: 1])
{5, ["x = x + 1", "x = x + 1", "x = x + 1", "x = x + 1"]}