View Source Inlets, unwrapping and context
Inlets
Building a request to be passed to a pipeline can be verbose, especially when
you just need to pass in a couple of arguments in the command field. Plumbery
provides a helper that makes things easier, called inlet. An inlet is a
generated function that stuffs arguments into a request's command
struct/map
and calls a pipeline.
Let's see how inlets work. If you read through other guides, you may have already noticed that we use examples that do not look like something one would realistically write but hopefully explain concepts well. I will not break this pattern and create a pipeline that does something that does not require a pipeline.
defmodule Example do
use Plumbery
import Plumbery.Request
defp mul_xy(%{command: %{x: x, y: y}} = req), do: success(req, x * y)
pipeline :multiply do
pipe :mul_xy
end
end
%{result: {:ok, 999}} =
%Plumbery.Request{command: %{x: 9, y: 111}}
|> Example.multiply()
Looks like that's too much effort to just pass in a couple of numbers. Let's create and use an inlet.
defmodule Example do
use Plumbery
import Plumbery.Request
defp mul_xy(%{command: %{x: x, y: y}} = req), do: success(req, x * y)
pipeline :multiply_pipeline do
private true
pipe :mul_xy
inlet multiply(x, y \\ 2) when is_number(x) and is_number(y)
# This requires x and y to be the same
inlet multiply_x_x(x, y = x) when is_number(x)
end
end
%{result: {:ok, 999}} = Example.multiply(9, 111)
%{result: {:ok, 18}} = Example.multiply(9)
%{result: {:ok, 81}} = Example.multiply_x_x(9, 9)
# raises FunctionClauseError
%{result: {:ok, 90}} = Example.multiply_x_x(9, 10)
Here we can see some interesting things about an inlet:
- you can define the exact signature of the inlet function, including patterns, default values and guard clauses
- the arguments are added to request's command, under the same names as the arguments
Info
In some cases when using named arguments and patterns it is not possible to distinguish variable name from the pattern (like in
y = x
above). In that case the lefthand side is used as variable name and the key to put inside the command map.
Unwrapping
Passing the whole request structure from pipe to pipe is necessary since we need
to keep track of the pipeline state, but for a pipeline that is not meant to be
included in other pipelines all we are interested in is the execution result,
not command, assigns and other stuff inside the request struct. We can make a
pipeline to return only the result using the unwrap
option.
defmodule Example do
use Plumbery
import Plumbery.Request
defp mul_xy(%{command: %{x: x, y: y}} = req), do: success(req, x * y)
pipeline :multiply_pipeline do
private true
unwrap true
pipe :mul_xy
inlet multiply(x, y \\ 2) when is_number(x) and is_number(y)
# This requires x and y to be the same
inlet multiply_x_x(x, y = x) when is_number(x)
end
end
{:ok, 999} = Example.multiply(9, 111)
{:ok, 18} = Example.multiply(9)
{:ok, 81} = Example.multiply_x_x(9, 9)
# raises FunctionClauseError
{:ok, 90} = Example.multiply_x_x(9, 10)
Inlets can be also defined outside of a pipeline, in that case you need to specify the pipeline to execute:
defmodule Example do
use Plumbery
import Plumbery.Request
defp mul_xy(%{command: %{x: x, y: y}} = req), do: success(req, x * y)
pipeline :multiply_pipeline do
private true
unwrap true
pipe :mul_xy
end
inlet multiply(x, y \\ 2) when is_number(x) and is_number(y) do
pipeline :multiply_pipeline
end
# This requires x and y to be the same
inlet multiply_x_x(x, y = x) when is_number(x), pipeline: :multiply_pipeline
end
{:ok, 999} = Example.multiply(9, 111)
{:ok, 18} = Example.multiply(9)
{:ok, 81} = Example.multiply_x_x(9, 9)
# raises FunctionClauseError
{:ok, 90} = Example.multiply_x_x(9, 10)
Context
Oftentimes when building APIs it is common that most entrypoints require a
context argument with current user and other information. For that purpose
Plumbery.Request
has context
field, but other than that Plumbery does not
provide support for context. The only exception is the ability to add context
as the last argument for functions generated by inlets. If you specify
use_context
option, the context
will be added as the last argument of the
generated function.
defmodule Example do
use Plumbery
import Plumbery.Request
defp act(req), do: success(req, req.context.actor)
pipeline :example_pipeline do
pipe :act
unwrap true
inlet entry(), use_context: true
end
end
{:ok, :system} = Example.entry(%{actor: :system})