Exop v1.1.1 Exop.Operation behaviour View Source

Provides macros for an operation’s contract definition and process/1 function.

Example

defmodule SomeOperation do
  use Exop.Operation

  parameter :param1, type: :integer, required: true
  parameter :param2, type: :string, length: %{max: 3}, format: ~r/foo/

  def process(params) do
    "This is the operation's result with one of the params = " <> params[:param1]
  end
end

Link to this section Summary

Functions

Authorizes an action with predefined policy (see policy macro docs). If authorization fails, any code after (below) auth check will be postponed (an error {:error, {:auth, _reason}} will be returned immediately)

Returns policy that was defined in an operation

Defines a fallback module that will be used for an operation’s non-ok-tuple (fail) result handling.

defmodule MultiplyByTenOperation do
  use Exop.Operation

Defines a parameter with name and opts in an operation contract. Options could include the parameter value checks and transformations (like coercion)

Defines a policy that will be used for authorizing the possibility of a user to invoke an operation.

defmodule ReadOperation do
  use Exop.Operation

Callbacks

Operation’s entry point. Takes defined contract as the single parameter. Contract itself is a Keyword.t list: [param_name: param_value]

Link to this section Functions

Link to this macro authorize(opts \\ nil) View Source (macro)

Authorizes an action with predefined policy (see policy macro docs). If authorization fails, any code after (below) auth check will be postponed (an error {:error, {:auth, _reason}} will be returned immediately)

Link to this macro current_policy() View Source (macro)

Returns policy that was defined in an operation.

Link to this macro fallback(fallback_module, opts \\ []) View Source (macro)

Defines a fallback module that will be used for an operation’s non-ok-tuple (fail) result handling.

defmodule MultiplyByTenOperation do
  use Exop.Operation

  fallback LoggerFallback

  parameter :a, type: :integer, required: true

  def process(%{a: a}), do: a * 10
end

A fallback module itself might be:

defmodule LoggerFallback do
  use Exop.Fallback
  require Logger

  def process(operation_module, params_passed_to_the_operation, operation_error_result) do
    Logger.error("Oops")
  end
end

If return: true option is provided then failed operation’s run/1 will return the fallback’s process/3 result.

Link to this macro parameter(name, opts \\ []) View Source (macro)

Defines a parameter with name and opts in an operation contract. Options could include the parameter value checks and transformations (like coercion).

Example

parameter :some_param, type: :map, required: true

Available checks are:

type

Checks whether a parameter’s value is of declared type.

parameter :some_param, type: :map

required

Checks the presence of a parameter in passed params collection.

parameter :some_param, required: true

default

Checks if the parameter is missed and assigns default value to it if so.

parameter :some_param, default: "default value"

numericality

Checks whether a parameter’s value is a number and passes constraints (if constraints were defined).

parameter :some_param, numericality: %{equal_to: 10, greater_than: 0,
                                       greater_than_or_equal_to: 10,
                                       less_than: 20,
                                       less_than_or_equal_to: 10}

in

Checks whether a parameter’s value is within a given list.

parameter :some_param, in: ~w(a b c)

not_in

Checks whether a parameter’s value is not within a given list.

parameter :some_param, not_in: ~w(a b c)

format

Checks wether parameter’s value matches given regex.

parameter :some_param, format: ~r/foo/

length

Checks the length of a parameter’s value.

parameter :some_param, length: %{min: 5, max: 10, is: 7, in: 5..8}

inner

Checks the inner of either Map or Keyword parameter.

parameter :some_param, type: :map, inner: %{
  a: [type: :integer, required: true],
  b: [type: :string, length: %{min: 1, max: 6}]
}

struct

Checks whether the given parameter is expected structure.

parameter :some_param, struct: %SomeStruct{}

list_item

Checks whether each of list items conforms defined checks. An item’s checks could be any that Exop offers:

parameter :list_param, list_item: %{type: :string, length: %{min: 7}}

func

Checks whether an item is valid over custom validation function.

parameter :some_param, func: &__MODULE__.your_validation/2

def your_validation(_params, param), do: !is_nil(param)

Coercion

It is possible to coerce a parameter before the contract validation, all validation checks will be invoked on coerced parameter value. Since coercion changes a parameter before any validation has been invoked, default values are resolved (with :default option) before the coercion. The flow looks like: Resolve param default value -> Coerce -> Validate coerced

parameter :some_param, default: 1, numericality: %{greater_than: 0}, coerce_with: &__MODULE__.coerce/1

def coerce(x), do: x * 2

For more information and examples check out general Exop docs.

Link to this macro policy(policy_module, action_name) View Source (macro)

Defines a policy that will be used for authorizing the possibility of a user to invoke an operation.

defmodule ReadOperation do
  use Exop.Operation

  policy MonthlyReportPolicy, :can_read?

  parameter :user, required: true, struct: %User{}

  def process(params) do
    authorize(params.user)

    # make some reading...
  end
end

A policy itself might be:

defmodule MonthlyReportPolicy do
  # not only Keyword or Map as an argument since 1.1.1
  def can_read?(%User{role: "manager"}), do: true
  def can_read?(_opts), do: false

  def can_write?(%User{role: "manager"}), do: true
  def can_write?(_opts), do: false
end

Link to this section Callbacks

Link to this callback process(map) View Source
process(map()) ::
  {:ok, any()}
  | Exop.Validation.validation_error()
  | {:interrupt, any()}
  | :ok
  | no_return()

Operation’s entry point. Takes defined contract as the single parameter. Contract itself is a Keyword.t list: [param_name: param_value]