# `Rexsilient.Decorator`
[🔗](https://github.com/fabiobarkoski/rexsilient/blob/v1.0.0/lib/rexsilient/decorator.ex#L1)

Runs a function through a circuit breaker, a retry, a fallback or a policy
by annotating it.

Instead of wrapping every call in `Rexsilient.CircuitBreaker.run/3`, a
function can be marked with `@circuit_breaker` so that all its calls go
through the circuit breaker:

    defmodule MyApp.Payments do
      use Rexsilient.Decorator

      @circuit_breaker :payments
      def charge(order) do
        PaymentsClient.charge(order)
      end
    end

`MyApp.Payments.charge(order)` now behaves like:

    Rexsilient.CircuitBreaker.run(:payments, fn -> PaymentsClient.charge(order) end)

The circuit breaker must be declared with `Rexsilient` and registered as
usual; see `Rexsilient.CircuitBreaker` for its results and how failures are
counted.

`@retry` and `@fallback` work the same way, with `Rexsilient.Retry.run/2`
and `Rexsilient.Fallback.run/3`. The fallback function is called with the
decorated function's arguments.

## Options

To pass options to `Rexsilient.CircuitBreaker.run/3`, give a keyword list
with the circuit breaker's name under `:name`:

    @circuit_breaker name: :prices, on_open: {:error, :unavailable}
    def fetch_price(sku) do
      PricesClient.fetch(sku)
    end

`@retry`, `@fallback` and `@policy` take only a name.

## Stacking

`@fallback`, `@retry` and `@circuit_breaker` can be used on the same
function. They always nest in the same order, whatever the order they are
written in:

    @fallback :prices
    @retry :prices
    @circuit_breaker :prices
    def fetch_price(sku) do
      PricesClient.fetch(sku)
    end

behaves like:

    Rexsilient.Fallback.run(:prices, fn sku ->
      Rexsilient.Retry.run(:prices, fn ->
        Rexsilient.CircuitBreaker.run(:prices, fn -> PricesClient.fetch(sku) end)
      end)
    end, sku)

Every attempt goes through the breaker and is counted. The retry stops as
soon as the breaker is open, and the fallback replaces whatever outcome is
left: the last attempt's failure or the breaker's rejection.

When the same stack is used on several functions, declare it once as a
policy with `Rexsilient.policy/2` and use `@policy`:

    # in the module that uses Rexsilient
    policy :prices do
      fallback :prices
      retry :prices
      circuit_breaker :prices
    end

    # in the decorated module
    @policy :prices
    def fetch_price(sku) do
      PricesClient.fetch(sku)
    end

A policy nests in the same order. `@policy` cannot be combined with the
other decorators on the same function.

For another order, or the same pattern twice, decorate one function per
layer, each calling the next:

    # The breaker counts a whole retry sequence as one call.
    @circuit_breaker :prices
    def fetch_price(sku), do: fetch_price_with_retry(sku)

    @doc false
    @retry :prices
    def fetch_price_with_retry(sku), do: PricesClient.fetch(sku)

The inner function must be public, so calling it directly skips the outer
layer.

## Rules

  * The attribute goes right before the function's first clause and applies
    to every clause of that name and arity.

  * Only public functions (`def`) can be decorated. Using it on `defp` or a
    macro fails compilation, and so does an attribute that is not followed
    by a function.

  * Recursive calls from inside the function go through the decorators too.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
