Today I Learned

Using changesets for normal structs

Changesets are known to be used with Ecto schemas:

defmodule Something do
  use MyApp.Schema

 schema "somethings" do
    field :name, :string
  end

 @fields ~w(name)a

 def changeset(struct, params) do
    struct
    |> cast(params, @fields)
    ... # validations
  end
end

iex> changeset = Something.changeset(%Something{}, %{name: "some name"})
#Ecto.Changeset<
  action: nil,
  changes: %{name: "some name"},
  errors: [],
  data: #Something<>,
  valid?: true
>

The fun fact is, we can also use them to validate our own, custom structs, like this:

defmodule BidRequest do
  import Ecto.Changeset

  defstruct ~w(bid)a

  @types %{bid: :integer}

  def changeset(struct, params) do
    {struct, @types}
    |> cast(params, Map.keys(@types))
    |> validate_number(:bid, greater_than: 0)
  end
end

iex> BidRequest.changeset(%BidRequest{}, %{bid: 20})
#Ecto.Changeset<
    action: nil,
    changes: %{bid: 20},
    errors: [],
    data: #BidRequest<>,
    valid?: true
>

iex> BidRequest.changeset(%BidRequest{}, %{bid: -10})
#Ecto.Changeset<
    action: nil,
    changes: %{bid: -10},
    errors: [
      bid: {"must be greater than %{number}",
       [validation: :number, kind: :greater_than, number: 0]}
    ],
    data: #BidRequest<>,
    valid?: false
>