# `Money.ExchangeRates`
[🔗](https://github.com/kipcole9/money/blob/v6.0.0-rc.0/lib/money/exchange_rates.ex#L1)

Implements a behaviour and functions to retrieve exchange rates
from an exchange rate service.

Configuration for the exchange rate service is defined
in a `Money.ExchangeRates.Config` struct.  A default
configuration is returned by `Money.ExchangeRates.default_config/0`.

The default configuration is:

    config :ex_money,
      auto_start_exchange_rate_service: false,
      exchange_rates_retrieve_every: 300_000,
      api_module: Money.ExchangeRates.OpenExchangeRates,
      callback_module: Money.ExchangeRates.Callback,
      preload_historic_rates: nil
      log_failure: :warn,
      log_info: :info,
      log_success: nil

These keys are are defined as follows:

* `:auto_start_exchange_rate_service` is a boolean that determines whether to
  automatically start the exchange rate retrieval service.
  The default it false.

* `:exchange_rates_retrieve_every` defines how often the exchange
  rates are retrieved in milliseconds. The default is 5 minutes
  (300,000 milliseconds).

* `:api_module` identifies the module that does the retrieval of
  exchange rates. This is any module that implements the
  `Money.ExchangeRates` behaviour. The default is
  `Money.ExchangeRates.OpenExchangeRates`.

* `:callback_module` defines a module that follows the
  Money.ExchangeRates.Callback behaviour whereby the function
  `rates_retrieved/2` is invoked after every successful retrieval
  of exchange rates. The default is `Money.ExchangeRates.Callback`.

* `:preload_historic_rates` defines a date or a date range,
  that will be requested when the exchange rate service starts up.
  The date or date range should be specified as either a `Date.t`
  or a `Date.Range.t` or a tuple of `{Date.t, Date.t}` representing
  the `from` and `to` dates for the rates to be retrieved. The
  default is `nil` meaning no historic rates are preloaded.

* `:log_failure` defines the log level at which api retrieval
  errors are logged. The default is `:warning`.

* `:log_success` defines the log level at which successful api
  retrieval notifications are logged. The default is `nil` which
  means no logging.

* `:log_info` defines the log level at which service startup messages
  are logged. The default is `:info`.

* `:retriever_options` is available for exchange rate retriever
  module developers as a place to add retriever-specific configuration
  information.  This information should be added in the `init/1`
  callback in the retriever module.  See `Money.ExchangeRates.OpenExchangeRates.init/1`
  for an example.

Keys can also be configured to retrieve values from environment
variables. This lookup is done at runtime to facilitate deployment
strategies. If the value of a configuration key is
`{:system, "some_string"}` then "some_string" is interpreted as
an environment variable name which is passed to System.get_env/2.

An example configuration might be:

    config :ex_money,
      auto_start_exchange_rate_service: {:system, "RATE_SERVICE"},
      exchange_rates_retrieve_every: {:system, "RETRIEVE_EVERY"},

## Open Exchange Rates

If you plan to use the provided Open Exchange Rates module
to retrieve exchange rates then you should also provide the additional
configuration key for `app_id`:

    config :ex_money,
      open_exchange_rates_app_id: "your_app_id"

or configure it via environment variable:

    config :ex_money,
      open_exchange_rates_app_id: {:system, "OPEN_EXCHANGE_RATES_APP_ID"}

The default exchange rate retrieval module is provided in
`Money.ExchangeRates.OpenExchangeRates` which can be used
as a example to implement your own retrieval module for
other services.

## Managing the configuration at runtime

During exchange rate service startup, the function `init/1` is called
on the configuration exchange rate retrieval module.  This module is
expected to return an updated configuration allowing a developer to
customise how the configuration is to be managed.  See the implementation
at `Money.ExchangeRates.OpenExchangeRates.init/1` for an example.

# `t`

```elixir
@type t() :: %{required(Localize.Currency.currency_code()) =&gt; Decimal.t()}
```

# `decode_rates`

```elixir
@callback decode_rates(any()) :: map()
```

Decode the body returned from the API request and
return a map of rates.  THe map of rates must have
an upcased atom key representing an ISO 4217 currency
code and the value must be a Decimal number.

# `get_historic_rates`

```elixir
@callback get_historic_rates(Date.t(), config :: Money.ExchangeRates.Config.t()) ::
  {:ok, map()} | {:error, binary()}
```

Invoked to return the historic exchange rates from the configured
exchange rate retrieval service.

* `config` is an `%Money.ExchangeRataes.Config{}` struct

Returns `{:ok, map_of_historic_rates}` or `{:error, reason}`

# `get_latest_rates`

```elixir
@callback get_latest_rates(config :: Money.ExchangeRates.Config.t()) ::
  {:ok, map()} | {:error, binary()}
```

Invoked to return the latest exchange rates from the configured
exchange rate retrieval service.

* `config` is an `%Money.ExchangeRataes.Config{}` struct

Returns `{:ok, map_of_rates}` or `{:error, reason}`

# `init`
*optional* 

```elixir
@callback init(config :: Money.ExchangeRates.Config.t()) :: Money.ExchangeRates.Config.t()
```

Given the default configuration, returns an updated configuration at runtime
during exchange rates service startup.

This callback is optional.  If the callback is not defined, the default
configuration returned by `Money.ExchangeRates.default_config/0` is used.

* `config` is the configuration returned by `Money.ExchangeRates.default_config/0`

The callback is expected to return a `%Money.ExchangeRates.Config.t()` struct
which may have been updated.  The configuration key `:retriever_options` is
available for any service-specific configuration.

# `config`

Returns the configuration for `ex_money` including the
configuration merged from the configured exchange rates
retriever module.

# `default_config`

Returns the default configuration for the exchange rates retriever.

# `historic_rates`

```elixir
@spec historic_rates(Date.t()) :: {:ok, map()} | {:error, {Exception.t(), binary()}}
```

Return historic exchange rates.

* `date` is a date returned by `Date.new/3` or any struct with the
  elements `:year`, `:month` and `:day`.

Returns:

* `{:ok, rates}` if exchange rates are successfully retrieved.  `rates` is a map of
  exchange rates.

* `{:error, reason}` if no exchange rates can be returned.

**Note;** all dates are expected to be in the Calendar.ISO calendar

This function looks up the historic exchange rates in a an ETS table
called `:exchange_rates`.  The actual retrieval of rates is requested
through `Money.ExchangeRates.Retriever.historic_rates/1`.

# `last_updated`

```elixir
@spec last_updated() :: {:ok, DateTime.t()} | {:error, {Exception.t(), binary()}}
```

Return the timestamp of the last successful retrieval of exchange rates or
`{:error, reason}` if no timestamp is known.

## Example

    Money.ExchangeRates.last_updated
    #> {:ok,
     %DateTime{calendar: Calendar.ISO, day: 20, hour: 12, microsecond: {731942, 6},
      minute: 36, month: 11, second: 6, std_offset: 0, time_zone: "Etc/UTC",
      utc_offset: 0, year: 2016, zone_abbr: "UTC"}}

# `latest_rates`

```elixir
@spec latest_rates() :: {:ok, map()} | {:error, {Exception.t(), binary()}}
```

Return the latest exchange rates.

Returns:

* `{:ok, rates}` if exchange rates are successfully retrieved.  `rates` is a map of
  exchange rates.

* `{:error, reason}` if no exchange rates can be returned.

This function looks up the latest exchange rates in a an ETS table
called `:exchange_rates`.  The actual retrieval of rates is requested
through `Money.ExchangeRates.Retriever.latest_rates/0`.

# `latest_rates_available?`

```elixir
@spec latest_rates_available?() :: boolean()
```

Returns `true` if the latest exchange rates are available
and false otherwise.

---

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