Shrine 3.0 Released

I’ve just released version 3.0 of Shrine, a gem for handling file attachments in Ruby applications. It’s been months of hard work, but I feel it’s finally ready.

Redesigned website

The old Jekyll website has been rewritten to use Docusaurus. :sparkles:

Shrine new website

Docusaurus gives us nice features such as sidebars with autogenerated TOC and related documents, which greatly improve navigation experience.

Shrine website sidebars

We also now have a snippet switcher, which makes it easier to show code for different libraries.

Shrine website snippet switcher A Shrine website snippet switcher B Shrine website snippet switcher C

Last but not least, we now have a documentation search bar powered by Algolia DocSearch.

Shrine website search

Major features

I’ve talked about the major changes in more detail Upcoming Features in Shrine 3.0, but I’ll give a brief overview here.

Attacher redesign

The Shrine::Attacher class has been decoupled from the Active Record pattern. It now supports handling attachments for immutable structs (which are commonly used in Hanami, ROM, and dry-rb):

class Photo < Hanami::Entity # immutable struct
  include ImageUploader::Attachment(:image)
end
photo = photo_repository.find(photo_id)
photo.image #=> #<Shrine::UploadedFile>

attacher = photo.image_attacher
attacher.attach(file) # attach new file

photo_repository.update(photo_id, { image_data: attacher.column_data })

It can also be used standalone:

attacher = ImageUploader::Attacher.new
attacher.attach(file)
attacher.file #=> #<Shrine::UploadedFile>
attacher.url  #=> "https://my-bucket.s3.amazonaws.com/path/to/image.jpg"

Versions rewrite

The versions plugin is used for storing processed files alongside the main file. Its implementation had many limitations, such as being coupled to the attachment flow, mixing processed files and original file together, not being able to specify different storage for processed files etc.

The new derivatives plugin is a rewrite of versions, offering an explicit interface and the much needed flexibility.

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

Processing is now triggered explicitly:

photo = Photo.new(image: file)
photo.image_derivatives! # calls derivatives processor
photo.save

And processed data is saved separately from the original file:

photo.image             #=> #<Shrine::UploadedFile @id="30733d04faceec5b.jpg" ...>
photo.image_derivatives #=>
# { large:  #<Shrine::UploadedFile @id="86500abe387b20b1.jpg" ...>,
#   medium: #<Shrine::UploadedFile @id="9e46ffbcca548290.jpg" ...>,
#   small:  #<Shrine::UploadedFile @id="f80fb04c3627c5a5.jpg" ...>, }

Mirroring

The mirroring plugin has been added, which allows replicating uploads and deletes to additional storage services. It’s inspired by Active Storage and its mirror service.

Shrine.storages = {
  cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
  store: Shrine::Storage::S3.new(**s3_options),
  gcs:   Shrine::Storage::GoogleCloudStorage.new(**gcs_options),
}

Shrine.plugin :mirroring, mirror: { store: :gcs }
photo = Photo.create(image: file) # uploads main file to S3 and GCS
photo.image_derivatives!          # uploads processed files to S3 and GCS
photo.destroy                     # deletes files from S3 and GCS

You can also separate the mirroring operations into background jobs, so that they don’t slow down the attachment flow.

Changes

For the full list of changes, see the release notes. If you’re currently on Shrine 2.x, there is a detailed guide for upgrading to Shrine 3.x.

Plugins

The following plugins have been added :gift::

The following plugins have been deprecated :hand::

  • versions
  • processing
  • recache
  • delete_raw
  • module_include

The following plugins have been removed :scissors::

  • logging
  • moving
  • copy
  • backup
  • hooks
  • parallelize
  • parsed_json
  • delete_promoted
  • multi_delete
  • direct_upload
  • background_helpers
  • migration_helpers

Gems

All official shrine-* gems have been updated to work with Shrine 3.0:

ROM & Hanami integration

As a result of the attacher redesign, Shrine 3.0 should now integrate better with ROM and Hanami. I’m still wrapping up the shrine-rom gem, it should hopefully get released in the next couple of days. :crossed_fingers:

Conclusion

This has been the biggest Shrine release so far :open_mouth:. Shrine has been on 2.x for the past three years, and during that time it became obvious that some features weren’t well designed.

Shrine’s plugin architecture helped us a lot in addressing these, as it kept features largely decoupled from one another. This made it easier to identify issues and make changes, as we could work on one feature without affecting others.

Let me know if you have any feedback. Feel free to post on our Discourse forum if you have any troubles upgrading.

Janko Marohnić

Janko Marohnić

A passionate Ruby backend developer who fell in love with Roda & Sequel, and told Rails “it’s not me, it’s you”. He enjoys working with JSON APIs and SQL databases, while prioritizing testing, and always tries to find the best library for the job. Creator of Shrine and test.vim.

comments powered by Disqus