Defines "Value Object" modules.
Summary
Functions
Converts the module into an Ecto.Schema and add factory functions to create structs.
Returns an Ecto.Changeset.t/0 for a given value object struct.
Creates a value object struct for the given module and attributes.
Creates a value object struct for the given module and attributes, raising on validation errors.
Functions
Converts the module into an Ecto.Schema and add factory functions to create structs.
Using
Imports
use Trogon.Ecto.ValueObject imports PolymorphicEmbed.polymorphic_embeds_one/2
and PolymorphicEmbed.polymorphic_embeds_many/2 so they can be used inside
embedded_schema/1.
Usage
defmodule MyValueObject do
use Trogon.Ecto.ValueObject
embedded_schema do
field :title, :string
# ...
end
endOverridable
validate/2to add custom validation to the existingchangeset/2without overriding the wholechangeset/2function.defmodule MyValueObject do use Trogon.Ecto.ValueObject embedded_schema do field :amount, :integer end def validate(changeset, _attrs) do Ecto.Changeset.validate_number(changeset, :amount, greater_than: 0) end endchangeset/2returns anEcto.Changeset.t/0for a given value object struct.Overriding Changeset
Be careful when overriding
changeset/2because the default implementation takes care ofcast,validate_requiredthe@enforced_keysand nested embeds. You may want to callTrogon.Ecto.ValueObject.changeset/2to have such features.If you only need to extend the changeset, you can override the
validate/2function instead.
Returns an Ecto.Changeset.t/0 for a given value object struct.
It reads the @enforced_keys from the struct and validates the required
fields. Also, it casts the embeds. It is useful when you override the
changeset/2 function in your value object.
Examples
defmodule MyValueObject do
use Trogon.Ecto.ValueObject
@enforce_keys [:title, :amount]
embedded_schema do
field :title, :string
field :amount, :integer
end
def changeset(message, attrs) do
message
|> ValueObject.changeset(attrs)
|> Changeset.validate_number(:amount, greater_than: 0)
end
end
@spec new(struct_module :: atom(), attrs :: map() | struct()) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
Creates a value object struct for the given module and attributes.
This function applies the changeset validation logic defined in the value object
module and returns either {:ok, struct} on success or {:error, changeset}
when validation fails.
Parameters
struct_module- The value object module that usesTrogon.Ecto.ValueObjectattrs- A map of attributes to create the value object with
Examples
Creating a simple value object:
iex> Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.MessageOne, %{title: "Hello"})
{:ok, %Trogon.Ecto.TestSupport.MessageOne{title: "Hello"}}Creating a value object with validation:
iex> Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.TransferableMoney, %{amount: 100, currency: :USD})
{:ok, %Trogon.Ecto.TestSupport.TransferableMoney{amount: 100, currency: :USD}}Validation failure example:
iex> {:error, changeset} = Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.TransferableMoney, %{amount: -5, currency: :USD})
iex> changeset.valid?
falseMissing required field:
iex> {:error, changeset} = Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.MyValueObject, %{amount: 25})
iex> changeset.valid?
falsePassing an own-type struct returns it unchanged (value objects are immutable):
iex> Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.MessageOne, %Trogon.Ecto.TestSupport.MessageOne{title: "Hello"})
{:ok, %Trogon.Ecto.TestSupport.MessageOne{title: "Hello"}}Passing a foreign struct raises an ArgumentError.
Creates a value object struct for the given module and attributes, raising on validation errors.
This function is similar to new/2 but raises an Ecto.InvalidChangesetError
instead of returning an error tuple when validation fails.
Parameters
struct_module- The value object module that usesTrogon.Ecto.ValueObjectattrs- A map of attributes to create the value object with
Examples
Creating a simple value object:
iex> Trogon.Ecto.ValueObject.new!(Trogon.Ecto.TestSupport.MessageOne, %{title: "Hello"})
%Trogon.Ecto.TestSupport.MessageOne{title: "Hello"}Creating a value object with validation:
iex> Trogon.Ecto.ValueObject.new!(Trogon.Ecto.TestSupport.TransferableMoney, %{amount: 100, currency: :USD})
%Trogon.Ecto.TestSupport.TransferableMoney{amount: 100, currency: :USD}Validation failure raises an exception:
iex> try do
...> Trogon.Ecto.ValueObject.new!(Trogon.Ecto.TestSupport.TransferableMoney, %{amount: -5, currency: :USD})
...> rescue
...> Ecto.InvalidChangesetError -> :error_raised
...> end
:error_raisedMissing required field raises an exception:
iex> try do
...> Trogon.Ecto.ValueObject.new!(Trogon.Ecto.TestSupport.MyValueObject, %{amount: 25})
...> rescue
...> Ecto.InvalidChangesetError -> :error_raised
...> end
:error_raisedPassing an own-type struct returns it unchanged:
iex> Trogon.Ecto.ValueObject.new!(Trogon.Ecto.TestSupport.MessageOne, %Trogon.Ecto.TestSupport.MessageOne{title: "Hello"})
%Trogon.Ecto.TestSupport.MessageOne{title: "Hello"}Passing a foreign struct raises an ArgumentError.