EctoHooks (ecto_hooks v1.0.1) 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/2:

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

Hooks to MyApp.EctoSchema.after_delete/2:

  • delete/2
  • delete!/2

Hooks to MyApp.EctoSchema.after_insert/2:

  • insert/2
  • insert!/2

Hooks to MyApp.EctoSchema.after_update/2:

  • update/2
  • update!/2

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

  • 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.

Also, all after_* hooks are provided an EctoHooks.Delta struct as a second parameter, which in some cases can be helpful for intuiting the diff between the result before versus after running a repo operation. Please see the docs pertaining to EctoHooks.Delta for more information.

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.

All defined hooks are executed synchronously immediately before or after calling into your configured database. To prevent the potential for hooks to infinite loop before returning, by default, EctoHooks will not trigger more than once within a single Repo call. You can opt out of this via calling EctoHooks.enable_hooks/0 in any of your defined hooks.

This infinite loop protection only currently works within a given process. Take care when a defined hook may spawn other processes which may trigger database updates which themselves result in hooks being called.

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__{} = user, %EctoHooks.Delta{}) do
    %__MODULE__{user | full_name: user.first_name <> " " <> user.last_name}
  end
end

Link to this section Summary

Functions

Disables EctoHooks from running for all future Repo operations in the current process.

Enables EctoHooks from running for all future Repo operations in the current process.

Returns a boolean indicating if EctoHooks are enabled in the current process.

Link to this section Functions

Disables EctoHooks from running for all future Repo operations in the current process.

Enables EctoHooks from running for all future Repo operations in the current process.

Returns a boolean indicating if EctoHooks are enabled in the current process.