Skip to content

Commit

Permalink
Standardise @docs, some refactoring for robustness and code consisten…
Browse files Browse the repository at this point in the history
…cy, prepare for 1.0.0-rc.0 release
  • Loading branch information
kipcole9 committed Nov 19, 2017
1 parent ecc509e commit 1a21723
Show file tree
Hide file tree
Showing 17 changed files with 535 additions and 305 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ This is the changelog for Cldr v1.0.0-rc.0 released on November 19th, 2017. For

This version signals API stability and the first release candidate.

### Enhancements
## Breaking Changes

* The modules `Cldr.Currency`, `Cldr.Number.System` and `Cldr.Number.Format` now consistently returns an `{:ok, result}` tuple rather than just the `result`

## Enhancements

* Update to [ex_cldr](https://hex.pm/packages/ex_cldr) version 1.0.0-rc.0




8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ iex> h Cldr.Number.to_string

> The maximum number of integer digits, if present, specifies the exponent grouping. The most common use of this is to generate engineering notation, in which the exponent is a multiple of three, for example, "##0.###E0". The number 12345 is formatted using "##0.####E0" as "12.345E3".
`ex_cldr_numbers` does not currently support such functionality.
`ex_cldr_numbers` does not currently support this functionality.

## Installation

Expand All @@ -55,6 +55,10 @@ then retrieve `ex_cldr_numbers` from [hex](https://hex.pm/packages/ex_cldr_numbe
mix deps.get
mix deps.compile

## Configuration

`ex_cldr_numbers` uses the configuration set for the dependency `ex_cldr`. See the documentation for [ex_cldr](https://hexdocs.pm/ex_cldr)

## Using Cldr_Numbers

CLDR defines many different ways to format a number for different uses and defines a set of formats categorised by common pupose to make it easier to express the same intent across many different locales that represent many different territories, cultures, number systems and scripts.
Expand Down Expand Up @@ -135,7 +139,7 @@ iex> Cldr.Number.to_string 1234.31, format: :currency, currency: :CHF, cash: tru
{:ok, "CHF1,234.30"}
```

* `:accounting` which formats a positive number like `standard` but which usually wraps a negative number in `()`. The `:accounting` format also requires that the `:currency` option be specified.
* `:accounting` which formats a positive number like `:standard` but which usually wraps a negative number in `()`. The `:accounting` format also requires that the `:currency` option be specified.

```elixir
iex> Cldr.Number.to_string 1234, format: :accounting, currency: :THB
Expand Down
140 changes: 86 additions & 54 deletions lib/currency.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,18 @@ defmodule Cldr.Currency do
@doc """
Returns a `Currency` struct created from the arguments.
## Options
* `currency` is a custom currency code of a format defined in ISO4217
* `options` is a map of options representing the optional elements of the `%Currency{}` struct
## Returns
* `{:ok, Cldr.Currency.t}` or
* `{:error, {exception, message}}`
## Example
iex> Cldr.Currency.new(:XAA)
Expand All @@ -63,7 +71,7 @@ defmodule Cldr.Currency do
tender: false}}
iex> Cldr.Currency.new(:XBC)
{:error, "Currency :XBC is already defined"}
{:error, {Cldr.CurrencyAlreadyDefined, "Currency :XBC is already defined"}}
"""
@spec new(binary | atom, map | list) :: t | {:error, binary}
Expand All @@ -84,16 +92,18 @@ defmodule Cldr.Currency do
end

def new(currency, options) when is_map(options) do
with false <- known_currency?(currency),
{:ok, currency_code} <- make_currency_code(currency)
with \
{:error, _currency} <- Cldr.validate_currency(currency),
{:ok, currency_code} <- make_currency_code(currency)
do
options = @currency_defaults
|> Map.merge(options)
|> Map.merge(%{code: currency_code})
options =
@currency_defaults
|> Map.merge(options)
|> Map.merge(%{code: currency_code})

{:ok, struct(__MODULE__, options)}
else
true -> {:error, "Currency #{inspect currency} is already defined"}
{:ok, _} -> {:error, {Cldr.CurrencyAlreadyDefined, "Currency #{inspect currency} is already defined"}}
error -> error
end
end
Expand All @@ -102,30 +112,38 @@ defmodule Cldr.Currency do
Returns the appropriate currency display name for the `currency`, based
on the plural rules in effect for the `locale`.
## Options
* `number` is an integer, float or `Decimal`
* `currency` is any currency returned by `Cldr.Currency.known_currencies/0`
* `options` is a keyword list of options
* `:locale` is any locale returned by `Cldr.Locale.new!/1`. The
* `:locale` is any locale returned by `Cldr.Locale.new!/1`. The
default is `Cldr.get_current_locale/0`
## Returns
* `{:ok, plural_string}` or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Currency.pluralize 1, :USD
"US dollar"
{:ok, "US dollar"}
iex> Cldr.Currency.pluralize 3, :USD
"US dollars"
{:ok, "US dollars"}
iex> Cldr.Currency.pluralize 12, :USD, locale: Cldr.Locale.new!("zh")
"美元"
iex> Cldr.Currency.pluralize 12, :USD, locale: "zh"
{:ok, "美元"}
iex> Cldr.Currency.pluralize 12, :USD, locale: Cldr.Locale.new!("fr")
"dollars des États-Unis"
iex> Cldr.Currency.pluralize 12, :USD, locale: "fr"
{:ok, "dollars des États-Unis"}
iex> Cldr.Currency.pluralize 1, :USD, locale: Cldr.Locale.new!("fr")
"dollar des États-Unis"
iex> Cldr.Currency.pluralize 1, :USD, locale: "fr"
{:ok, "dollar des États-Unis"}
"""
def pluralize(number, currency, options \\ []) do
Expand All @@ -135,13 +153,11 @@ defmodule Cldr.Currency do

with \
{:ok, currency_code} <- Cldr.validate_currency(currency),
{:ok, locale} <- Cldr.validate_locale(locale)
{:ok, locale} <- Cldr.validate_locale(locale),
{:ok, currency_data} <- currency_for_code(currency_code, locale)
do
currency_data = for_code(currency_code, locale)
counts = Map.get(currency_data, :count)
Cldr.Number.Cardinal.pluralize(number, locale, counts)
else
{:error, reason} -> {:error, reason}
{:ok, Cldr.Number.Cardinal.pluralize(number, locale, counts)}
end
end

Expand All @@ -161,11 +177,17 @@ defmodule Cldr.Currency do
@doc """
Returns a boolean indicating if the supplied currency code is known.
## Options
* `currency_code` is a `binary` or `atom` representing an ISO4217
currency code
currency code
* `custom_currencies` is an optional list of custom currencies created by the
`Cldr.Currency.new/2` function
`Cldr.Currency.new/2` function
## Returns
* `true` or `false`
## Examples
Expand Down Expand Up @@ -197,18 +219,29 @@ defmodule Cldr.Currency do
custom currency code must start with an "X" followed by two alphabetic
characters.
Note that since this function creates atoms but to a maximum of
26 * 26 == 676 since the format permits 2 alphabetic characters only.
## Options
* `currency_code` is a `String.t` or and `atom` representing the new
currency code to be created
## Returns
* `{:ok, currency_code}` or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Currency.make_currency_code("xzz")
{:ok, :XZZ}
iex> Cldr.Currency.make_currency_code("aaa")
{:error,
"Invalid currency code \\"AAA\\". Currency codes must start with 'X' followed by 2 alphabetic characters only."}
{:error, {Cldr.CurrencyCodeInvalid,
"Invalid currency code \\"AAA\\". Currency codes must start with 'X' followed by 2 alphabetic characters only."}}
Note that since this function creates atoms, its important that this
function not be called with arbitrary user input since that risks
overflowing the atom table.
"""
@valid_currency_code Regex.compile!("^X[A-Z]{2}$")
@spec make_currency_code(binary | atom) :: {:ok, atom} | {:error, binary}
Expand All @@ -221,73 +254,72 @@ defmodule Cldr.Currency do
if String.match?(currency_code, @valid_currency_code) do
{:ok, String.to_atom(currency_code)}
else
{:error, "Invalid currency code #{inspect currency_code}. " <>
"Currency codes must start with 'X' followed by 2 alphabetic characters only."}
{:error, {Cldr.CurrencyCodeInvalid, "Invalid currency code #{inspect currency_code}. " <>
"Currency codes must start with 'X' followed by 2 alphabetic characters only."}}
end
end

@doc """
Returns the currency metadata for the requested currency code.
* `currency_code` is a `binary` or `atom` representation of an ISO 4217 currency code.
## Options
* `currency_code` is a `binary` or `atom` representation of an
ISO 4217 currency code.
## Examples
iex> Cldr.Currency.for_code("AUD")
%Cldr.Currency{cash_digits: 2, cash_rounding: 0, code: "AUD",
iex> Cldr.Currency.currency_for_code("AUD")
{:ok, %Cldr.Currency{cash_digits: 2, cash_rounding: 0, code: "AUD",
count: %{one: "Australian dollar", other: "Australian dollars"},
digits: 2, name: "Australian Dollar", narrow_symbol: "$",
rounding: 0, symbol: "A$", tender: true}
rounding: 0, symbol: "A$", tender: true}}
iex> Cldr.Currency.for_code("THB")
%Cldr.Currency{cash_digits: 2, cash_rounding: 0, code: "THB",
iex> Cldr.Currency.currency_for_code("THB")
{:ok, %Cldr.Currency{cash_digits: 2, cash_rounding: 0, code: "THB",
count: %{one: "Thai baht", other: "Thai baht"}, digits: 2,
name: "Thai Baht", narrow_symbol: "฿", rounding: 0, symbol: "THB",
tender: true}
tender: true}}
"""
@spec for_code(code, LanguageTag.t) :: %{}
def for_code(currency_code, locale \\ Cldr.get_current_locale()) do
@spec currency_for_code(code, LanguageTag.t) :: %{}
def currency_for_code(currency_code, locale \\ Cldr.get_current_locale()) do
with \
{:ok, code} <- Cldr.validate_currency(currency_code),
{:ok, locale} <- Cldr.validate_locale(locale)
{:ok, locale} <- Cldr.validate_locale(locale),
{:ok, currencies} <- currencies_for_locale(locale)
do
locale
|> for_locale
|> Map.get(code)
else
{:error, reason} ->
{:error, reason}
{:ok, Map.get(currencies, code)}
end
end

@doc """
Returns the currency metadata for a locale.
"""
@spec for_locale(Locale.name | LanguageTag.t) :: Map.t
def for_locale(locale \\ Cldr.get_current_locale())
@spec currencies_for_locale(Locale.name | LanguageTag.t) :: Map.t
def currencies_for_locale(locale \\ Cldr.get_current_locale())

for locale_name <- Cldr.Config.known_locale_names() do
currencies =
locale_name
|> Cldr.Config.get_locale
|> Map.get(:currencies)
|> Enum.map(fn {k, v} -> {k, struct(@struct, v)} end)

def for_locale(%LanguageTag{cldr_locale_name: unquote(locale_name)}) do
unquote(Macro.escape(currencies))
|> Enum.into(%{})

def currencies_for_locale(%LanguageTag{cldr_locale_name: unquote(locale_name)}) do
{:ok, unquote(Macro.escape(currencies))}
end
end

def for_locale(locale_name) when is_binary(locale_name) do
def currencies_for_locale(locale_name) when is_binary(locale_name) do
case Locale.canonical_language_tag(locale_name) do
{:ok, locale} -> for_locale(locale)
{:ok, locale} -> currencies_for_locale(locale)
{:error, reason} -> {:error, reason}
end
end

def for_locale(locale) do
def currencies_for_locale(locale) do
{:error, Locale.locale_error(locale)}
end
end
36 changes: 36 additions & 0 deletions lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@ defmodule Cldr.Rbnf.NoRuleForNumber do
"""
defexception [:message]

def exception(message) do
%__MODULE__{message: message}
end
end

defmodule Cldr.CurrencyAlreadyDefined do
@moduledoc """
Exception raised when an attempt is made to define a currency
that already exists.
"""
defexception [:message]

def exception(message) do
%__MODULE__{message: message}
end
end

defmodule Cldr.CurrencyCodeInvalid do
@moduledoc """
Exception raised when an attempt is made to define a currency
code that is invalid.
"""
defexception [:message]

def exception(message) do
%__MODULE__{message: message}
end
end

defmodule Cldr.NoNumberSymbols do
@moduledoc """
Exception raised when when there are no number
symbols for a locale and number system.
"""
defexception [:message]

def exception(message) do
%__MODULE__{message: message}
end
Expand Down
Loading

0 comments on commit 1a21723

Please sign in to comment.