Packages
Convert Money Amounts between currencies.
Retired package: Deprecated - This package is no longer maintained
Current section
Files
Jump to
Current section
Files
lib/currency_conversion.ex
defmodule CurrencyConversion do
@moduledoc """
Module to Convert Currencies.
`CurrencyConversion` is a wrapper around the currency conversion. We can define an
implementation as follows:
defmodule MyApp.CurrencyConversion do
use CurrencyConversion, otp_app: :my_app
end
Where the configuration for the Converter must be in your application environment,
usually defined in your `config/config.exs`:
config :my_app, MyApp.CurrencyConversion,
source: MyApp.CurrencyConversion.Source.CustomSource
If your application was generated with a supervisor (by passing `--sup` to `mix new`)
you will have a `lib/my_app/application.ex` file containing the application start
callback that defines and starts your supervisor. You just need to edit the `start/2`
function to start the converter as a supervisor on your application's supervisor:
def start(_type, _args) do
children = [
{MyApp.CurrencyConversion, []}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
"""
@convert_doc """
Convert from currency A to B.
### Example
iex> convert(Money.new(7_00, :CHF), :USD)
%Money{amount: 7_03, currency: :USD}
"""
@doc @convert_doc
@callback convert(amount :: Money.t(), to_currency :: atom) :: Money.t()
@get_currencies_doc """
Get all currencies
### Examples
iex> get_currencies()
[:EUR, :CHF, :USD, ...]
"""
@doc @get_currencies_doc
@callback get_currencies :: [atom]
@get_rates_doc """
Get current exchange rates
### Examples
iex> get_rates()
%CurrencyConversion.Rates{
base: :EUR,
rates: %{
AUD: 1.4205,
BGN: 1.9558,
...
}
}
"""
@doc @get_rates_doc
@callback get_rates :: CurrencyConversion.Rates.t()
@refresh_rates_doc """
Refresh exchange rates
### Examples
iex> refresh_rates()
:ok
"""
@doc @refresh_rates_doc
@callback refresh_rates :: :ok | {:error, term}
defmacro __using__(opts) do
otp_app = Keyword.fetch!(opts, :otp_app)
quote do
@moduledoc """
Module to Convert Currencies for `#{unquote(otp_app)}`.
"""
@behaviour unquote(__MODULE__)
@update_worker Module.concat(__MODULE__, UpdateWorker)
@otp_app unquote(otp_app)
alias CurrencyConversion.UpdateWorker
use Supervisor
@doc false
def start_link(_opts) do
Supervisor.start_link(__MODULE__, nil, name: __MODULE__)
end
@doc false
def child_spec(init_arg) do
super(init_arg)
end
@impl Supervisor
def init(_init_arg) do
config = Application.get_env(@otp_app, __MODULE__, [])
Supervisor.init([{UpdateWorker, config ++ [name: @update_worker]}], strategy: :one_for_one)
end
@doc unquote(@convert_doc)
@impl unquote(__MODULE__)
def convert(amount, to_currency) do
unquote(__MODULE__).convert(amount, to_currency, UpdateWorker.get_rates(@update_worker))
end
@doc unquote(@get_currencies_doc)
@impl unquote(__MODULE__)
def get_currencies do
unquote(__MODULE__).get_currencies(UpdateWorker.get_rates(@update_worker))
end
@doc unquote(@get_rates_doc)
@impl unquote(__MODULE__)
def get_rates do
UpdateWorker.get_rates(@update_worker)
end
@doc unquote(@refresh_rates_doc)
@impl unquote(__MODULE__)
def refresh_rates do
UpdateWorker.refresh_rates(@update_worker)
end
end
end
alias CurrencyConversion.Rates
@doc false
@spec convert(amount :: Money.t(), to_currency :: atom, rates :: Rates.t()) :: Money.t()
def convert(%Money{amount: 0}, to_currency, _), do: Money.new(0, to_currency)
def convert(amount = %Money{currency: currency}, currency, _), do: amount
def convert(%Money{amount: amount, currency: currency}, to_currency, %Rates{
base: currency,
rates: rates
}) do
Money.new(round(amount * Map.fetch!(rates, to_currency)), to_currency)
end
def convert(%Money{amount: amount, currency: currency}, to_currency, %Rates{
base: to_currency,
rates: rates
}) do
Money.new(round(amount / Map.fetch!(rates, currency)), to_currency)
end
def convert(amount, to_currency, rates) do
convert(convert(amount, rates.base, rates), to_currency, rates)
end
@doc false
@spec get_currencies(rates :: Rates.t()) :: [atom]
def get_currencies(%Rates{base: base, rates: rates}), do: [base | Map.keys(rates)]
end