Uni Ecto Plugin Now
step = Ecto.update(changeset)
Works identically to insert, but for an existing record.
defmodule Uni.Plugin.SoftDelete do @behaviour Uni.Plugindef prepare_changeset(changeset, opts) do column = Keyword.get(opts, :column, :deleted_at) if changeset.action == :delete do changeset |> Ecto.Changeset.put_change(column, DateTime.utc_now()) |> Map.put(:action, :update) else changeset end end
def modify_query(query, opts) do column = Keyword.get(opts, :column, :deleted_at) from q in query, where: is_nil(field(q, ^column)) end
def before_action(:delete, _struct, opts), do: :ok, nil # prevent actual delete def before_action(, struct, opts), do: :ok, struct def after_action(, _, _), do: nil end
Ecto returns :error, changeset. But what about validation errors vs. database constraint errors vs. connection errors? Uni normalizes all Ecto errors into a standard Uni.Error.t() structure.
test/uni_ecto_plugin/soft_delete_test.exs
defmodule UniEctoPlugin.SoftDeleteTest do use ExUnit.Case, async: true alias Ecto.Adapters.SQL.Sandbox # Setup a test repo (see full example in Appendix)defmodule TestRepo do use Ecto.Repo, otp_app: :uni_ecto_plugin, adapter: Ecto.Adapters.Postgres end
defmodule User do use Ecto.Schema use UniEctoPlugin.SoftDelete uni ecto plugin
schema "users" do field :name, :string timestamps() endend
setup do :ok = Sandbox.checkout(TestRepo) end
test "soft_delete/2 sets deleted_at" do :ok, user = %Username: "Jane" |> TestRepo.insert() assert user.deleted_at == nil :ok, deleted_user = User.soft_delete(user, TestRepo) refute is_nil(deleted_user.deleted_at) assert User.deleted?(deleted_user) end
test "restore/2 clears deleted_at" do user = %Username: "John", deleted_at: DateTime.utc_now() :ok, restored = User.restore(user, TestRepo) assert is_nil(restored.deleted_at) refute User.deleted?(restored) end endstep = Ecto
case UserRegistration.run(%email: "alice@example.com", name: "Alice") do
:ok, %update_role: user ->
IO.inspect(user, label: "Registered user")
:error, step_name, error, _ctx ->
IO.puts("Failed at step #step_name: #inspect(error)")
end
Key observation: Each step’s result is automatically stored under its step name (:insert_user, :update_role) inside the pipeline context. The named access (ctx.data.insert_user) makes dependencies explicit and traceable.
Uni Ecto Plugin is an Elixir library plugin that adds Uni (a hypothetical or project-specific) integration to Ecto, Elixir’s database wrapper and query generator. It provides adapter-like helpers, custom types, and query conveniences so Ecto schemas and queries work smoothly with Uni-specific data shapes, behaviors, or remote datastore features.
The plugin introduces a set of generic "Behaviors" that external services must conform to. Works identically to insert , but for an existing record
Let's assume you are using the popular triplex library (which implements the uni pattern) or the unifex approach. For this guide, we will use a generic setup based on the uni_ecto_plugin pattern.