Skuld.Effects.Port.Repo.Contract behaviour (skuld v0.23.0)

View Source

Port contract for common Ecto Repo operations.

Provides defport declarations for the standard write and read operations from Ecto.Repo, so that domain code using Port for database access doesn't need to redeclare these with identical boilerplate.

Write Operations

Write operations return {:ok, struct()} | {:error, Ecto.Changeset.t()} and auto-generate bang variants (insert!, update!, delete!) that unwrap the success value or dispatch Throw on error.

Bulk Operations

insert_all/3, update_all/3 and delete_all/2 follow Ecto's return convention of {count, nil | list}. No bang variants are generated for these.

Read Operations

Read operations follow Ecto's conventions: get/2, get_by/2, one/1 return nil on not-found; all/1 returns a list; exists?/1 returns a boolean; aggregate/3 returns a term.

Bang read variants (get!/2, get_by!/2, one!/1) are provided as separate port operations that mirror Ecto's raise-on-not-found semantics. In the effectful context these dispatch Throw instead of raising.

Example

alias Skuld.Effects.Port.Repo

use Skuld.Syntax

defcomp create_user(attrs) do
  changeset = User.changeset(%User{}, attrs)
  user <- Repo.EffectPort.insert!(changeset)
  return(user)
end

defcomp find_user(id) do
  user <- Repo.EffectPort.get(User, id)
  return(user)
end

Summary

Callbacks

aggregate(queryable, aggregate, field)

@callback aggregate(queryable :: Ecto.Queryable.t(), aggregate :: atom(), field :: atom()) ::
  term()

all(queryable)

@callback all(queryable :: Ecto.Queryable.t()) :: [struct()]

delete(record)

@callback delete(record :: struct()) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}

delete_all(queryable, opts)

@callback delete_all(queryable :: Ecto.Queryable.t(), opts :: keyword()) ::
  {non_neg_integer(), nil | list()}

exists?(queryable)

@callback exists?(queryable :: Ecto.Queryable.t()) :: boolean()

get(queryable, id)

@callback get(queryable :: Ecto.Queryable.t(), id :: term()) :: struct() | nil

get!(queryable, id)

@callback get!(queryable :: Ecto.Queryable.t(), id :: term()) :: struct()

get_by(queryable, clauses)

@callback get_by(queryable :: Ecto.Queryable.t(), clauses :: keyword() | map()) ::
  struct() | nil

get_by!(queryable, clauses)

@callback get_by!(queryable :: Ecto.Queryable.t(), clauses :: keyword() | map()) ::
  struct()

insert(changeset)

@callback insert(changeset :: Ecto.Changeset.t()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}

insert_all(source, entries, opts)

@callback insert_all(
  source :: Ecto.Queryable.t() | binary(),
  entries :: [map() | keyword()],
  opts :: keyword()
) :: {non_neg_integer(), nil | list()}

one(queryable)

@callback one(queryable :: Ecto.Queryable.t()) :: struct() | nil

one!(queryable)

@callback one!(queryable :: Ecto.Queryable.t()) :: struct()

update(changeset)

@callback update(changeset :: Ecto.Changeset.t()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}

update_all(queryable, updates, opts)

@callback update_all(
  queryable :: Ecto.Queryable.t(),
  updates :: keyword(),
  opts :: keyword()
) ::
  {non_neg_integer(), nil | list()}