mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
improvement: before_action?
on validate
, validate inline
This commit is contained in:
parent
274fc9ea88
commit
92344029d3
7 changed files with 90 additions and 30 deletions
|
@ -12,6 +12,7 @@ locals_without_parens = [
|
|||
attribute: 2,
|
||||
attribute: 3,
|
||||
base_filter: 1,
|
||||
before_action?: 1,
|
||||
belongs_to: 2,
|
||||
belongs_to: 3,
|
||||
calculate: 3,
|
||||
|
|
|
@ -636,11 +636,7 @@ defmodule Ash.Changeset do
|
|||
module.change(changeset, opts, %{actor: actor})
|
||||
|
||||
%{validation: _} = validation, changeset ->
|
||||
if validation.expensive? and not changeset.valid? do
|
||||
changeset
|
||||
else
|
||||
do_validation(changeset, validation)
|
||||
end
|
||||
validate(changeset, validation)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -693,19 +689,29 @@ defmodule Ash.Changeset do
|
|||
defp default(:update, %{update_default: value}), do: value
|
||||
|
||||
defp add_validations(changeset) do
|
||||
Ash.Changeset.before_action(changeset, fn changeset ->
|
||||
changeset.resource
|
||||
# We use the `changeset.action_type` to support soft deletes
|
||||
# Because a delete is an `update` with an action type of `update`
|
||||
|> Ash.Resource.Info.validations(changeset.action_type)
|
||||
|> Enum.reduce(changeset, fn validation, changeset ->
|
||||
changeset.resource
|
||||
# We use the `changeset.action_type` to support soft deletes
|
||||
# Because a delete is an `update` with an action type of `update`
|
||||
|> Ash.Resource.Info.validations(changeset.action_type)
|
||||
|> Enum.reduce(changeset, &validate(&2, &1))
|
||||
end
|
||||
|
||||
defp validate(changeset, validation) do
|
||||
if validation.before_action? do
|
||||
before_action(changeset, fn changeset ->
|
||||
if validation.expensive? and not changeset.valid? do
|
||||
changeset
|
||||
else
|
||||
do_validation(changeset, validation)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
else
|
||||
if validation.expensive? and not changeset.valid? do
|
||||
changeset
|
||||
else
|
||||
do_validation(changeset, validation)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_validation(changeset, validation) do
|
||||
|
|
|
@ -39,7 +39,16 @@ defmodule Ash.Resource.Validation do
|
|||
end
|
||||
```
|
||||
"""
|
||||
defstruct [:validation, :module, :opts, :expensive?, :description, :message, on: []]
|
||||
defstruct [
|
||||
:validation,
|
||||
:module,
|
||||
:opts,
|
||||
:expensive?,
|
||||
:description,
|
||||
:message,
|
||||
:before_action?,
|
||||
on: []
|
||||
]
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
|
@ -92,6 +101,12 @@ defmodule Ash.Resource.Validation do
|
|||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the validation"
|
||||
],
|
||||
before_action?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
doc:
|
||||
"If set to `true`, the validation is not run when building changesets using `Ash.Changeset.for_*`. The validation will only ever be run once the action itself is called."
|
||||
]
|
||||
]
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ defmodule Ash.Resource.Validation.Confirm do
|
|||
Changeset.get_argument(changeset, opts[:field]) ||
|
||||
Changeset.get_attribute(changeset, opts[:field])
|
||||
|
||||
if confirmation_value == value do
|
||||
if Comp.equal?(confirmation_value, value) do
|
||||
:ok
|
||||
else
|
||||
{:error,
|
||||
|
|
|
@ -36,20 +36,38 @@ defmodule Ash.Resource.Validation.Match do
|
|||
@impl true
|
||||
def validate(changeset, opts) do
|
||||
case Ash.Changeset.fetch_change(changeset, opts[:attribute]) do
|
||||
{:ok, changing_to} when is_binary(changing_to) ->
|
||||
if String.match?(changing_to, opts[:match]) do
|
||||
:ok
|
||||
else
|
||||
{:error,
|
||||
InvalidAttribute.exception(
|
||||
field: opts[:attribute],
|
||||
message: opts[:message],
|
||||
vars: [match: opts[:match]]
|
||||
)}
|
||||
{:ok, changing_to} ->
|
||||
case string_value(changing_to, opts) do
|
||||
{:ok, changing_to} ->
|
||||
if String.match?(changing_to, opts[:match]) do
|
||||
:ok
|
||||
else
|
||||
{:error,
|
||||
InvalidAttribute.exception(
|
||||
field: opts[:attribute],
|
||||
message: opts[:message],
|
||||
vars: [match: opts[:match]]
|
||||
)}
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp string_value(value, opts) do
|
||||
{:ok, to_string(value)}
|
||||
rescue
|
||||
_ ->
|
||||
{:error,
|
||||
InvalidAttribute.exception(
|
||||
field: opts[:attribute],
|
||||
message: opts[:message],
|
||||
vars: [match: opts[:match]]
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ defmodule Ash.Resource.Validation.OneOf do
|
|||
:ok
|
||||
|
||||
{:ok, changing_to} ->
|
||||
if changing_to in opts[:values] do
|
||||
if Enum.any?(opts[:values], &Comp.equal?(&1, changing_to)) do
|
||||
:ok
|
||||
else
|
||||
{:error,
|
||||
|
|
|
@ -36,12 +36,32 @@ defmodule Ash.Resource.Validation.StringLength do
|
|||
|
||||
@impl true
|
||||
def validate(changeset, opts) do
|
||||
Ash.Changeset.get_attribute(changeset, opts[:attribute])
|
||||
|> do_validate(Enum.into(opts, %{}))
|
||||
end
|
||||
case Ash.Changeset.get_attribute(changeset, opts[:attribute]) do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
defp do_validate(nil, _) do
|
||||
:ok
|
||||
value ->
|
||||
value =
|
||||
try do
|
||||
{:ok, to_string(value)}
|
||||
rescue
|
||||
_ ->
|
||||
{:error,
|
||||
InvalidAttribute.exception(
|
||||
field: opts[:attribute],
|
||||
message: "%{field} could not be parsed",
|
||||
vars: [field: opts[:attribute]]
|
||||
)}
|
||||
end
|
||||
|
||||
case value do
|
||||
{:ok, value} ->
|
||||
do_validate(value, Enum.into(opts, %{}))
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_validate(value, %{exact: exact} = opts) do
|
||||
|
|
Loading…
Reference in a new issue