Last Update: 2018-04-25 02:09:49 +0200

Shrine for Refile Users

This guide is aimed at helping Refile users transition to Shrine, and it consists of three parts:

  1. Explanation of the key differences in design between Refile and Shrine

  2. Instructions how to migrate and existing app that uses Refile to Shrine

  3. Extensive reference of Refile's interface with Shrine equivalents


Shrine borrows many great concepts from Refile: Refile's “backends” are here named “storages”, it uses the same IO abstraction for uploading and representing uploaded files, similar attachment logic, and direct uploads are also supported.

While in Refile you work with storages directly, Shrine uses uploaders which act as wrappers around storages:

storage = Shrine.storages[:store]
storage #=> #<Shrine::Storage::S3 ...>

uploader =
uploader         #=> #<Shrine @storage_key=:store @storage=#<Shrine::Storage::S3>> #=> #<Shrine::Storage::S3 ...>

uploaded_file = uploader.upload(image)
uploaded_file #=> #<Shrine::UploadedFile>

This way Shrine can perform tasks like generating location, extracting metadata, processing, and logging, which are all storage-agnostic, and leave storages to deal only with actual file storage. And these tasks can be configured differently depending on the types of files you're uploading:

class ImageUploader < Shrine
  add_metadata :exif do |io, context|
class VideoUploader < Shrine
  add_metadata :duration do |io, context|


Refile implements on-the-fly processing, serving all files through the Rack endpoint. However, it doesn't offer any abilities for processing on upload. Shrine, on the other hand, generates URLs to specific storages and offers processing on upload (like CarrierWave and Paperclip), but doesn't support on-the-fly processing.

The reason for this decision is that an image server is a completely separate responsibility, and it's better to use any of the generic services for on-the-fly processing. Shrine already has integrations for many such services: shrine-cloudinary, shrine-imgix, and shrine-uploadcare. There is even an open-source solution, Attache, which you can also use with Shrine.

This is how you would process multiple versions in Shrine:

require "image_processing/mini_magick"

class ImageUploader < Shrine
  plugin :processing
  plugin :versions

  process(:store) do |io, context|
    original =
    pipeline = ImageProcessing::MiniMagick.source(original)

    size_800 = pipeline.resize_to_limit!(800, 800)
    size_500 = pipeline.resize_to_limit!(500, 500)
    size_300 = pipeline.resize_to_limit!(300, 300)


    { original: io, large: size_800, medium: size_500, small: size_300 }


While Refile serves all files through the Rack endpoint mounted in your app, Shrine serves files directly from storage services:

Refile.attachment_url(@photo, :image) #=> "/attachments/cache/50dfl833lfs0gfh.jpg"
@photo.image.url #=> ""

If you're using storage which don't expose files over URL (e.g. a database storage), or you want to secure your downloads, you can also serve files through your app using the download_endpoint plugin.


While in Refile you configure attachments by passing options to .attachment, in Shrine you define all your uploading logic inside uploaders, and then generate an attachment module with that uploader which is included into the model:

class Photo < Sequel::Model
  extend Shrine::Sequel::Attachment
  attachment :image, destroy: false
class ImageUploader < Shrine
  plugin :sequel
  plugin :keep_files, destroyed: true

class Photo < Sequel::Model

This way we can encapsulate all attachment logic inside a class and share it between different models.


Refile allows you to save additional metadata about uploaded files in additional columns, so you can define <attachment>_filename, <attachment>_content_type, or <attachment>_size.

Shrine, on the other hand, saves all metadata into a single <attachment>_data column:

photo.image_data #=>
# {
#   "storage" => "store",
#   "id" => "photo/1/image/0d9o8dk42.png",
#   "metadata" => {
#     "filename"  => "nature.png",
#     "size"      => 49349138,
#     "mime_type" => "image/png"
#   }
# }

photo.image.original_filename #=> "nature.png"
photo.image.size              #=> 49349138
photo.image.mime_type         #=> "image/png"

By default “filename”, “size” and “mime_type” is stored, but you can also store image dimensions, or define any other custom metadata. This also allow storages to add their own metadata.


In Refile you define validations by passing options to .attachment, while in Shrine you define validations on the instance-level, which allows them to be dynamic:

class Photo < Sequel::Model
  attachment :image,
    extension: %w[jpg jpeg png gif],
    content_type: %w[image/jpeg image/png image/gif]
class ImageUploader < Shrine
  plugin :validation_helpers

  Attacher.validate do
    validate_extension_inclusion %w[jpg jpeg png gif]
    validate_mime_type_inclusion %w[image/jpeg image/png image/gif]
    validate_max_size 10*1024*1024 unless record.admin?

Refile extracts the MIME type from the file extension, which means it can easily be spoofed (just give a PHP file a .jpg extension). Shrine has the determine_mime_type plugin for determining MIME type from file content.

Multiple uploads

Shrine doesn't have a built-in solution for accepting multiple uploads, but it's actually very easy to do manually, see the demo app on how you can do multiple uploads directly to S3.

Direct uploads

Shrine borrows Refile's idea of direct uploads, and ships with upload_endpoint and presign_endpoint plugins which provide endpoints for uploading files and generating presigns.

