Trogon.Ecto.ValueObject (Trogon.Ecto v0.1.1)

Copy Markdown View Source

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

__using__(opts \\ [])

(macro)
@spec __using__(opts :: Keyword.t()) :: Macro.t()

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
end

Overridable

  • validate/2 to add custom validation to the existing changeset/2 without overriding the whole changeset/2 function.

      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
      end
  • changeset/2 returns an Ecto.Changeset.t/0 for a given value object struct.

    Overriding Changeset

    Be careful when overriding changeset/2 because the default implementation takes care of cast, validate_required the @enforced_keys and nested embeds. You may want to call Trogon.Ecto.ValueObject.changeset/2 to have such features.

    If you only need to extend the changeset, you can override the validate/2 function instead.

changeset(message, attrs)

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

new(struct_module, attrs)

@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 uses Trogon.Ecto.ValueObject
  • attrs - 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?
false

Missing required field:

iex> {:error, changeset} = Trogon.Ecto.ValueObject.new(Trogon.Ecto.TestSupport.MyValueObject, %{amount: 25})
iex> changeset.valid?
false

Passing 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.

new!(struct_module, attrs)

@spec new!(struct_module :: atom(), attrs :: map() | struct()) :: struct()

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 uses Trogon.Ecto.ValueObject
  • attrs - 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_raised

Missing 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_raised

Passing 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.