# `Storehouse.Ecto`
[🔗](https://github.com/hattoriai/storehouse/blob/v0.1.0/lib/storehouse/ecto.ex#L2)

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.

# `delete`

```elixir
@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`

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

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

# `files`

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

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

# `insert`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
