Storehouse.Ecto.Usage (Storehouse v0.2.0)

Copy Markdown View Source

Which records use each file of a library (Storehouse.Ecto.Blob): kept as the records change, so the library can say what each file is used by, and refuse to delete one that is used.

A record says which files it uses now, each time it is saved, in the same transaction: a document whose blocks carry blob ids in JSON, a page with a cover and a gallery.

Repo.transaction(fn ->
  story = Repo.update!(changeset)
  ids = for %{"image_id" => id} <- story.blocks, do: id
  {:ok, _} = Storehouse.Ecto.Usage.record(Repo, story, ids)
  story
end)

A record is named by its table and its primary key, so any schema's records use files, with no column for each. A record deleted uses none: forget/3.

Storehouse.Ecto.Usage.used_by(Repo, blob)
#=> [%{type: "stories", id: "42"}]

Storehouse.Ecto.Usage.delete(Repo, blob)
#=> {:error, {:in_use, [%{type: "stories", id: "42"}]}}

The usage table's foreign key refuses the deletion of a blob that is used in the database itself, so two requests at once never delete a file a record has just started to use.

Every function takes :schema, the blob schema (Storehouse.Ecto.Blob by default); the usage table is the schema's (use Storehouse.Ecto.Blob.Schema, usages: …).

Summary

Types

A record that uses a file: its table and its primary key.

Functions

Deletes blob from the library, and then its file from the store (Storehouse.delete_later/2), unless a record uses it: {:error, {:in_use, records}}, and nothing is deleted.

record uses no file any more: for a record deleted.

Makes blob_ids the files record uses, all of them: those it used and no longer does are let go. Gives {:ok, %{added: n, removed: n}}, or {:error, {:unknown, ids}} for ids that are no blob, and nothing is changed.

The ids of the files record uses.

The records that use blob (a blob, or its id).

Types

user()

@type user() :: %{type: String.t(), id: String.t()}

A record that uses a file: its table and its primary key.

Functions

delete(repo, blob, opts \\ [])

@spec delete(module(), struct(), keyword()) ::
  {:ok, struct()} | {:error, {:in_use, [user()]}} | {:error, Ecto.Changeset.t()}

Deletes blob from the library, and then its file from the store (Storehouse.delete_later/2), unless a record uses it: {:error, {:in_use, records}}, and nothing is deleted.

Inside a transaction of the app's, the file is not deleted: delete it after the transaction commits, as a rollback would bring the row back.

forget(repo, record, opts \\ [])

@spec forget(module(), struct(), keyword()) ::
  {:ok, %{added: 0, removed: non_neg_integer()}}

record uses no file any more: for a record deleted.

record(repo, record, blob_ids, opts \\ [])

@spec record(module(), struct(), [String.t()], keyword()) ::
  {:ok, %{added: non_neg_integer(), removed: non_neg_integer()}}
  | {:error, {:unknown, [String.t()]}}

Makes blob_ids the files record uses, all of them: those it used and no longer does are let go. Gives {:ok, %{added: n, removed: n}}, or {:error, {:unknown, ids}} for ids that are no blob, and nothing is changed.

It does not check whose the files are: check ids from a form or a document with Storehouse.Ecto.Blob.owned/4 first.

used(repo, record, opts \\ [])

@spec used(module(), struct(), keyword()) :: [String.t()]

The ids of the files record uses.

used_by(repo, blob, opts \\ [])

@spec used_by(module(), struct() | String.t(), keyword()) :: [user()]

The records that use blob (a blob, or its id).