Shrine.plugin :upload_endpoint
Shrine.upload_endpoint(:cache) # Rack app that uploads files to specified storage

Shrine.plugin :upload_endpoint
Shrine.presign_endpoint(:cache) # Rack app that generates presigns for specified storage

Unlike Refile, Shrine doesn't ship with complete JavaScript which you can just include to make it work. However, Uppy is an excellent JavaScript file upload library that integrates wonderfully with Shrine, see the demo app for a complete example.

Migrating from Refile

You have an existing app using Refile and you want to transfer it to Shrine. Let's assume we have a Photo model with the “image” attachment. First we need to create the image_data column for Shrine:

add_column :photos, :image_data, :text

Afterwards we need to make new uploads write to the image_data column. This can be done by including the below module to all models that have Refile attachments:

module RefileShrineSynchronization
  def write_shrine_data(name)
    if read_attribute("#{name}_id").present?
      data = {
        storage: :store,
        id: send("#{name}_id"),
        metadata: {
          size: (send("#{name}_size") if respond_to?("#{name}_size")),
          filename: (send("#{name}_filename") if respond_to?("#{name}_filename")),
          mime_type: (send("#{name}_content_type") if respond_to?("#{name}_content_type")),

      write_attribute(:"#{name}_data", data.to_json)
      write_attribute(:"#{name}_data", nil)
class Photo < ActiveRecord::Base
  attachment :image
  include RefileShrineSynchronization

  before_save do
    write_shrine_data(:image) if changes.key?(:image_id)

After you deploy this code, the image_data column should now be successfully synchronized with new attachments. Next step is to run a script which writes all existing Refile attachments to image_data:

Photo.find_each do |photo|

Now you should be able to rewrite your application so that it uses Shrine instead of Refile, using equivalent Shrine storages. For help with translating the code from Refile to Shrine, you can consult the reference below.

Refile to Shrine direct mapping


.cache, .store, .backends

Shrine calles these “storages”, and it doesn't have special accessors for :cache and :store:

Shrine.storages = {

.app, .mount_point, .automount

The upload_endpoint and presign_endpoint plugins provide methods for generating Rack apps, but you need to mount them explicitly:

# config/routes.rb
Rails.application.routes.draw do
  # adds `POST /images/upload` endpoint
  mount ImageUploader.upload_endpoint(:cache) => "/images/upload"


The Shrine.upload_endpoint and Shrine.presign_endpoint require you to specify the storage that will be used.


Shrine.plugin :logging

.processors, .processor

class MyUploader < Shrine
  plugin :processing

  process(:store) do |io, context|
    # ...


In Shrine validations are done by calling .validate on the attacher class:

class MyUploader < Shrine
  plugin :validation_helpers

  Attacher.validate do
    validate_max_size 5*1024*1024

.extract_filename, .extract_content_type

In Shrine equivalents are (private) methods Shrine#extract_filename and Shrine#extract_mime_type.


You should use your framework to generate the URL to your mounted direct enpdoint.

.attachment_url, .file_url

You can call #url on the uploaded file, or #<name>_url on the model. Additionally you can use the download_endpoint plugin.

.upload_url, .attachment_upload_url, .presign_url, .attachment_presign_url

These should be generated directly by you, it depends on where you've mounted the direct endpoint.

.host, .cdn_host, .app_host, .allow_downloads_from, allow_origin, .content_max_age

Not needed since Shrine doesn't offer on-the-fly processing.

.secret_key, .token, .valid_token?

Not needed since Shrine doesn't offer on-the-fly processing.


Shrine's equivalent to calling the attachment is including an attachment module of an uploader:

class User

:extension, :content_type, :type

In Shrine validations are done instance-level inside the uploader, most commonly with the validation_helpers plugin:

class ImageUploader < Shrine
  plugin :validation_helpers

  Attacher.validate do
    validate_extension_inclusion %w[jpg jpeg png]
    validate_mime_type_inclusion %w[image/jpeg image/png]

:cache, :store

Shrine provides a default_storage plugin for setting custom storages on the uploader:

Shrine.storages[:custom_cache] =*args)
Shrine.storages[:custom_store] =*args)
class ImageUploader < Shrine
  plugin :default_storage, cache: :custom_cache, store: :custom_store


No equivalent currently exists in Shrine.


No equivalent in Shrine, but take a look at the “Multiple Files” guide.

Form helpers


The following Refile code

form_for @user do |form|
  form.attachment_field :profile_image

is equivalent to the following Shrine code

Shrine.plugin :cached_attachment_data
form_for @user do |form|
  form.hidden_field :profile_image, value: @user.cached_profile_image_data
  form.file_field :profile_image

Model methods


Shrine comes with a remove_attachment plugin which adds the same #remove_<attachment> method to the model.

Shrine.plugin :remove_attachment
form_for @user do |form|
  form.hidden_field :profile_image, value: @user.cached_profile_image_data
  form.file_field :profile_image
  form.check_box :remove_profile_image


Shrine comes with a remote_url plugin which adds the same #<attachment>_remote_url method to the model.

Shrine.plugin :remote_url
form_for @user do |form|
  form.hidden_field :profile_image, value: @user.cached_profile_image_data
  form.file_field :profile_image
  form.text_field :profile_image_remote_url