Context module for cached variant derivations of Attached.Originals.Original.
A variant is produced on demand from a parent original and a named transform
declared on the parent schema, then cached as an Attached.Variants.Variant
row pointing back at its parent original via original_id.
Custom transformations
Use fn: to bypass the built-in transformer entirely:
attached :report, variants: %{
text: [fn: &__MODULE__.to_text/3, mime_type: "text/plain"]
}
def to_text(input_path, _transforms, output_path) do
System.cmd("pdftotext", [input_path, output_path])
:ok
endThe function receives (input_path, transforms, output_path) and must return
:ok or {:error, reason}. Use named function captures only — anonymous
functions produce non-deterministic variant digests.
mime_type: sets the content type of the stored variant. Defaults to
"image/png" when omitted.
quality: (integer 1–100) sets the encoder quality for the output file.
Honored by jpeg, webp, and gif; ignored for png. Different quality values
produce distinct cached variants.
attached :header_image, variants: %{
medium: [resize_to_limit: {700, 700}, mime_type: "image/webp", quality: 80]
}Querying
This module exposes the standard CRUD shape against attached_variants:
list/1, get/2, get!/2, count/1, paginate/1. See
Attached.Ecto.CRUD for the supported option set.
Attached.Variants.list(order_by: [desc: :inserted_at], limit: 50)
Summary
Functions
Counts variants matching the given options.
Deletes all variants belonging to original — both their storage files and
DB rows.
Fetches a variant by id. Returns nil if not found.
Fetches a variant by id. Raises Ecto.NoResultsError if not found.
Reverse of path_for/2 — given a storage path, returns the matching
variant (or nil if the path doesn't refer to one).
Fetches the variant for original with transform_digest, or nil if none
exists yet.
Returns variants matching the given options.
Paginates variants.
Returns the storage path for variant (a %Variant{} or
{name, transform_digest} pair) belonging to parent.
Returns {:ok, url} for a cached preview image of original, generating
the variant on first call. Returns {:error, reason} when the original is
not previewable or the transform fails.
Returns true when a preview image can be produced for original.
Returns the cached %Variant{} for original with transform_digest,
generating and storing it if it doesn't exist yet.
Deletes a variant and its storage file synchronously.
Returns the deterministic digest for transforms, used as the cache key
in attached_variants.transform_digest.
Returns the transforms configured for variant_name on field of record.
Functions
Counts variants matching the given options.
Accepts the same :query hook as list/1.
Deletes all variants belonging to original — both their storage files and
DB rows.
Idempotent. Called explicitly from Attached.Originals.purge!/1 before the
parent delete so storage gets cleaned (FK cascade would handle the DB
rows but not the files).
Fetches a variant by id. Returns nil if not found.
Supports :preload and :query.
Fetches a variant by id. Raises Ecto.NoResultsError if not found.
Supports :preload and :query.
Reverse of path_for/2 — given a storage path, returns the matching
variant (or nil if the path doesn't refer to one).
Used by Attached.Web.Plug to resolve the variant's content type when
serving a variant URL. Falls back to a LIKE match on the 4-char
transform-digest prefix encoded in the path.
Fetches the variant for original with transform_digest, or nil if none
exists yet.
Returns variants matching the given options.
See Attached.Ecto.CRUD for the supported option set
(:preload, :order_by, :limit, :offset, :select, :distinct,
:exclude_nil, :query).
Paginates variants.
Accepts the same :query/:order_by/:preload/:select options as
list/1, plus:
:page— 1-based page number (default1):per_page— items per page (default25)
Returns %{entries: [...], total: n, page: p, per_page: pp}.
Returns the storage path for variant (a %Variant{} or
{name, transform_digest} pair) belonging to parent.
Variants live under a _variants/ namespace separate from the original
files, so listings and backups can treat derived files independently.
Prefix-based cleanup therefore requires two calls: one for the parent key
and one for the _variants/<parent.key> prefix.
Returns {:ok, url} for a cached preview image of original, generating
the variant on first call. Returns {:error, reason} when the original is
not previewable or the transform fails.
The preview is a small image/png (≤400×400), cached as a variant so
subsequent calls are cheap.
Returns true when a preview image can be produced for original.
Any image/* original is previewable. For other content types an image previewer
must accept the type (which includes a runtime availability check — e.g.
ffmpeg installed for video, pdftoppm for PDF).
Returns the cached %Variant{} for original with transform_digest,
generating and storing it if it doesn't exist yet.
Idempotent — safe to call concurrently; a unique index on
(original_id, transform_digest) prevents duplicate rows.
Deletes a variant and its storage file synchronously.
Accepts a %Variant{} struct (with :original preloaded) or a variant id.
Returns the deterministic digest for transforms, used as the cache key
in attached_variants.transform_digest.
The :variant_name key is excluded from the hash so renaming a variant
does not invalidate its cached original.
Returns the transforms configured for variant_name on field of record.
Raises ArgumentError if the variant is not declared.