Packages
ash
3.13.2
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/resource/validation/compare.ex
# SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule Ash.Resource.Validation.Compare do
@moduledoc false
use Ash.Resource.Validation
alias Ash.Error.Changes.InvalidAttribute
import Ash.Expr
@opt_schema [
greater_than: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should be greater than."
],
greater_than_or_equal_to: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should be greater than or equal to"
],
less_than: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should be less than"
],
less_than_or_equal_to: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should be less than or equal to"
],
is_equal: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should be equal to"
],
is_not_equal: [
type: {:or, [:any, {:tagged_tuple, :value, :any}, {:tagged_tuple, :ref, :atom}]},
required: false,
doc: "The value that the attribute should not be equal to"
],
is_nil: [
type: :boolean,
required: false,
doc: "Whether the attribute should be nil (true) or not nil (false)"
],
attribute: [
type: :atom,
hide: true
]
]
def opt_schema, do: @opt_schema
opt_schema = @opt_schema
defmodule Opts do
@moduledoc false
use Spark.Options.Validator, schema: opt_schema
end
@impl true
def init(opts) do
case Opts.validate(opts) do
{:ok, opts} ->
{:ok, Opts.to_options(opts)}
{:error, error} ->
{:error, Exception.message(error)}
end
end
@impl true
def supports(_), do: [Ash.Changeset, Ash.Query, Ash.ActionInput]
@impl true
def validate(subject, opts, _context) do
value =
if Enum.any?(subject.action.arguments, &(&1.name == opts[:attribute])) do
Ash.Subject.fetch_argument(subject, opts[:attribute])
else
case subject do
%Ash.Changeset{} -> {:ok, Ash.Changeset.get_attribute(subject, opts[:attribute])}
_ -> :error
end
end
case value do
{:ok, nil} ->
if opts[:is_nil] == false do
invalid_attribute_error(opts, nil)
else
:ok
end
{:ok, value} ->
validate_value(subject, opts, value)
_ ->
:ok
end
end
@impl true
def atomic(changeset, opts, context) do
if Enum.any?(changeset.action.arguments, &(&1.name == opts[:attribute])) do
# (!Keyword.has_key?(changeset.atomics, opts[:attribute]) &&
# Map.has_key?(changeset.attributes, opts[:attribute])) do
validate(changeset, opts, context)
else
opts
|> Keyword.take([
:greater_than,
:less_than,
:greater_than_or_equal_to,
:less_than_or_equal_to,
:is_equal,
:is_not_equal,
:is_nil
])
|> Enum.map(fn
{:greater_than, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) <= ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must be greater than %{greater_than}"),
vars: %{field: ^opts[:attribute], greater_than: ^atomic_value(value)}
})
)}
{:less_than, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) >= ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must be less than %{less_than}"),
vars: %{field: ^opts[:attribute], less_than: ^atomic_value(value)}
})
)}
{:greater_than_or_equal_to, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) < ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message:
^(context.message ||
"must be greater than or equal to %{greater_than_or_equal_to}"),
vars: %{field: ^opts[:attribute], greater_than_or_equal_to: ^atomic_value(value)}
})
)}
{:less_than_or_equal_to, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) > ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message:
^(context.message || "must be less than or equal to %{less_than_or_equal_to}"),
vars: %{field: ^opts[:attribute], less_than_or_equal_to: ^atomic_value(value)}
})
)}
{:is_equal, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) != ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must be equal to %{is_equal}"),
vars: %{field: ^opts[:attribute], is_equal: ^atomic_value(value)}
})
)}
{:is_not_equal, value} ->
{:atomic, [opts[:attribute]],
expr(^atomic_ref(opts[:attribute]) == ^atomic_value(value)),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must not be equal to %{is_not_equal}"),
vars: %{field: ^opts[:attribute], is_not_equal: ^atomic_value(value)}
})
)}
{:is_nil, true} ->
{:atomic, [opts[:attribute]], expr(not is_nil(^atomic_ref(opts[:attribute]))),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must be nil"),
vars: %{field: ^opts[:attribute]}
})
)}
{:is_nil, false} ->
{:atomic, [opts[:attribute]], expr(is_nil(^atomic_ref(opts[:attribute]))),
expr(
error(^InvalidAttribute, %{
field: ^opts[:attribute],
value: ^atomic_ref(opts[:attribute]),
message: ^(context.message || "must not be nil"),
vars: %{field: ^opts[:attribute]}
})
)}
end)
end
end
@impl true
def describe(opts) do
[
vars:
[
greater_than: opts[:greater_than],
less_than: opts[:less_than],
greater_than_or_equal_to: opts[:greater_than_or_equal_to],
less_than_or_equal_to: opts[:less_than_or_equal_to],
is_equal: opts[:is_equal],
is_not_equal: opts[:is_not_equal],
is_nil: opts[:is_nil]
]
|> Enum.map(fn {k, v} ->
case v do
fun when is_function(fun, 0) -> {k, fun.()}
v when is_struct(v) -> {k, v}
v when is_list(v) -> {k, format_value(v)}
v when is_map(v) -> {k, format_value(v)}
v when is_tuple(v) -> {k, format_value(v)}
v -> {k, v}
end
end),
message: opts[:message] || message(opts)
]
end
defp validate_value(changeset, opts, value) do
cond do
opts[:is_nil] == true && !is_nil(value) ->
invalid_attribute_error(opts, value)
opts[:is_nil] == false && is_nil(value) ->
invalid_attribute_error(opts, value)
is_nil(value) ->
:ok
true ->
validate_comparisons(changeset, opts, value)
end
end
defp validate_comparisons(subject, opts, value) do
opts
|> Keyword.take([
:greater_than,
:less_than,
:greater_than_or_equal_to,
:less_than_or_equal_to,
:is_equal,
:is_not_equal
])
|> Enum.find_value(fn
{:greater_than, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.greater_than?/2, opts)
{:greater_than_or_equal_to, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.greater_or_equal?/2, opts)
{:less_than, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.less_than?/2, opts)
{:less_than_or_equal_to, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.less_or_equal?/2, opts)
{:is_equal, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.equal?/2, opts)
{:is_not_equal, attribute} ->
attribute_val = normalize_value(attribute_value(subject, attribute))
compare_values(value, attribute_val, &Comp.not_equal?/2, opts)
end) || :ok
end
defp normalize_value(value) do
case value do
{:value, val} -> val
_ -> value
end
end
defp compare_values(value, compare_to, comparison_fn, opts) do
if !comparison_fn.(value, compare_to) do
invalid_attribute_error(opts, value)
end
end
defp format_value(value) when is_list(value) do
items = Enum.map_join(value, ", ", &format_value/1)
"[#{items}]"
end
defp format_value(value) when is_map(value) do
items =
value
|> Enum.sort_by(fn {k, _v} -> to_string(k) end)
|> Enum.map_join(", ", fn {k, v} -> "#{k}: #{format_value(v)}" end)
"%{#{items}}"
end
defp format_value(value) when is_tuple(value) do
case value do
{:value, val} ->
val
_ ->
items = value |> Tuple.to_list() |> Enum.map_join(", ", &format_value/1)
"{#{items}}"
end
end
defp format_value(value), do: value
defp attribute_value(_changeset, attribute) when is_function(attribute, 0) do
attribute.()
end
defp attribute_value(subject, attribute) when is_atom(attribute) do
Ash.Subject.get_argument_or_attribute(subject, attribute)
end
defp attribute_value(_subject, attribute), do: attribute
defp atomic_value(attribute) when is_function(attribute, 0) do
attribute.()
end
defp atomic_value(attribute) when is_atom(attribute) do
atomic_ref(attribute)
end
defp atomic_value(attribute), do: attribute
defp invalid_attribute_error(opts, attribute_value) do
value =
if is_function(attribute_value) do
attribute_value.()
else
attribute_value
end
{:error,
[
field: opts[:attribute],
value: value
]
|> with_description(opts)
|> InvalidAttribute.exception()}
end
defp message(opts) do
opts
|> Keyword.take([
:greater_than,
:less_than,
:greater_than_or_equal_to,
:less_than_or_equal_to,
:is_equal,
:is_not_equal,
:is_nil
])
|> Enum.map_join(" and ", fn {key, value} ->
case key do
:greater_than ->
"must be greater than %{greater_than}"
:less_than ->
"must be less than %{less_than}"
:greater_than_or_equal_to ->
"must be greater than or equal to %{greater_than_or_equal_to}"
:less_than_or_equal_to ->
"must be less than or equal to %{less_than_or_equal_to}"
:is_equal ->
"must be equal to %{is_equal}"
:is_not_equal ->
"must not be equal to %{is_not_equal}"
:is_nil when value == true ->
"must be nil"
:is_nil when value == false ->
"must not be nil"
end
end)
end
end