Skip to content

Instantly share code, notes, and snippets.

@meantheory
Last active September 15, 2023 02:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meantheory/59b9daa16e5ee8c4a10e253f10e30d0a to your computer and use it in GitHub Desktop.
Save meantheory/59b9daa16e5ee8c4a10e253f10e30d0a to your computer and use it in GitHub Desktop.
defmodule Example.UserRole do
use Ash.Resource, data_layer: AshPostgres.DataLayer, extensions: [AshTypeID]
postgres do
schema "this"
table "user_role"
repo Example.Repo
end
actions do
defaults [:create, :read, :update, :destroy]
end
attributes do
typeid_attribute :id, prefix: "role"
attribute :role_name, :string do
allow_nil? false
constraints [
allow_empty?: false,
min_length: 3,
max_length: 100,
]
end
create_timestamp :inserted_at
update_timestamp :updated_at
end
relationships do
belongs_to :user, Example.User do
primary_key? false
attribute_type :typeid
attribute_writable? true
end
end
end
defmodule AshTypeID.TypeID do
@constraints [
prefix: [type: :string, doc: "Prefix", default: "id"],
# string?: [type: :boolean, doc: "String?", default: true]
]
@moduledoc """
AshTypeID - type-safe, K-sortable, globally unique identifier inspired by Stripe IDs.
```Elixir
config :ash, :custom_types, [ash_typeid: AshTypeID.TypeID]
```
### Constraints
#{Spark.OptionsHelpers.docs(@constraints)}
"""
use Ash.Type
@doc """
Returns the *underlying* storage type (the underlying type of the *ecto type* of the *ash type*)
"""
# @spec storage_type(t()) :: Ecto.Type.t()
@impl Ash.Type
def storage_type, do: :binary_id
@doc """
Returns the ecto compatible type for an Ash.Type.
If you `use Ash.Type`, this is created for you. For builtin types
this may return a corresponding ecto builtin type (atom)
"""
# @spec ecto_type(t) :: Ecto.Type.t()
# @impl Ash.Type
# def ecto_type({:array, type}), do: {:array, ecto_type(type)}
@impl Ash.Type
def generator(constraints) do
TypeID.new(constraints[:prefix])
end
@doc """
Casts input (e.g. unknown) data to an instance of the type, or errors
Maps to `Ecto.Type.cast/2`
"""
# @spec cast_input(t(), term, constraints | nil) :: {:ok, term} | {:error, Keyword.t()} | :error
@impl Ash.Type
def cast_input(nil, _), do: {:ok, nil}
def cast_input(term, constraints) do
TypeID.Ecto.cast(term, %{prefix: constraints[:prefix]})
end
@doc """
Casts a value from the data store to an instance of the type, or errors
Maps to `Ecto.Type.load/2`
"""
# @spec cast_stored(t(), term, constraints | nil) :: {:ok, term} | {:error, keyword()} | :error
@impl Ash.Type
def cast_stored(nil, _), do: {:ok, nil}
def cast_stored(term, constraints) do
TypeID.Ecto.load(term, nil, %{prefix: constraints[:prefix], type: storage_type()})
end
@doc """
Casts a value from the Elixir type to a value that the data store can persist
Maps to `Ecto.Type.dump/2`
"""
# @spec dump_to_native(t(), term, constraints | nil) :: {:ok, term} | {:error, keyword()} | :error
@impl Ash.Type
def dump_to_native(nil, _), do: {:ok, nil}
def dump_to_native(term, constraints) do
TypeID.Ecto.dump(term, nil, %{prefix: constraints[:prefix], type: storage_type()})
end
@doc """
Casts a value from the Elixir type to a value that can be embedded in another data structure.
Embedded resources expect to be stored in JSON, so this allows things like UUIDs to be stored
as strings in embedded resources instead of binary.
"""
# @spec dump_to_embedded(t(), term, constraints | nil) :: {:ok, term} | {:error, keyword()} | :error
@impl Ash.Type
def dump_to_embedded(term, constraints) do
TypeID.Ecto.dump(term, nil, %{prefix: constraints[:prefix], type: :string})
end
@doc """
Determines if two values of a given type are equal.
Maps to `Ecto.Type.equal?/3`
"""
# @spec equal?(t(), term, term) :: boolean
@impl Ash.Type
def equal?(term1, term2) do
dump_to_embedded(term1, constraints()) == dump_to_embedded(term2, constraints())
end
@impl Ash.Type
def constraints, do: @constraints
@impl Ash.Type
def apply_constraints(_term, _constraints), do: :ok
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment