EctoHooks (ecto_hooks v0.3.0) View Source

When use-ed in a module that also use-es Ecto.Repo, augments the following Ecto.Repo callbacks to provide user definable hooks following successful execution.

Hooks to MyApp.EctoSchema.after_get/1:

  • all/2
  • get/3
  • get!/3
  • get_by/3
  • get_by!/3
  • one/2
  • one!/2

Hooks to MyApp.EctoSchema.after_delete/1:

  • delete/2
  • delete!/2

Hooks to MyApp.EctoSchema.after_insert/1:

  • insert/2
  • insert!/2

Hooks to MyApp.EctoSchema.after_update/1:

  • update/2
  • update!/2

Hooks to MyApp.EctoSchema.after_insert/1 or to MyApp.Ecto.Schema.after_update/1:

  • insert_or_update/2
  • insert_or_update!/2

Hooks to MyApp.EctoSchema.before_delete/1:

  • delete/2
  • delete!/2

Hooks to MyApp.EctoSchema.before_insert/1:

  • insert/2
  • insert!/2

Hooks to MyApp.EctoSchema.before_update/1:

  • update/2
  • update!/2

Hooks to MyApp.EctoSchema.before_insert/1 or to MyApp.Ecto.Schema.before_update/1:

  • insert_or_update/2
  • insert_or_update!/2

Please note that for all after_* hooks, the result of executing a MyApp.Repo.* callback is what ultimately gets returned from the hook, and thus you should aim to write logic that is transparent and does not break the expected semantics or behaviour of said callback.

Any results wrapped within an {:ok, _} or {:error, _} are also returned re-wrapped as expected.

For all before_* hooks, the result returned by hook is passed directly to the MyApp.Repo.* callback called and thus care must be made to be aware of any implicit changes to changesets prior to writing to the database.

The hooking functionality provided by EctoHooks can be pretty useful for resolving virtual fields, but can also prove useful for centralising some logging or telemetry logic. Note that because any business logic is executed synchronously after the hooked Ecto.Repo callback, one should avoid doing any blocking or potentially terminating logic within hooks as weird or strange behaviour may occur.

Example usage:

def MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres

  use EctoHooks
end

def MyApp.User do
  use Ecto.Changeset

  require Logger

  schema "users" do
    field :first_name, :string
    field :last_name, :string

    field :full_name, :string, virtual: true
  end

  def before_insert(changeset) do
    Logger.warning("updating a user...")
    changeset
  end

  def after_get(%__MODULE__{first_name: first_name, last_name: last_name} = user) do
    %__MODULE__{user | full_name: first_name <> " " <> last_name}
  end
end