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.

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

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

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

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 :

The following plugins have been deprecated :

versions

processing

recache

delete_raw

module_include

The following plugins have been removed :

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.

Conclusion

This has been the biggest Shrine release so far . 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.