Skip to content

♻️ Simple soft deletes for Ecto

License

Notifications You must be signed in to change notification settings

newaperio/trash

Repository files navigation

♻️ Trash

Simple soft deletes for Ecto

Installation

Trash is available on Hex.

This package can be installed by adding trash to your list of dependencies in mix.exs:

def deps do
  [
    {:trash, "~> 0.1.0"}
  ]
end

Usage

Check the documentation for complete details.

Trash helps manage soft-deleting Ecto.Schemas by providing convenience functions to update and query for discarded and kept records.

Terminology

Trash uses a few terms throughout to indicate the state of a record. Here are some quick definitions:

  • Soft-deletion: removing a record by updating an attribute instead of issuing a SQL DELETE
  • Discarded: a record that has been soft-deleted
  • Kept: a record that has not been soft-deleted
  • Restore: reverse a soft-deletion to keep a record

Getting Started

Trash is opt-in on individual Ecto.Schemas. To start marking schemas as trashable, first add the required trashable fields:

mix ecto.gen.migration add_trashable_to_posts
defmodule MyApp.Repo.Migrations.AddTrashableToPosts do
  use Ecto.Migration

  def change do
    alter(table(:posts)) do
      add(:discarded_at, :utc_datetime)
    end

    create(index(:posts, :discarded_at))
  end
end

Then declare the fields on your schema. You can do this manually or use the convenience functions in Trash.Schema:

defmodule MyApp.Posts.Post do
  use Ecto.Schema
  use Trash.Schema

  schema "posts" do
    field(:title, :string)
    trashable_fields()
  end
end

Next, import Trash by using it in your MyApp.Repo.

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

  use Trash.Repo, repo: __MODULE__
end

This generates shorthand functions with the repo implicitly passed. However, it's not required to call use. If preferred you can call the functions directly on Trash.Repo by passing the Ecto.Repo manually. It's a bit more convenient with use, though.

# Shorthand with `use`
MyRepo.all_discarded(Post)

# Long form without
MyRepo.all_discarded(Post, [], MyRepo)

Soft-deleting and Restoring

The functions discard and restore will soft-delete and restore records, respectively.

alias MyApp.Posts
alias MyApp.Repo

post = Posts.get_last_post!

{:ok, post} = Repo.discard(post) # => %Post{discarded_at: %DateTime{}}
post = Repo.restore(post) # => %Post{discarded_at: nil}

These call out to the repo's update function. This means a SQL UPDATE has been issued and the returned schema has updated trashable fields.

These functions also have bang versions, which unwrap the return tuple and raise on error. Note: when passing a struct instead of a changeset, the bang versions of these will never raise an error.

Querying

Trash provides discarded and kept variations of the following Ecto.Repo functions:

  • all
  • exists?
  • get
  • get!
  • get_by
  • get_by!
  • one
  • one!

The variations are postfixed with discarded and kept (with the exception of exists? which is replaced by discarded? and kept?) and modify the passed-in queryable to add a WHERE condition to only return discarded or kept records.

Trash also provides helper where functions that can be used in conjunction with Ecto.Query.

import Ecto.Query
alias MyApp.Posts.Post

from(p in Post) |> Trash.Query.where_discarded() |> Repo.all()

There is also a function that merges in the trashable fields into the select statement to always ensure they are returned. It also hydrates discarded? with a computed SQL value.

import Ecto.Query
alias MyApp.Posts.Post
alias MyApp.Repo

Post
|> Trash.Query.where_discarded()
|> Repo.all()
|> Trash.Query.select_trashable()

Contributing

Contributions are welcome! To make changes, clone the repo, make sure tests pass, and then open a PR on GitHub.

git clone https://github.com/newaperio/trash.git
cd trash
mix deps.get
mix test

License

Trash is Copyright © 2020 NewAperio. It is free software, and may be redistributed under the terms specified in the LICENSE file.

About NewAperio

Trash is built by NewAperio, LLC.

NewAperio is a web and mobile design and development studio. We offer expert Elixir and Phoenix development as part of our portfolio of services. Get in touch to see how our team can help you.