Packages
ex_money
6.1.1
6.1.1
6.1.0
6.0.0
6.0.0-rc.0
5.24.2
5.24.1
5.24.0
retired
5.23.0
5.22.0
5.21.0
5.20.0
5.19.2
5.19.1
5.19.0
5.18.0
5.17.2
5.17.1
5.17.0
5.16.1
5.16.0
5.15.4
5.15.3
5.15.2
5.15.1
5.15.0
5.14.1
5.13.0
5.12.4
5.12.3
5.12.2
5.12.1
5.12.0
5.11.0
5.10.0
5.9.0
5.8.0
5.7.4
5.7.3
5.7.2
5.7.1
5.7.0
5.6.0
5.5.5
5.5.4
5.5.3
5.5.2
5.5.1
5.5.0
5.4.1
5.4.0
5.4.0-rc.0
5.3.2
5.3.1
5.3.0
5.2.1
5.2.0
5.1.0
5.0.2
5.0.1
5.0.0
retired
4.4.3
4.4.2
retired
4.4.1
4.4.0
4.3.0
4.2.2
4.2.1
4.2.0
4.1.0
4.0.0
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.1
3.3.0
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.0
3.0.0
2.12.2
2.12.1
retired
2.12.0
retired
2.11.0
2.10.0
2.9.1
2.9.0
2.8.0
2.7.3
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.0
2.4.0
2.3.0
2.2.2
2.2.1
2.2.0
2.1.0
2.1.0-rc.1
retired
2.1.0-rc.0
retired
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.3
1.1.2
1.1.1
retired
1.1.0
1.0.0
1.0.0-rc.0
retired
0.9.0
retired
0.8.5
retired
0.8.4
retired
0.8.3
retired
0.8.2
retired
0.8.1
retired
0.8.0
retired
0.7.0
retired
0.6.2
retired
0.6.1
retired
0.6.0
retired
0.5.3
retired
0.5.2
retired
0.5.1
retired
0.5.0
retired
0.4.3
retired
0.4.2
retired
0.4.1
retired
0.4.0
retired
0.3.0
retired
0.2.0
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
0.0.16
retired
0.0.15
retired
0.0.14
retired
0.0.13
retired
0.0.12
retired
0.0.11
retired
0.0.10
retired
0.0.9
retired
0.0.7
retired
0.0.6
retired
0.0.5
retired
0.0.4
retired
0.0.3
retired
0.0.2
retired
Money functions for operations on and localization of a money data type with support for ISO 4217 currencies and ISO 24165 digial tokens (crypto currencies).
Current section
Files
Jump to
Current section
Files
lib/money/currency/store.ex
defmodule Money.Currency.Store do
@moduledoc """
Persistent-term-backed store for custom currencies.
Custom currencies are currencies defined with `Money.Currency.new/2`,
either at runtime or declaratively through the `:custom_currencies`
application configuration. They are stored in `:persistent_term` for
fast concurrent read access.
This module is started as part of the Money application supervision
tree. Currencies declared in `:custom_currencies` are registered each
time the store starts, so they are restored automatically after a
supervisor restart; currencies added at runtime with
`Money.Currency.new/2` are not.
"""
use GenServer
require Logger
@persistent_key {:ex_money, :custom_currencies}
# ── Client API ────────────────────────────────────────────────
@doc """
Starts the custom currency store.
"""
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end
@doc """
Returns all custom currencies as a map of `%{currency_code => currency_struct}`.
### Returns
* A map of custom currencies. Returns an empty map if no custom
currencies have been defined.
"""
@spec all() :: %{atom() => Localize.Currency.t()}
def all do
:persistent_term.get(@persistent_key, %{})
end
@doc """
Returns a list of all custom currency codes.
### Returns
* A list of atom currency codes.
"""
@spec codes() :: [atom()]
def codes do
Map.keys(all())
end
@doc """
Stores a custom currency.
### Arguments
* `currency` is a `t:Localize.Currency.t/0` struct.
### Returns
* `{:ok, currency}` on success.
* `{:error, Money.CurrencyNotSavedError.t()}` if the store
is not running.
"""
@spec put(Localize.Currency.t()) ::
{:ok, Localize.Currency.t()} | {:error, Exception.t()}
def put(%Localize.Currency{} = currency) do
GenServer.call(__MODULE__, {:put, currency})
catch
:exit, _reason ->
{:error, Money.CurrencyNotSavedError.exception(currency: currency.code)}
end
@doc """
Returns the custom currency for the given code, or `nil`.
### Arguments
* `code` is a currency code atom.
### Returns
* A `t:Localize.Currency.t/0` struct or `nil`.
"""
@spec get(atom()) :: Localize.Currency.t() | nil
def get(code) when is_atom(code) do
Map.get(all(), code)
end
# ── Server callbacks ──────────────────────────────────────────
@impl true
def init(_options) do
# Currencies declared in the `:custom_currencies` configuration are
# materialised here so that they are (re-)registered every time the store
# starts, including after a supervisor restart. Runtime currencies added
# later with `Money.Currency.new/2` do not survive a restart.
currencies = configured_currencies()
:persistent_term.put(@persistent_key, currencies)
{:ok, currencies}
end
defp configured_currencies do
Money.Currency.configured_currency_specs()
|> Enum.reduce(%{}, &put_configured_currency/2)
end
defp put_configured_currency({code, options}, currencies) do
case Money.Currency.build(code, options) do
{:ok, currency} ->
Map.put(currencies, currency.code, currency)
{:error, exception} ->
Logger.warning(
"Failed to register custom currency #{inspect(code)}: " <>
Exception.message(exception)
)
currencies
end
end
defp put_configured_currency(other, currencies) do
Logger.warning(
"Ignoring invalid :custom_currencies entry #{inspect(other)}; " <>
"expected a {code, options} tuple"
)
currencies
end
@impl true
def handle_call({:put, %Localize.Currency{code: code} = currency}, _from, state) do
currencies = :persistent_term.get(@persistent_key, %{})
updated = Map.put(currencies, code, currency)
:persistent_term.put(@persistent_key, updated)
{:reply, {:ok, currency}, state}
end
end