Skip to content

Commit

Permalink
Merge pull request #52 from danielberkompas/43-decimal-type
Browse files Browse the repository at this point in the history
✨ Add `Decimal` type
  • Loading branch information
danielberkompas committed Apr 6, 2024
2 parents be99311 + 6e98740 commit 98b1c96
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
57 changes: 57 additions & 0 deletions lib/cloak_ecto/types/decimal.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Cloak.Ecto.Decimal do
@moduledoc """
An `Ecto.Type` to encrypt a decimal field, relying on the `Decimal`
library, which is a dependency of Ecto.
Decimals are more precise than floats.
## Migration
The database field must be of type `:binary`.
add :encrypted_field, :binary
## Usage
Define an `Encrypted.Decimal` module in your project:
defmodule MyApp.Encrypted.Decimal do
use Cloak.Ecto.Decimal, vault: MyApp.Vault
end
Then, define the type of your desired fields:
schema "table_name" do
field :encrypted_field, MyApp.Encrypted.Decimal
end
"""

@doc false
defmacro __using__(opts) do
opts = Keyword.merge(opts, vault: Keyword.fetch!(opts, :vault))

quote do
use Cloak.Ecto.Type, unquote(opts)

def cast(closure) when is_function(closure, 0) do
cast(closure.())
end

def cast(value), do: Ecto.Type.cast(:decimal, value)

def before_encrypt(value) do
case Ecto.Type.cast(:decimal, value) do
{:ok, d} -> Decimal.to_string(d)
_error -> :error
end
end

def after_decrypt(value) do
case Decimal.new(value) do
%Decimal{} = d -> d
_error -> :error
end
end
end
end
end
3 changes: 2 additions & 1 deletion lib/cloak_ecto/types/float.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Cloak.Ecto.Float do
@moduledoc """
An `Ecto.Type` to encrypt a float field.
An `Ecto.Type` to encrypt a float field. Consider using `Cloak.Ecto.Decimal`
instead if precision is important.
## Migration
Expand Down
5 changes: 3 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@ defmodule Cloak.Ecto.MixProject do
],
"Ecto Types": [
Cloak.Ecto.Binary,
Cloak.Ecto.DateTime,
Cloak.Ecto.Date,
Cloak.Ecto.DateTime,
Cloak.Ecto.Decimal,
Cloak.Ecto.Float,
Cloak.Ecto.HMAC,
Cloak.Ecto.IntegerList,
Cloak.Ecto.Integer,
Cloak.Ecto.IntegerList,
Cloak.Ecto.Map,
Cloak.Ecto.NaiveDateTime,
Cloak.Ecto.PBKDF2,
Expand Down
51 changes: 51 additions & 0 deletions test/cloak_ecto/types/decimal_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule Cloak.Ecto.DecimalTest do
use ExUnit.Case

defmodule Field do
use Cloak.Ecto.Decimal, vault: Cloak.Ecto.TestVault
end

defmodule ClosureField do
use Cloak.Ecto.Decimal, vault: Cloak.Ecto.TestVault, closure: true
end

test ".type is :binary" do
assert Field.type() == :binary
end

test ".cast rejects invalid input" do
assert :error == Field.cast("blahblah")
end

test ".cast converts integers, floats and string to Decimals" do
assert {:ok, Decimal.from_float(21.0)} == Field.cast(21.0)
assert {:ok, Decimal.new(21)} == Field.cast(21)
assert {:ok, Decimal.new(21)} == Field.cast("21")
assert {:ok, Decimal.from_float(21.0)} == Field.cast("21.0")
end

test ".cast unwraps closures" do
assert {:ok, Decimal.from_float(21.0)} == Field.cast(fn -> 21.0 end)
assert {:ok, Decimal.new(21)} == Field.cast(fn -> 21 end)
assert {:ok, Decimal.new(21)} == Field.cast(fn -> "21" end)
assert {:ok, Decimal.from_float(21.0)} == Field.cast(fn -> "21.0" end)
end

test ".dump encrypts the value" do
{:ok, ciphertext} = Field.dump(1.0)
assert ciphertext != Decimal.from_float(1.0)
assert ciphertext != "1.0"
end

test ".load decrypts an encrypted value" do
{:ok, ciphertext} = Field.dump(1.0)
assert {:ok, Decimal.from_float(1.0)} == Field.load(ciphertext)
end

test ".load with closure option wraps decrypted value" do
{:ok, ciphertext} = ClosureField.dump(1.0)
assert {:ok, closure} = ClosureField.load(ciphertext)
assert is_function(closure, 0)
assert Decimal.from_float(1.0) == closure.()
end
end

0 comments on commit 98b1c96

Please sign in to comment.