Last Update: 2019-07-15 16:05:48 +0200

Using Attacher

The most convenient way to use Shrine is through the model, using the interface provided by Shrine's attachment module. This way you can interact with the attachment just like with any other column attribute, and adding attachment fields to the form just works.

class Photo < Sequel::Model

However, if you don't want to add additional methods on the model and prefer explicitness, or you need more control, you can achieve the same behaviour using the Shrine::Attacher object, which is what the attachment interface uses under the hood.

attacher =, :image) # equivalent to `photo.image_attacher`
attacher.assign(file)                                 # equivalent to `photo.image = file`
attacher.get                                          # equivalent to `photo.image`

The attacher will use the <attachment>_data attribute for storing information about the attachment.

attacher.data_attribute #=> :image_data


The attacher object exposes the objects it uses:

attacher.record #=> #<Photo>   #=> :image
attacher.cache  #=> #<ImageUploader @storage_key=:cache>  #=> #<ImageUploader @storage_key=:store>

The attacher will automatically use :cache and :store storages, but you can also tell it to use different temporary and permanent storage:, :image, cache: :other_cache, store: :other_store)

# OR

photo.image_attacher(cache: :other_cache, store: :other_store)
photo.image = file # uploads to :other_cache storage         # promotes to :other_store storage

You can pass the :cache and :store options via too:

class Photo < Sequel::Model
  include, cache: :other_cache, store: :other_store)

Note that it's not necessary to use the temporary storage, see the next section for more details.


The #assign method accepts either an IO object to be cached, or an already cached file in form of a JSON string, and assigns the cached result to record's <attachment>_data attribute.

# uploads the `io` object to temporary storage, and writes to the data column

# writes the given cached file to the data column
attacher.assign('{"id":"9260ea09d8effd.jpg","storage":"cache","metadata":{ ... }}')

When assigning an IO object, any additional options passed to #assign will be forwarded to Shrine#upload. This allows you to do things like overriding metadata, setting upload location, or passing upload options:

attacher.assign io,
  metadata:       { "filename" => "myfile.txt" },
  location:       "custom/location",
  upload_options: { acl: "public-read" }

If you're attaching a cached file and want to override its metadata before assignment, you can do it like so:

cached_file = Shrine.uploaded_file('{"id":"9260ea09d8effd.jpg","storage":"cache","metadata":{ ... }}')
cached_file.metadata["filename"] = "myfile.txt"


For security reasons #assign doesn't accept files uploaded to permanent storage, but you can use #set to attach any Shrine::UploadedFile object. You can use this to skip temporary storage altogether and upload files directly to permanent storage:

uploaded_file =!(file) # upload a file directly to permanent storage
attacher.set(uploaded_file)          # attach the uploaded file


The #get method reads record's <attachment>_data attribute, and constructs a Shrine::UploadedFile object from it.

attacher.get #=> #<Shrine::UploadedFile>

The #read method will just return the value of the underlying <attachment>_data attribute. #=> '{"id":"dsg024lfs.jpg","storage":"cache","metadata":{...}}'

In general you can use #uploaded_file to contruct a Shrine::UploadedFile from a JSON string.

attachment_data = '{"id":"dsg024lfs.jpg","storage":"cache","metadata":{...}}'
attacher.uploaded_file(attachment_data) #=> #<Shrine::UploadedFile>


The #url method returns the URL to the attached file, and returns nil if no file is attached.

attacher.url # calls `attacher.get.url`


You can ask the attacher whether the currently attached file is cached or stored.



Whenever a file is assigned via #assign or #set, the file validations are automatically run, and you can access the validation errors through #errors:

attacher.errors #=> ["is larger than 10 MB"]


After the attachment is assigned and you run validations, it should be promoted to permanent storage after the record is saved. You can use #finalize for that, since that will also automatically delete any previously attached files.

# Replaces previous attachment and replaces new

This is normally automatically added to a callback by the ORM plugin when going through the model. Internally this calls #promote, which uploads a given Shrine::UploadedFile to permanent storage, and swaps it with the current attachment, unless a new file was attached in the meanwhile.

# uploads cached file to permanent storage and replaces the current one
attacher.promote(cached_file, action: :custom_name)

The :action parameter is optional; it can be used for triggering a certain processing block, or for additional context during instrumentation.

As a matter of fact, all additional options passed to #promote will be forwarded to Shrine#upload. So unless you're generating versions, you can do things like override metadata, set upload location, or pass upload options:

attacher.promote cached_file,
  metadata:       { "filename" => "myfile.txt" },
  location:       "custom/location",
  upload_options: { acl: "public-read" }

Internally #promote calls #swap, which will update the record with any uploaded file, but will reload the record to check if the current attachment hasn't changed (if the backgrounding plugin is loaded).


Both #promote and #swap are useful for file migrations.


When the backgrounding plugin is loaded, it allows you to promote and delete files in the background, and the corresponding methods are prefixed with _:

Shrine.plugin :backgrounding
Shrine::Attacher.promote { |data| PromoteJob.perform_async(data) }
Shrine::Attacher.delete { |data| DeleteJob.perform_async(data) }
attacher._promote(cached_file)  # calls the registered `Attacher.promote` block
attacher._delete(uploaded_file) # calls the registered `Attacher.delete` block

These are automatically used when using Shrine through models.


The attacher sends #context to each upload/delete call to the uploader. By default it will hold :record and :name:

attacher.context #=>
# {
#   record: #<Photo...>,
#   name:   :image,
# }

However, you can change/add additional context to be sent when calling the uploaders:

attacher.context[:foo] = "bar"

This is useful for example if you have immutable model instances, and you want to assign a new updated instance. For example both foreground and background #promote requires that the record is persisted (and its #id is present).

Uploading and deleting

Normally you can upload and delete directly by using the uploader.

uploader =
uploaded_file = uploader.upload(image) # uploads the file to `:store` storage
uploader.delete(uploaded_file)         # deletes the uploaded file from `:store`

But the attacher also has wrapper methods for uploading and deleting, which also automatically pass in the attacher #context (which includes :record and :name):

attacher.cache!(file) # uploads file to temporary storage
# => #<Shrine::UploadedFile: @data={"storage" => "cache", ...}>!(file) # uploads file to permanent storage
# => #<Shrine::UploadedFile: @data={"storage" => "store", ...}>
attacher.delete!(uploaded_file) # deletes uploaded file from storage

These methods only upload/delete files, they don't write to record's data column. You can also pass additional options for Shrine#upload and Shrine#delete:

attacher.cache!(file, upload_options: { acl: "public-read" })!(file, location: "custom/location")
attacher.delete!(uploaded_file, foo: "bar")