mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
61e6b7c80c
feat: add `contains/2` query function
135 lines
4.3 KiB
Elixir
135 lines
4.3 KiB
Elixir
defmodule Ash do
|
|
@moduledoc " Types and simple helpers for Ash"
|
|
alias Ash.Resource.Actions.{Create, Destroy, Read, Update}
|
|
alias Ash.Resource.Relationships.{BelongsTo, HasMany, HasOne, ManyToMany}
|
|
|
|
@type action :: Create.t() | Read.t() | Update.t() | Destroy.t()
|
|
@type action_type :: :read | :create | :update | :destroy
|
|
@type actor :: Ash.record()
|
|
@type aggregate :: Ash.Query.Aggregate.t() | Ash.Resource.Aggregate.t()
|
|
@type aggregate_kind :: Ash.Query.Aggregate.kind()
|
|
@type api :: module
|
|
@type attribute :: Ash.Resource.Attribute.t()
|
|
@type calculation :: Ash.Resource.Calculation.t()
|
|
@type cardinality_many_relationship() :: HasMany.t() | ManyToMany.t()
|
|
@type cardinality_one_relationship() :: HasOne.t() | BelongsTo.t()
|
|
@type changeset :: Ash.Changeset.t()
|
|
@type data_layer :: module
|
|
@type data_layer_query :: struct
|
|
@type error :: struct
|
|
@type filter :: Ash.Filter.t()
|
|
@type params :: Keyword.t()
|
|
@type primary_key :: record() | map | term
|
|
@type query :: Ash.Query.t()
|
|
@type record :: struct
|
|
@type relationship :: cardinality_one_relationship() | cardinality_many_relationship()
|
|
@type relationship_cardinality :: :many | :one
|
|
@type resource :: module
|
|
@type side_loads :: term
|
|
@type page :: Ash.Page.Keyset.t() | Ash.Page.Offset.t()
|
|
@type sort_order ::
|
|
:asc | :desc | :asc_nils_first | :asc_nils_last | :desc_nils_first | :desc_nils_last
|
|
@type sort :: list(atom | {atom, sort_order})
|
|
@type validation :: Ash.Resource.Validation.t()
|
|
@type notification :: Ash.Notifier.Notification.t()
|
|
|
|
require Ash.Dsl.Extension
|
|
|
|
def implements_behaviour?(module, behaviour) do
|
|
:attributes
|
|
|> module.module_info()
|
|
|> Enum.flat_map(fn
|
|
{:behaviour, value} -> List.wrap(value)
|
|
_ -> []
|
|
end)
|
|
|> Enum.any?(&(&1 == behaviour))
|
|
rescue
|
|
_ ->
|
|
false
|
|
end
|
|
|
|
def uuid do
|
|
Ecto.UUID.generate()
|
|
end
|
|
|
|
# A restricted version of `:erlang.binary_to_term/2` that forbids
|
|
# *executable* terms, such as anonymous functions.
|
|
# The `opts` are given to the underlying `:erlang.binary_to_term/2`
|
|
# call, with an empty list as a default.
|
|
# By default this function does not restrict atoms, as an atom
|
|
# interned in one node may not yet have been interned on another
|
|
# (except for releases, which preload all code).
|
|
# If you want to avoid atoms from being created, then you can pass
|
|
# `[:safe]` as options, as that will also enable the safety mechanisms
|
|
# from `:erlang.binary_to_term/2` itself.
|
|
# Ripped from https://github.com/elixir-plug/plug_crypto/blob/v1.2.0/lib/plug/crypto.ex
|
|
|
|
# sobelow_skip ["Misc.BinToTerm"]
|
|
def non_executable_binary_to_term(binary, opts \\ []) when is_binary(binary) do
|
|
term = :erlang.binary_to_term(binary, opts)
|
|
non_executable_terms(term)
|
|
term
|
|
end
|
|
|
|
defp non_executable_terms(list) when is_list(list) do
|
|
non_executable_list(list)
|
|
end
|
|
|
|
defp non_executable_terms(tuple) when is_tuple(tuple) do
|
|
non_executable_tuple(tuple, tuple_size(tuple))
|
|
end
|
|
|
|
defp non_executable_terms(map) when is_map(map) do
|
|
folder = fn key, value, acc ->
|
|
non_executable_terms(key)
|
|
non_executable_terms(value)
|
|
acc
|
|
end
|
|
|
|
:maps.fold(folder, map, map)
|
|
end
|
|
|
|
defp non_executable_terms(other)
|
|
when is_atom(other) or is_number(other) or is_bitstring(other) or is_pid(other) or
|
|
is_reference(other) do
|
|
other
|
|
end
|
|
|
|
defp non_executable_terms(other) do
|
|
raise ArgumentError,
|
|
"cannot deserialize #{inspect(other)}, the term is not safe for deserialization"
|
|
end
|
|
|
|
defp non_executable_list([]), do: :ok
|
|
|
|
defp non_executable_list([h | t]) when is_list(t) do
|
|
non_executable_terms(h)
|
|
non_executable_list(t)
|
|
end
|
|
|
|
defp non_executable_list([h | t]) do
|
|
non_executable_terms(h)
|
|
non_executable_terms(t)
|
|
end
|
|
|
|
defp non_executable_tuple(_tuple, 0), do: :ok
|
|
|
|
defp non_executable_tuple(tuple, n) do
|
|
non_executable_terms(:erlang.element(n, tuple))
|
|
non_executable_tuple(tuple, n - 1)
|
|
end
|
|
|
|
@doc "Returns all extensions of a resource or api"
|
|
@spec extensions(resource() | api()) :: [module]
|
|
def extensions(resource) do
|
|
Ash.Dsl.Extension.get_persisted(resource, :extensions)
|
|
end
|
|
|
|
@spec try_compile(term) :: :ok
|
|
def try_compile(module) when is_atom(module) do
|
|
Code.ensure_loaded(module)
|
|
:ok
|
|
end
|
|
|
|
def try_compile(_), do: :ok
|
|
end
|