View Source SwissSchema behaviour (swiss_schema v0.6.0)

SwissSchema is a query toolkit for Ecto schemas. It makes it easy to manipulate data using Ecto schemas by implementing relevant Ecto.Repo Query API and Schema API functions, pre-configured to work specifically with the given Ecto schema.

Setup

Add swiss_schema as a dependency in mix.exs:

def deps do
[
  # ...
  {:swiss_schema, "~> 0.6"}
]
end

Then, use SwissSchema in your Ecto schemas:

# lib/my_app/accounts/user.ex

defmodule MyApp.Accounts.User do
  use Ecto.Schema
  use SwissSchema, repo: MyApp.Repo

  def changeset(%__MODULE__{} = user, params) do
    # Set up your schema's default changeset here
  end
end

That's it, you should be good to go.

Usage

After setting up SwissSchema in your Ecto schema, there isn't much else to do. Just use the available functions now available in your own schema module:

iex> alias MyApp.Accounts.User

iex> User.all()
[]

iex> User.create(%{name: "John Smith", email: "john@smiths.net"})
{:ok, %User%{name: "John Smith", ...}}

iex> User.all()
[%User%{name: "John Smith", ...}]

SwissSchema functions tries to mimic the Ecto.Repo's Query and Repo APIs by acting as a thin, pre-configured interface for Ecto.Repo callbacks.

Note

To keep examples simple and short, we use the example module above, MyApp.Accounts.User, through the documentation. So, when reading the docs for SwissSchema functions, assume the following alias was previously set up:

alias MyApp.Accounts.User

So, MyApp.Accounts.User will be referred to as User.

Summary

Ecto.Repo Query API

Calculate the given aggregation.

Calculate the given aggregate over the given field.

Fetches all entries from the respective data store.

Deletes all entries.

Fetches an entry by the primary key.

Similar to get/2 but raises Ecto.NoResultsError if no entry was found.

Fetches a single entry by using a keyword list of clauses.

Similar to get_by/2 but raises Ecto.NoResultsError if no entry is found, or Ecto.MultipleResultsError if multiple entries are found.

stream(opts) deprecated

Returns a lazy enumerable that emits all entries from the data store.

Updates all entries matching the given query with the given values.

Ecto.Repo Schema API

Deletes an entry using its primary key.

Same as delete/2 but returns the entry, or raises.

Inserts a new entry.

Same as insert/2 but returns the struct or raises if the changeset is invalid.

Inserts all entries into the repository.

Inserts or updates a changeset depending on whether the struct is persisted or not.

Same as insert_or_update/2 but returns the struct or raises if parameters are invalid.

SwissSchema API

Defines the default changeset function.

Creates a new struct.

Same as create/2 but returns the struct or raises if parameters are invalid.

Updates a struct.

Same as update/3 but returns the struct or raises if parameters are invalid.

Ecto.Repo Query API

@callback aggregate(
  type :: :count,
  opts :: Keyword.t()
) :: term() | nil

Calculate the given aggregation.

Examples

# Returns the number of users
User.aggregate(:count)

# Set a timeout of 60s
User.aggregate(:count, timeout: 60_000)

See Ecto's aggregate/3 for extensive info.

Link to this callback

aggregate(type, field, opts)

View Source
@callback aggregate(
  type :: :avg | :count | :max | :min | :sum,
  field :: atom(),
  opts :: Keyword.t()
) :: term() | nil

Calculate the given aggregate over the given field.

Examples

# Returns the sum of the number of visits for every user
User.aggregate(:sum, :visits)

# Returns the average number of user visits
User.aggregate(:avg, :visits)

See Ecto's aggregate/4 for extensive info.

@callback all(opts :: Keyword.t()) :: [Ecto.Schema.t() | term()]

Fetches all entries from the respective data store.

Examples

# Fetch all users
User.all()

See Ecto's all/2 for extensive info.

@callback delete_all(opts :: Keyword.t()) :: {non_neg_integer(), nil | [term()]}

Deletes all entries.

It returns a two-values tuple: the number of entries deleted, and nil.

Examples

User.delete_all()

See Ecto's delete_all/2 for extensive info.

