Storehouse.Ecto (Storehouse v0.1.0)

Copy Markdown View Source

Files in records, with Ecto: a column of type Storehouse.Ecto.File (or a list of them), files put in a changeset with put_file/4, made permanent when the record is saved, and deleted once nothing has them.

schema "users" do
  field :name, :string
  field :avatar, Storehouse.Ecto.File
end

def changeset(user, attrs, avatar) do
  user
  |> cast(attrs, [:name])
  |> Storehouse.Ecto.put_file(:avatar, avatar, accept: Storehouse.Check.images(), max_bytes: 5_000_000)
end

Temporary, then permanent

A file a person uploads for a form is temporary until the form is saved: store it with cache: true (Storehouse.Live.allow/3 and consume/3, Storehouse.store/2), and its key is under the temporary prefix (Storehouse.cache_prefix/0). When the record is saved, put_file/4 makes it permanent (Storehouse.promote/2): a copy in the store (on S3, in the bucket), in the save's transaction. A form never saved leaves its files under the prefix, where a lifecycle rule of the bucket deletes them:

{"Rules": [{"ID": "storehouse-cache", "Status": "Enabled",
            "Filter": {"Prefix": "cache/"},
            "Expiration": {"Days": 1}}]}

Replaced and deleted files

A file the record no longer has (replaced, removed, or the record deleted) is deleted from the store once the database has the change: insert/3, update/3 and delete/3 save and then delete them. Inside a transaction of yours, use Repo as usual, and delete replaced/2's files after your transaction commits: a rollback would otherwise leave a record whose file is gone.

Summary

Functions

repo.delete/2, then the record's files are deleted from the store. Inside a transaction, they are not: delete files/1's after it commits.

Deletes files from the store (Storehouse.delete/2).

Every file of a record, in its fields of files.

repo.insert/2, as it is: the files are made permanent in its transaction (put_file/4).

Puts a file (a Storehouse.File, a list of them for a list field, or nil to remove it) in the changeset, checked, and made permanent when the record is saved.

The files a saved changeset leaves behind: those its record had in a field of files, and does not have after it. Delete them once the transaction has committed (delete_files/2).

repo.update/2, then the files it replaced or removed are deleted from the store. Inside a transaction, they are not: delete replaced/2's after it commits.

Functions

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

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

repo.delete/2, then the record's files are deleted from the store. Inside a transaction, they are not: delete files/1's after it commits.

delete_files(files, opts \\ [])

@spec delete_files([Storehouse.File.t()], keyword()) :: :ok

Deletes files from the store (Storehouse.delete/2).

files(record)

@spec files(struct()) :: [Storehouse.File.t()]

Every file of a record, in its fields of files.

insert(repo, changeset, opts \\ [])

@spec insert(module(), Ecto.Changeset.t(), keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}

repo.insert/2, as it is: the files are made permanent in its transaction (put_file/4).

put_file(changeset, field, file_or_files, opts \\ [])

@spec put_file(
  Ecto.Changeset.t(),
  atom(),
  Storehouse.File.t() | [Storehouse.File.t()] | nil,
  keyword()
) ::
  Ecto.Changeset.t()

Puts a file (a Storehouse.File, a list of them for a list field, or nil to remove it) in the changeset, checked, and made permanent when the record is saved.

Options

  • :accept - the types the field takes (Storehouse.Check.images()).
  • :max_bytes, :max_pixels - the limits.
  • :adapter - the store, to make it permanent. The default is Storehouse.adapter/0.

A file over a limit, or of a type not taken, is an error of the field (validation: :storehouse, with the :reason).

replaced(changeset, saved)

@spec replaced(Ecto.Changeset.t(), struct()) :: [Storehouse.File.t()]

The files a saved changeset leaves behind: those its record had in a field of files, and does not have after it. Delete them once the transaction has committed (delete_files/2).

update(repo, changeset, opts \\ [])

@spec update(module(), Ecto.Changeset.t(), keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}

repo.update/2, then the files it replaced or removed are deleted from the store. Inside a transaction, they are not: delete replaced/2's after it commits.