—
title: Using Attacher¶ ↑
This guide explains what is Shrine::Attacher and how to use it.
Introduction¶ ↑
The attachment logic is handled by a Shrine::Attacher object. The Shrine::Attachment module simply provides a convenience layer around a Shrine::Attacher object, which can be accessed via the #<name>_attacher attribute.
class Photo include ImageUploader::Attachment(:image) end
photo = Photo.new photo.image_attacher #=> #<ImageUploader::Attacher>
We can also instantiate the same Shrine::Attacher object directly:
attacher = ImageUploader::Attacher.from_model(photo, :image) attacher.file # called by `photo.image` attacher.url # called by `photo.image_url`
The {model}, {entity}, and {column} plugins provide additional Shrine::Attacher methods (such as Shrine::Attacher.from_model we see above), but in this guide we’ll focus only on the core Shrine::Attacher methods.
So, we’ll assume a Shrine::Attacher object not backed by any model/entity:
attacher = Shrine::Attacher.new
Storage¶ ↑
By default, an Attacher will use the :cache storage as the temporary storage, and the :store storage as the permanent storage.
Shrine.storages = { cache: Shrine::Storage::Memory.new, store: Shrine::Storage::Memory.new, }
attacher = Shrine::Attacher.new attacher.cache_key #=> :cache attacher.store_key #=> :store
We can also change the default storage:
attacher = Shrine::Attacher.new(cache: :other_cache, store: :other_store) attacher.cache_key #=> :other_cache attacher.store_key #=> :other_store
You can also change default attacher options on the Shrine::Attachment module:
class Photo include ImageUploader::Attachment(:image, cache: :other_cache, store: :other_store) end
The Attacher#cache and Attacher#store methods will retrieve corresponding uploaders:
attacher.cache #=> #<MyUploader @storage_key=:cache> attacher.store #=> #<MyUploader @storage_key=:store>
Attaching¶ ↑
Attaching cached¶ ↑
For attaching files submitted via a web form, Attacher#assign can be used:
attacher.assign(file)
If given a raw file, it will upload it to temporary storage:
attacher.assign(file) attacher.file #=> #<Shrine::UploadedFile id="asdf.jpg" storage=:cache ...>
If given cached file data (JSON or Hash), it will set the cached file:
attacher.assign('{"id":"asdf.jpg","storage":"cache","metadata":{...}}') attacher.file #=> #<Shrine::UploadedFile id="asdf.jpg" storage=:cache ...>
If given an empty string, it will no-op:
attacher.assign("") # no-op
If given nil, it will clear the attached file:
attacher.file #=> <Shrine::UploadedFile> attacher.assign(nil) attacher.file #=> nil
This plays nicely with the recommended HTML form fields for the attachment. If you’re not using the hidden form field (and therefore don’t need empty strings to be handled), you can also use Attacher#attach_cached:
# uploads file to cache
attacher.attach_cached(file)
# sets cached file
attacher.attach_cached('{"id":"asdf.jpg","storage":"cache","metadata":{...}}')
attacher.attach_cached({ "id" => "asdf.jpg", "storage" => "cache", "metadata" => { ... } })
attacher.attach_cached({ id: "asdf.jpg", storage: "cache", metadata: { ... } })
# unsets attached file
attacher.attach_cached(nil)
Attaching stored¶ ↑
The Attacher#attach method uploads a given file to permanent storage:
attacher.attach(file) attacher.file #=> #<Shrine::UploadedFile id="asdf.jpg" storage=:store ...>
This method is useful when attaching files from scripts, where validation doesn’t need to be performed, and where temporary storage can be skipped.
You can specify a different destination storage with the :storage option:
attacher.attach(file, storage: :other_store) attacher.file #=> #<Shrine::UploadedFile id="asdf.jpg" storage=:other_store ...>
Any additional options passed to Attacher#attach, Attacher#attach_cached and Attacher#assign are forwarded to the uploader:
attacher.attach(file, metadata: { "foo" => "bar" }) # adding metadata attacher.attach(file, upload_options: { acl: "private" }) # setting upload options attacher.attach(file, location: "path/to/file") # setting upload location
Uploading¶ ↑
If you want to upload a file to without attaching it, you can use Attacher#upload:
attacher.upload(file) #=> #<Shrine::UploadedFile storage=:store ...> attacher.upload(file, :cache) #=> #<Shrine::UploadedFile storage=:cache ...> attacher.upload(file, :other_store) #=> #<Shrine::UploadedFile storage=:other_store ...>
This is useful if you want to attacher context such as :record and :name to be automatically passed to the uploader.
You can also pass additional options for Shrine#upload:
attacher.upload(file, metadata: { "foo" => "bar" }) # adding metadata attacher.upload(file, upload_options: { acl: "private" }) # setting upload options attacher.upload(file, location: "path/to/file") # setting upload location
Changes¶ ↑
When a new file is attached, calling {Attacher#finalize} will perform additional actions such as promotion and deleting any previous file. It will also trigger validation.
You can check whether a new file has been attached with Attacher#changed?:
attacher.changed? #=> true
You can use Attacher#change to attach an UploadedFile object as is:
uploaded_file #=> #<Shrine::UploadedFile id="foo" ...> attacher.change(uploaded_file) attacher.file #=> #<Shrine::UploadedFile id="foo" ...> attacher.changed? #=> true
If you want to attach a file without triggering dirty tracking or validation, you can use Attacher#set:
uploaded_file #=> #<Shrine::UploadedFile id="foo" ...> attacher.set(uploaded_file) attacher.file #=> #<Shrine::UploadedFile id="foo" ...> attacher.changed? #=> false
Finalizing¶ ↑
After the file is attached (with Attacher#assign, Attacher#attach_cached, or Attacher#attach), and data has been validated, the attachment can be “finalized”:
attacher.finalize
The Attacher#finalize method performs promoting and replacing. It also clears dirty tracking:
attacher.changed? #=> true attacher.finalize attacher.changed? #=> false
Promoting¶ ↑
Attacher#finalize checks if the attached file has been uploaded to temporary storage, and in this case uploads it to permanent storage.
attacher.attach_cached(io) attacher.finalize # uploads attached file to permanent storage attacher.file #=> #<Shrine::UploadedFile storage=:store ...>
Internally it calls Attacher#promote_cached, which you can call directly if you want to pass any promote options:
attacher.file #=> #<Shrine::UploadedFile storage=:cache ...> attacher.promote_cached # uploads attached file to permanent storage if new and cached attacher.file #=> #<Shrine::UploadedFile storage=:store ...>
You can also call Attacher#promote if you want to upload attached file to permanent storage, regardless of whether it’s cached or newly attached:
attacher.promote
Any options passed to Attacher#promote_cached or Attacher#promote will be forwarded to Shrine#upload.
Replacing¶ ↑
Attacher#finalize also deletes the previous attached file if any:
previous_file = attacher.file attacher.attach(io) attacher.finalize previous_file.exists? #=> false
Internally it calls Attacher#destroy_previous to do this:
attacher.destroy_previous
Retrieving¶ ↑
File¶ ↑
The Attacher#file is used to retrieve the attached file:
attacher.file #=> #<Shrine::UploadedFile>
If no file is attached, Attacher#file returns nil:
attacher.file #=> nil
If you want to assert a file is attached, you can use Attacher#file!:
attacher.file! #~> Shrine::Error: no file is attached
Attached¶ ↑
You can also check whether a file is attached with Attacher#attached?:
attacher.attached? # returns whether file is attached
If you want to check to which storage a file is uploaded to, you can use Attacher#cached? and Attacher#stored?:
attacher.attach(io) attacher.stored? #=> true (checks current file) attacher.stored?(attacher.file) #=> true (checks given file)
attacher.attach_cached(io) attacher.cached? #=> true (checks current file) attacher.cached?(attacher.file) #=> true (checks given file)
URL¶ ↑
The attached file URL can be retrieved with Attacher#url:
attacher.url #=> "https://example.com/file.jpg"
If no file is attached, Attacher#url returns nil:
attacher.url #=> nil
Data¶ ↑
You can retrieve plain attached file data with Attacher#data:
attacher.data #=> # { # "id" => "abc123.jpg", # "storage" => "store", # "metadata" => { # "size" => 223984, # "filename" => "nature.jpg", # "mime_type" => "image/jpeg", # } # }
This data can be stored somewhere, and later the attached file can be loaded from it:
# new attacher attacher = Shrine::Attacher.from_data(data) attacher.file #=> #<Shrine::UploadedFile> # existing attacher attacher.file #=> nil attacher.load_data(data) attacher.file #=> #<Shrine::UploadedFile>
Internally Attacher#uploaded_file is used to convert uploaded file data into a Shrine::UploadedFile object:
attacher.uploaded_file("id" => "...", "storage" => "...", "metadata" => { ... }) #=> #<Shrine::UploadedFile>
attacher.uploaded_file(id: "...", storage: "...", metadata: { ... }) #=> #<Shrine::UploadedFile>
attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}') #=> #<Shrine::UploadedFile>
You will likely want to use a higher level abstraction for saving and loading this data, see {column}, {entity} and {model} plugins for more details.
Deleting¶ ↑
The attached file can be deleted via Attacher#destroy_attached:
attacher.destroy_attached
This will not delete cached files, to not interrupt any potential backgrounding that might be in process.
If you want to delete the attached file regardless of storage it’s uploaded to, you can use Attacher#destroy:
attacher.destroy
Context¶ ↑
The Attacher#context hash is automatically forwarded to the uploader on Attacher#upload. When {model} or {entity} plugin is loaded, this will include :record and :name values:
attacher = Shrine::Attacher.from_model(photo, :image) attacher.context #=> { record: #<Photo>, name: :image }
You can add here any other parameters you want to forward to the uploader:
attacher.context[:foo] = "bar"
However, it’s generally better practice to pass uploader options directly to Attacher#assign, Attacher#attach, Attacher#promote or any other method that’s calling Attacher#upload.