@callback get(
  id :: term(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:ok, term()} | {:error, :not_found}

Fetches an entry by the primary key.

It returns {:ok, entry} if the entry is found, or {:error, :not_found} otherwise.

Examples

iex> User.get(1)
{:ok, %User{}}

iex> User.get(1234567890)
{:error, :not_found}

See Ecto's get/3 for extensive info.

@callback get!(
  id :: term(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | term()

Similar to get/2 but raises Ecto.NoResultsError if no entry was found.

Examples

iex> User.get!(1)
%User{}

iex> User.get!(1234567890)
** (Ecto.NoResultsError) expected at least one result but got none in query

See Ecto's get!/3 for extensive info.

@callback get_by(
  clauses :: Keyword.t() | map(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:ok, term()} | {:error, :not_found}

Fetches a single entry by using a keyword list of clauses.

It returns a single matching entry as {:ok, entry}, or {:error, :not_found} when none is found.

Examples

iex> User.get_by(email: "john@smiths.net")
%User{}

See Ecto's get_by/3 for extensive info.

@callback get_by!(
  clauses :: Keyword.t() | map(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | term()

Similar to get_by/2 but raises Ecto.NoResultsError if no entry is found, or Ecto.MultipleResultsError if multiple entries are found.

Examples

iex> User.get_by!(email: "john@smiths.net")
%User{}

iex> User.get_by!(email: "nobody@localhost")
** (Ecto.NoResultsError)

iex> User.get_by!(first_name: "John")
** (Ecto.MultipleResultsError)

See Ecto's get_by!/3 for extensive info.

This callback is deprecated. Use Ecto.Repo's stream/2 instead.
@callback stream(opts :: Keyword.t()) :: Enum.t()

Returns a lazy enumerable that emits all entries from the data store.

See Ecto's stream/2 for extensive info.

Link to this callback

update_all(updates, opts)

View Source
@callback update_all(
  updates :: Keyword.t(),
  opts :: Keyword.t()
) :: {non_neg_integer(), nil | [term()]}

Updates all entries matching the given query with the given values.

Examples

iex> User.update_all(set: [is_active: false])

iex> User.update_all(inc: [age: 1])

See Ecto's update_all/3 for extensive info.

Ecto.Repo Schema API

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

Deletes an entry using its primary key.

It returns {:ok, struct} if the entry has been successfully deleted or {:error, changeset} if there was a validation or a known constraint error.

Examples

user = User.get!(42)

case User.delete user do
  {:ok, user}         -> # Deleted with success
  {:error, changeset} -> # Something went wrong
end

See Ecto's delete/2 for extensive info.

@callback delete!(
  struct :: Ecto.Schema.t(),
  opts :: Keyword.t()
) :: Ecto.Schema.t()

Same as delete/2 but returns the entry, or raises.

Examples

user = User.get!(42)

User.delete user do
  {:ok, user}         -> # Deleted with success
  {:error, changeset} -> # Something went wrong
end

See Ecto's delete!/2 for extensive info.

@callback insert(
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Inserts a new entry.

Accepts a map with key-values matching respective schema fields.

It returns {:ok, struct} if the entry has been successfully inserted, or {:error, %Ecto.Changeset{}} if there is a validation or a known constraint error.

Examples

A typical example is calling insert/2 with a map and acting on the return value:

case User.insert(%{name: "John S.", email: "john@smiths.net"}) do
  {:ok, user}         -> # Inserted with success
  {:error, changeset} -> # Something went wrong
end

See Ecto's insert/2 for extensive info.

@callback insert!(
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: Ecto.Schema.t()

Same as insert/2 but returns the struct or raises if the changeset is invalid.

Link to this callback

insert_all(entries, opts)

View Source
@callback insert_all(
  entries :: [%{required(atom()) => term()}] | Keyword.t(),
  opts :: Keyword.t()
) :: {non_neg_integer(), nil | [term()]}

Inserts all entries into the repository.

Examples

User.insert_all([ [name: "John S."], [name: "Jane S."] ])

User.insert_all([ %{name: "John S."}, %{name: "Jane S."} ])

See Ecto's insert_all/3 for extensive info.

Link to this callback

insert_or_update(changeset, opts)

View Source
@callback insert_or_update(
  changeset :: Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Inserts or updates a changeset depending on whether the struct is persisted or not.

Examples

iex> User.changeset(%User{}, %{name: "John S.", email: "john@smiths.net"})
     |> User.insert_or_update()
{:ok, %User{}}

User.get_by!(email: "john@smiths.net")
|> User.changeset(%{email: "john.s@smiths.net"})
|> User.insert_or_update()

See Ecto's insert_or_update/2 for extensive info.

Link to this callback

insert_or_update!(changeset, opts)

View Source
@callback insert_or_update!(
  changeset :: Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: Ecto.Schema.t()

Same as insert_or_update/2 but returns the struct or raises if parameters are invalid.

See Ecto's insert_or_update!/2 for extensive info.

SwissSchema API

Link to this callback

changeset(struct, params)

View Source
@callback changeset(
  struct :: Ecto.Schema.t(),
  params :: %{required(atom()) => term()}
) :: Ecto.Changeset.t()

Defines the default changeset function.

This callback is used by database-touching functions to validate changes.

Examples

User.changeset(%User{}, %{name: "John"})
iex> %Ecto.Changeset{valid?: true}

See Ecto's Ecto.Changeset for extensive info.

@callback create(
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Creates a new struct.

create/2 accepts a key-value map, validates it against the schema's changeset/2, and inserts into the repository if the validation succeeds.

Examples

# Returns an :ok tuple with valid params
{:ok, user} = User.create(%{name: "John S.", email: "john@smiths.net"})

# Returns an :error tuple with invalid params
{:error, %Ecto.Changeset{...}} = User.create(%{name: 123, email: "john"})
@callback create!(
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: Ecto.Schema.t()

Same as create/2 but returns the struct or raises if parameters are invalid.

Link to this callback

update(struct, params, opts)

View Source
@callback update(
  struct :: Ecto.Schema.t(),
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Updates a struct.

Examples

iex> User.get_by!(email: "john@smiths.net")
     |> User.update(%{email: "john.s@smiths.net"})
{:ok, %User{}}

See Ecto's update/2 for extensive info.

Link to this callback

update!(struct, params, opts)

View Source
@callback update!(
  struct :: Ecto.Schema.t(),
  params :: %{required(atom()) => term()},
  opts :: Keyword.t()
) :: Ecto.Schema.t()

Same as update/3 but returns the struct or raises if parameters are invalid.

Examples

iex> User.get_by!(email: "john@smiths.net")
     |> User.update!(%{email: "john.s@smiths.net"})
%User{}

See Ecto's update!/2 for extensive info.