Skip to content

Instantly share code, notes, and snippets.

@braynm
Last active January 8, 2024 02:31
Show Gist options
  • Save braynm/1b29e3b6904efe224efbd8ebc904df16 to your computer and use it in GitHub Desktop.
Save braynm/1b29e3b6904efe224efbd8ebc904df16 to your computer and use it in GitHub Desktop.
## Example unit test for `AmountFormatter` module
defmodule GroceryBudget.AmountFormatterTest do
use ExUnit.Case
alias GroceryBudget.Utils.AmountFormatter
describe "AmountFormatter" do
@tag :amount_formatter
test "to_db/1 should multiply by 100" do
assert AmountFormatter.to_db(500) == 50000
end
@tag :amount_formatter
test "to_db/1 should convert the decimal to whole number" do
assert AmountFormatter.to_db(500.25) == 50025
end
@tag :amount_formatter
test "to_db/1 should not convert" do
assert AmountFormatter.to_db("hellowrold") == {:error, :invalid_number}
end
@tag :amount_formatter
test "from_db/1 should format amount" do
assert AmountFormatter.from_db(5000) == 50
end
end
end
defmodule GroceryBudget.Utils.AmountFormatter do
@moduledoc """
We do not store decimal to amount fields in database. In order to
retain the decimal place when fetching, We multiply or divide it by 100. This way,
we only deal with whole number. If we want to fetch the original value
we have to use this `AmountFormatter` module.
Example Usage:
iex> AmountFormatter.to_db(123.50)
iex> 12350
iex> AmountFormatter.from_db(12350)
iex> 123.50
"""
def to_db(amount) when is_binary(amount) do
if Regex.match?(~r/^\d+$/, amount) do
amount
|> Decimal.new()
|> Decimal.mult(100)
|> Decimal.to_integer()
else
{:error, :invalid_number}
end
end
def to_db(amount) when is_float(amount) do
trunc(amount * 100)
end
def to_db(amount) when is_integer(amount) do
trunc(amount * 100)
end
def from_db(amount) when is_float(amount) do
trunc(amount / 100)
end
def from_db(amount) when is_integer(amount) do
amount
|> Decimal.new()
|> Decimal.div(100)
|> Decimal.normalize()
|> Decimal.round()
|> Decimal.to_integer()
end
def from_db(amount) when is_binary(amount) do
trunc(String.to_float(amount) / 100)
end
# Sometimes `Ecto` returns `Decimal` struct
# so we need to support it.
def from_db(%Decimal{} = amount) do
amount =
amount
|> Decimal.div(100)
|> Decimal.to_string()
converted_amount =
case Regex.match?(~r/\d\.\d/, amount) do
true -> String.to_float(amount)
false -> String.to_integer(amount) * 1.0
end
trunc(converted_amount)
# |> case do
# "0" ->
# 0
# amount ->
# String.to_float(amount)
# end
end
@doc """
Allow users to format amount to string
"""
def from_db(amount, :string) do
amount
|> Decimal.new()
|> Decimal.div(100)
|> Decimal.normalize()
|> Decimal.to_string(:normal)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment