—
title: Shrine 3.0.0¶ ↑
This guide covers all the changes in the 3.0.0 version of Shrine. If you’re currently using Shrine 2.x, see {Upgrading to Shrine 3.x}[https://shrinerb.com/docs/upgrading-to-3] for instructions on how to upgrade.
Major features¶ ↑
Derivatives¶ ↑
The new {derivatives}[https://shrinerb.com/docs/plugins/derivatives] plugin has been added for storing additional processed files alongside the main file.
Shrine.plugin :derivatives
class ImageUploader < Shrine Attacher.derivatives_processor do |original| magick = ImageProcessing::MiniMagick.source(original) { large: magick.resize_to_limit!(800, 800), medium: magick.resize_to_limit!(500, 500), small: magick.resize_to_limit!(300, 300), } end end
photo = Photo.new(photo_params) photo.image_derivatives! # creates derivatives photo.save
This is a rewrite of the {versions} plugin, bringing numerous improvements:
- 
processed files are separated from the main file
 
“‘rb photo.image_data #=> # { # “id”: “original.jpg”, # “storage”: “store”, # “metadata”: { … }, # “derivatives”: { # “large”: { “id”: “large.jpg”, “storage”: “store”, “metadata”: { … } }, # “medium”: { “id”: “medium.jpg”, “storage”: “store”, “metadata”: { … } }, # “small”: { “id”: “small.jpg”, “storage”: “store”, “metadata”: { … } } # } # }
photo.image #=>  processing is decoupled from promotion ability to add or remove processed files at any point “‘rb  class ImageUploader <  end   # … sometime later … photo.image_derivatives!(:crop, left: 0, top: 0, width: 300, height: 300)  photo.image_derivatives #=> { large: …, medium: …, small: …, cropped: … }  photo.save  “‘ possibility of uploading processed files to different storage “‘rb  class ImageUploader <  end   photo.image_derivatives!  photo.image_derivatives.storage_key #=> :other_store  “‘ The { The  Several more methods have been added: The new { The new { The new { The { There are several main differences compared to the old implementation: we are in charge of passing the record to the background job we can access the attacher before promotion we can react to errors that caused promotion to abort We can now also register backgrounding hooks on an attacher instance, allowing us to pass additional parameters to the background job: The persistence plugins ( The “atomic” methods use the new { The new { “‘rb  Shrine.storages = { cache: …, store: …, backup: … } Shrine.plugin :mirroring, mirror: { store: :backup }   The new { “‘rb  Shrine.storages = { cache: …, cache_one: …, cache_two: …, store: … } Shrine.plugin :multi_cache, additional_cache: [:cache_one, :cache_two]   The new { Model file assignment can now be configured to upload directly to permanent  storage. New  end  “‘ The  Including a  The attachment data serializer is now configurable (by default  “‘rb  require “oj” # github.com/ohler55/oj Shrine.plugin :column, serializer: Oj  “‘ It’s now possible to pass options to the validate block via the  Validation can now be skipped on assignment by passing  Closing the uploaded file can now be prevented by passing  Uploaded file can now be automatically deleted by passing  New  New  New  The attached file is now parsed and loaded from record column only once,  which can greatly improve performance if the same attached file is being  accessed multiple times. The  The  The  The memory storage from the shrine-memory gem has been merged into core. The  “‘rb  photo.image = { “id” => “…”, “storage” => “cache”, “metadata” => { … } }  ““ An  The temporary storage doesn’t need to be defined anymore if it’s not used. Any changes to  When copying the S3 object to another location, any specified upload options  will now be applied. Deprecation of passing unknown options to  The  The  The  The  The  Callback code from  The  }  “‘ The  The  New  The  The  The  The  The  The  The width & height validators in  Any options passed to  The  The  “‘rb  plugin :pretty_location  # “blogpost/aa357797-5845-451b-8662-08eecdc9f762/image/493g82jf23.jpg” plugin :pretty_location, class_underscore: :true  # “blog_post/aa357797-5845-451b-8662-08eecdc9f762/image/493g82jf23.jpg”  “‘ You can now load multiple persistence plugins simulatenously, and the correct  one will be activated during persistence. The  The  The  The  The deprecated  The  The  If you’re changing the attachment data column directly, you’ll now need to  call  The  The  The  The  The  The  The  The  The  The  The  The  The  The  The  The  The  The options for  The  The  The deprecated  The  The  “‘rb  # previous behaviour  uploaded_file.storage_key #=> “store” # new behaviour  uploaded_file.storage_key #=> :store  “‘ The  The  “‘rb  # this won’t work anymore  def open(id)  # …  end # this is now required  def open(id, **options)  # …  end  “‘ The  The support for  The  The  The  Specifying  Non URI-escaped  The  The deprecated  The  The deprecated  The deprecated  The deprecated  The  The  A custom downloader is now required to raise  The deprecated  The  The deprecated  The derivation block is now evaluated in context of a  The derivation block can now return only  The  The  The source  The  The  Support for legacy  The  The  The deprecated  The deprecated  The deprecated  Passing a Rack uploaded file hash to  The  The  Passing a block when loading the plugin is not supported anymore. The deprecated  The private  Failing to extract dimensions now prints out a warning by default. The private  The  The private  The width & height validators will now raise an exception if  Support for regexes has been dropped for MIME type and extension validators. The deprecated  The plugin will now always prevent deletion of both replaced and destroyed attachments. The  The  The  The  Passing  The rb   photo = Photo.create(image: file) # promote original file to permanent storage   photo.image_derivatives!          # generate derivatives after promotion   photo.save                        # save derivatives data Shrine  Attacher.derivatives_processor :thumbnails do |original|  # …  endAttacher.derivatives_processor :crop do |original, left:, top:, width:, height:|
  vips = ImageProcessing::Vips.source(original)
  { cropped: vips.crop!(left, top, width, height) }
end
 rb  photo.image_derivatives!(:thumbnails)  photo.image_derivatives #=> { large: …, medium: …, small: … }  photo.saveShrine  # specify storage for all derivatives  Attacher.derivatives_storage :other_store# or specify storage per derivative
Attacher.derivatives_storage { |derivative| :other_store }
 rb  photo = Photo.create(image: file)  photo.image.storage_key #=> :storeAttacher redesign¶ ↑
Shrine::Attacher} class has been rewritten and can now be used without models:attacher = Shrine::Attacher.new
attacher.attach(file)
attacher.file #=> #<Shrine::UploadedFile>
Attacher#data, Attacher#load_data, and Attacher.from_data methods have been added for dumping and loading the attached file:# dump attached file into a serializable Hash
data = attacher.data #=> { "id" => "abc123.jpg", "storage" => "store", "metadata" => { ... } }
# initialize attacher from attached file data...
attacher = Shrine::Attacher.from_data(data)
attacher.file #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store metadata={...}>
# ...or load attached file into an existing attacher
attacher = Shrine::Attacher.new
attacher.load_data(data)
attacher.file #=> #<Shrine::UploadedFile>
Attacher#attach – attaches the file directly to permanent storageAttacher#attach_cached – extracted from Attacher#assignAttacher#upload – calls Shrine#upload, passing :record and :name contextAttacher#file – alias for Attacher#getAttacher#cache_key – returns temporary storage key (:cache by default)Attacher#store_key – returns permanent storage key (:store by default)Column¶ ↑
column} plugin adds the ability to serialize attached file data, in format suitable for writing into a database column.Shrine.plugin :column
# dump attached file data into a JSON string
data = attacher.column_data #=> '{"id":"abc123.jpg","storage":"store","metadata":{...}}'
# initialize attacher from attached file data...
attacher = Shrine::Attacher.from_column(data)
attacher.file #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store metadata={...}>
# ...or load attached file into an existing attacher
attacher = Shrine::Attacher.new
attacher.load_column(data)
attacher.file #=> #<Shrine::UploadedFile>
Entity¶ ↑
entity} plugin adds support for immutable structs, which are commonly used with ROM, Hanami and dry-rb.Shrine.plugin :entity
class Photo < Hanami::Entity
  include Shrine::Attachment(:image)
end
photo = Photo.new(image_data: '{"id":"abc123.jpg","storage":"store","metadata":{...}}')
photo.image #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store ...>
Model¶ ↑
model} plugin adds support for mutable structs, which is used by activerecord and sequel plugins.Shrine.plugin :model
class Photo < Struct.new(:image_data)
  include Shrine::Attachment(:image)
end
photo = Photo.new
photo.image = file
photo.image #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:cache ...>
photo.image_data #=> #=> '{"id":"abc123.jpg", "storage":"cache", "metadata":{...}}'
Backgrounding rewrite¶ ↑
backgrounding} plugin has been rewritten for more flexibility and simplicity. The new usage is much more explicit:Shrine.plugin :backgrounding
Shrine::Attacher.promote_block do
  PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
end
Shrine::Attacher.destroy_block do
  DestroyJob.perform_async(self.class.name, data)
end
class PromoteJob
  include Sidekiq::Worker
  def perform(attacher_class, record_class, record.id, name, file_data)
    attacher_class = Object.const_get(attacher_class)
    record         = Object.const_get(record_class).find(record_id) # if using Active Record
    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
    attacher.atomic_promote
  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
    # attachment has changed or the record has been deleted, nothing to do
  end
end
class DestroyJob
  include Sidekiq::Worker
  def perform(attacher_class, data)
    attacher_class = Object.const_get(attacher_class)
    attacher = attacher_class.from_data(data)
    attacher.destroy
  end
end
photo = Photo.new(photo_params)
photo.image_attacher.promote_block do |attacher|
  PromoteJob.perform_async(
    attacher.class.name,
    attacher.record.class.name,
    attacher.record.id,
    attacher.name,
    attacher.file_data,
    current_user.id, # <== parameters from the controller
  )
end
photo.save # will call our instance-level backgrounding hook
Persistence interface¶ ↑
activerecord, sequel) now implement a unified persistence interface:
 
Method 
Description 
 
‘Attacher#persist` 
persists attachment data 
 
‘Attacher#atomic_persist` 
persists attachment data if attachment hasn’t changed 
 
‘Attacher#atomic_promote` 
promotes cached file and atomically persists changes 
atomic_helpers} plugin, and are useful for background jobs. For example, this is how we’d use them to implement metadata extraction in the background in a concurrency-safe way:MetadataJob.perform_async(
  attacher.class.name,
  attacher.record.class.name,
  attacher.record.id,
  attacher.name,
  attacher.file_data,
)
class MetadataJob
  include Sidekiq::Worker
  def perform(attacher_class, record_class, record_id, name, file_data)
    attacher_class = Object.const_get(attacher_class)
    record         = Object.const_get(record_class).find(record_id) # if using Active Record
    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
    attacher.refresh_metadata! # extract metadata
    attacher.atomic_persist    # persist if attachment hasn't changed
  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
    # attachment has changed or record has been deleted, nothing to do
  end
end
Other new plugins¶ ↑
mirroring} plugin has been added for replicating  uploads and deletes to other storages. rb  file = Shrine.upload(io, :store) # uploads to :store and :backup  file.delete # deletes from :store and :backup  “‘multi_cache} plugin has been added for allowing an  attacher to accept files from additional temporary storages. rb  photo.image = { “id” => “…”, “storage” => “cache”, “metadata” => { … } }  photo.image.storage_key #=> :cache  # or  photo.image = { “id” => “…”, “storage” => “cache_one”, “metadata” => { … } }  photo.image.storage_key #=> :cache_one  # or  photo.image = { “id” => “…”, “storage” => “cache_two”, “metadata” => { … } }  photo.image.storage_key #=> :cache_two  “‘form_assign} plugin has been added for assigning  files directly from form params.rb   Shrine.plugin :form_assign   rb   attacher = photo.image_attacher   attacher.form_assign({ "image" => file, "title" => "...", "description" => "..." })   attacher.file #=> #<Shrine::UploadedFile id="..." storage=:cache ...> Other features¶ ↑
rb   Shrine.plugin :model, cache: false   rb   photo.image = file   photo.image.storage_key #=> :store (permanent storage) Shrine.download_response method has been added to the  download_endpoint plugin for generating file response from the controller.rb   Rails.application.routes.draw do     get "/attachments" => "files#download"   end   “‘rb  class FilesController < ApplicationController  def download  # … we can now perform things like authentication here …  set_rack_response Shrine.download_response(env)  endprivate
def set_rack_response((status, headers, body))
  self.status = status
  self.headers.merge!(headers)
  self.response_body = body
end
Attacher#refresh_metadata! method has been added to refresh_metadata  plugin. It refreshes metadata and writes new attached file data back into the  data attribute.rb   attacher.file.refresh_metadata!   attacher.write   # can now be shortened to   attacher.refresh_metadata! Shrine::Attachment module now defines a .<name>_attacher  class method on the target class.rb   class Photo     include ImageUploader::Attachment(:image)   end   rb   Photo.image_attacher #=> #<ImageUploader::Attacher ...> JSON  standard library is used)::validate  option:rb   attacher.assign(file, validate: { foo: "bar" })   rb   class MyUploader < Shrine     Attacher.validate do |**options|       options #=> { foo: "bar" }     end   end validate: false.rb   attacher.attach(file, validate: false) # skip validation close: false to  Shrine#upload.delete: true to  Shrine#upload.Attacher#file! method has been added for retrieving the attached file  and raising and exception if it doesn’t exist.Derivation#opened method has been added for retrieving an opened  derivative in derivation_endpoint plugin.Storage#delete_prefixed method has been added for deleting all files  in specified directory.rb   storage.delete_prefixed("some_directory/") Performance improvements¶ ↑
rb   photo = Photo.find(photo_id)   photo.image # parses and loads attached file   photo.image # returns memoized attached file S3#open method doesn’t perform a #head_obect request anymore.derivation_endpoint plugin doesn’t perform a Storage#exists? call  anymore when :upload is enabled.download_endpoint plugin doesn’t perform a Storage#exists? call  anymore.Other Improvements¶ ↑
Core improvements¶ ↑
Shrine now works again with MRI 2.3.rb   # Gemfile   gem "shrine-memory" # this can be removed Attacher#assign method now accepts cached file data as a Hash.UploadedFile object can now be initialized with symbol keys.rb   Shrine.uploaded_file(id: "...", storage: :store, metadata: { ... }) Shrine.storages will now be applied to existing Shrine and  Attacher instances.FileSystem#open has been  reverted. This allows users to continue using FileSystem storage in tests  as a mock storage.Shrine#upload method now infers file extension from filename metadata,  making possible to use filename to specify file extension.rb   file = uploader.upload(StringIO.new("some text"), metadata: { "filename" => "file.txt" })   file.id #=> "2a2467ee6acbc5cb.txt" Shrine.opts hash is now deep-copied on subclassing. This allows plugins  to freely mutate hashes and arrays in Shrine.opts, knowing they won’t be  shared across subclasses.down dependency has been updated to ~> 5.0.Shrine::Attachment[] method has been added as an alternative syntax for  creating attachment modules.rb   class Photo     include ImageUploader::Attachment[:image]   end Plugin improvements¶ ↑
activerecord plugin now works with Active Record 3.activerecord and sequel plugin has been moved into  attacher methods, allowing the user to override them.Attacher#(activerecord|sequel)_before_saveAttacher#(activerecord|sequel)_after_saveAttacher#(activerecord|sequel)_after_destroyurl_options plugin now allows you to override URL options by deleting  them.rb   uploaded_file.url(response_content_disposition: "attachment")   “‘rb  plugin :url_options, store: -> (io, options) {  disposition = options.delete(:response_content_disposition, “inline”){
  response_content_disposition: ContentDisposition.format(
    disposition: disposition,
    filename:    io.original_filename,
  )
}
:upload_options hash passed to the uploader is now merged with any  options defined with the upload_options plugin.default_storage now evaluates the storage block in context of the  Attacher instance.Attacher.default_cache and Attacher.default_store methods have been  added to the default_storage plugin for declaratively setting default  storage.rb   Attacher.default_cache { ... }   Attacher.default_store { ... } derivation_endpoint plugin now handles string derivation names.Derivation#upload method from derivation_endpoint plugin now accepts  additional uploader optionsDerivation#upload method from derivation_endpoint plugin now accepts  any IO-like object.derivation_endpoint plugin doesn’t re-open File objects returned in  derivation block anymore.instrumentation plugin now instruments UploadedFile#open calls as a  new open.shrine event. UploadedFile#download is still instrumented as  download.shrine.upload.shrine event now has :metadata on the top level in  instrumentation plugin.store_dimensions plugin don’t require  UploadedFile#width and UploadedFile#height methods to be defined anymore,  only that the corresponding metadata exists.Attacher#attach_cached are now forwarded to metadata  extraction when restore_cached_data plugin is loaded.infer_extension plugin now works correctly with pretty_location  plugin when pretty_location was loaded after infer_extension.pretty_location plugin now accepts :class_underscore option for  underscoring class names.Backwards compatibility¶ ↑
Plugin deprecation and removal¶ ↑
backgrounding plugin has been rewritten and has a new API. While the  new API works in a similar way, no backwards compatibility has been kept with  the previous API.versions, processing, recache, and delete_raw plugins have been  deprecated in favor of the new derivatives plugin.module_include plugin has been deprecated over overriding core classes  directly.hooks, parallelize, parsed_json, and delete_promoted plugins have  been removed.copy, backup, multi_delete, moving, logging,  direct_upload background_helpers, and migration_helpers plugins have  been removed.default_url_options plugin has been renamed to url_options.Attacher API¶ ↑
Attacher.new method now only accepts a hash of options, use  Attacher.from_model for initializing from a model.Attacher#reload to make the attacher reflect those changes.Attacher#promote method now only saves the promoted file in memory,  it doesn’t persist the changes.Attacher#_set and Attacher#set methods have been renamed to  Attacher#set and Attacher#changed.Attacher#cache!, Attacher#store!, and Attacher#delete! methods have  been removed.Attacher#_promote and Attacher#_delete methods have been removed.Attacher#swap, Attacher#update, Attacher#read, Attacher#write,  Attacher#convert_to_data, Attacher#convert_before_write, and  Attache#convert_after_read methods have been removed.Attacher.validate, Attacher#validate and Attacher#errors methods  have been extracted into the new validation plugin.Attacher#data_attribute method has been renamed to Attacher#attribute.Attacher#replace method has been renamed to  Attacher#destroy_previous.Attacher#assign method now raises an exception when non-cached uploaded  file is assigned.Attacher#attached? method now returns whether a file is attached,  regardless of whether it was changed or not.Attachment API¶ ↑
Shrine::Attachment module doesn’t define any instance methods by itself  anymore. This has been moved into entity and model plugins.Uploader API¶ ↑
:phase option has been removed.Shrine#process and Shrine#processed methods have been removed.Shrine#store, Shrine#_store, Shrine#put, and Shrine#copy methods  have been removed.Shrine#delete, Shrine#_delete, and Shrine#remove methods have been  removed.Shrine#uploaded? method has been removed.Shrine.uploaded_file method doesn’t yield files anymore by default.Shrine#upload and Shrine#extract_metadata are now  required to have symbol keys.Shrine.uploaded_file method now raises ArgumentError on invalid  arguments.Shrine#upload method doesn’t rescue exceptions that happen in  IO#close anymore.Shrine::IO_METHODS constant has been removed.Uploaded File API¶ ↑
UploadedFile method now extracts data from the given hash on  initialization, it doesn’t store the whole hash anymore. This means the  following potential code won’t work anymore:rb   uploaded_file.id #=> "foo"   uploaded_file.data["id"] = "bar"   uploaded_file.id #=> "foo" UploadedFile#storage_key method now returns a Symbol instead of a  String.UploadedFile#== method now requires both uploaded file objects to be  of the same class.Storage API¶ ↑
Storage#open method is now required to accept additional options.Storage#open method is now required to raise Shrine::FileNotFound  exception when the file is missing.S3 API¶ ↑
aws-sdk 2.x and aws-sdk-s3 < 1.14 has been removed.S3#open now raises Shrine::FileNotFound exception when S3 object doesn’t  exist on the bucket.S3#upload method will now override any S3 object data when copying  from another S3 object. If you were relying on S3 object data being inherited  on copy, you will need to update your code.:host option in S3#initialize has been removed.:download option in S3#url has been removed.:multipart_threshold as an integer is not supported anymore.:content_disposition and :response_content_disposition  values are not supported anymore.S3#presign method now returns a hash instead of an object for default  POST method.S3#stream, S3#download, and S3#s3 methods have been  removed.S3#open method doesn’t include an :object in Down::ChunkedIO#data  anymore.FileSystem API¶ ↑
FileSystem#open now raises Shrine::FileNotFound exception when file does  not exist on the filesystem.:host option in FileSystem#initialize has been removed.:older_than option in FileSystem#clear! has been removed.FileSystem#download method has been removed.FileSystem#movable? and FileSystem#move methods have been made  private.FileSystem#open method doesn’t accept a block anymore.Plugins API¶ ↑
remote_urlShrine::Plugins::RemoteUrl::DownloadError in order for the exception to be converted into a validation error. Down::NotFound and Down::TooLarge exceptions are converted by default. All other exceptions are propagated.upload_endpointShrine::Plugins::UploadEndpoint::App constant has been removed.:request option holding the Rack::Request object isn’t passed to the uploader anymore.presign_endpointStorage#presign results that cannot coerce themselves into a Hash are not supported anymore.Shrine::Plugins::PresignEndpoint::App constant has been removed.derivation_endpointShrine::Derivation instance.File and Tempfile objects.:download_errors option has been removed, as it’s now obsolete.:include_uploaded_file option has been removed, as it’s now obsolete.UploadedFile is not passed to the derivation block anymore on download: false.Derivation#upload method now closes the uploaded file.derivation.upload instrumentation event payload now includes only :derivation key.download_endpoint/:storage/:id URLs has been dropped.:storages plugin option has been removed.Shrine::Plugins::DownloadEndpoint::App constant has been removed.data_uri:filename plugin option has been removed.Shrine::Plugins::DataUri::DataFile constant has been removed.rack_fileShrine::Plugins::RackFile::UploadedFile constant has been removed.Shrine#upload is not supported anymore.cached_attachment_data#<name>_cached_data= model method has been removed.Attacher#read_cached method has been renamed to Attacher#cached_data.default_urldetermine_mime_type:default analyzer alias has been removed.Shrine#mime_type_analyzers method has been removed.store_dimensionsShrine#extract_dimensions and Shrine#dimensions_analyzers methods have been removed.infer_extension:mini_mime inferrer is now the default inferrer, which requires the mini_mime gem.Shrine#infer_extension method has been removed.validation_helperswidth or height metadata is missing.versions:version_names, Shrine.version_names and Shrine.version? have been removed from versions plugin.keep_files:replaced and :destroyed options don’t have effect anymore.dynamic_storageShrine.dynamic_storages method has been removed.instrumentation:location, :upload_options, and :metadata keys have been removed from :options in upload.shrine event payload.:metadata key has been removed from metadata.shrine event payload.default_storagerecord & name arguments to the storage block has been deprecated over evaluating the block in context of the attacher instance.sequel:callbacks plugin option has been renamed to :hooks.