View Source Validations

Builtin Validations

Checkout the documentation for Ash.Resource.Validation.Builtins to see the builtin validations.

Some examples of usage of builtin validations

validate match(:email, ~r/@/)

validate compare(:age, greater_than_or_equal_to: 18) do
  message "must be over 18 to sign up"
end

validate present(:last_name) do
  where [present(:first_name), present(:middle_name)]
  message "must also be supplied if setting first name and middle_name"
end

Custom Validations

defmodule MyApp.Validations.IsPrime do
  # transform and validate opts

  use Ash.Resource.Validation

  @impl true
  def init(opts) do
    if is_atom(opts[:attribute]) do
      {:ok, opts}
    else
      {:error, "attribute must be an atom!"}
    end
  end

  @impl true
  def validate(changeset, opts) do
    value = Ash.Changeset.get_attribute(changeset, opts[:attribute])
    # this is a function I made up for example
    if is_nil(value) || Math.is_prime?(value) do
      :ok
    else
      # The returned error will be passed into `Ash.Error.to_ash_error/3`
      {:error, field: opts[:attribute], message: "must be prime"}
    end
  end
end

This could then be used in a resource via:

validate {MyApp.Validations.IsPrime, attribute: :foo}

Where

The where can be used to perform changes/validations conditionally. This functions by running the validation, and if the validation returns an error, we discard the error and skip the operation. This means that even custom validations can be used in conditions.

For example:

validate present(:other_number) do
  where [{MyApp.Validations.IsPrime, attribute: :foo}]
end

Action vs Global Validations

You can place a validation in any create, update, or destroy action. For example:

actions do
  create :create do
    validate compare(:age, greater_than_or_equal_to: 18)
  end
end

Or you can use the global validations block to validate on all actions of a given type. Where statements can be used in either. Use on to determine the types of actions the validation runs on. By default, it only runs on create an update actions

validations do
  validate present([:foo, :bar], at_least: 1) do
    on [:create, :update]
    where present(:baz)
  end
end

Action-Specific Validation

You can also put a validation directly in an action, like so:

actions do
  create do
    ...
    validate present([:foo, :bar], at_least: 1)
  end
end