Rails 5.2 was released a few weeks ago and comes with awesome features like the new credentials vault, HTTP/2 early hints, Redis cache store and ActiveStorage, which I’m going to focus on in this blog post. The project was initiated by DHH in mid-2017 and has been merged into Rails core. It’s a built-in way to deal with uploads without extra dependencies like Paperclip, Carrierwave or Shrine.
ActiveStorage comes with a complete DSL which allows you to attach and detach one or multiple files to a model. By default, ActiveStorage isn’t installed in a new rails project, you have to run:
rails active_storage:install
This command simply copies a migration in your projet. ActiveStorage needs two models/tables : active_storage_blobs
where each record represents a file (which is not stored in the database of course) and active_storage_attachments
is a polymorphic bridge between your models and your uploaded files.
In your model, you need to declare that you’re attaching files:
class Car < ApplicationRecord
has_one_attached :photo
end
Then, you can add a file input into your form:
<%= image_tag url_for(car.photo) if car.photo.attachment.present? %>
<%= form.label :photo %>
<%= form.file_field :photo %>
In your controller, you must specify that you want to attach a file:
@car.photo.attach(params[:car][:photo]) if params[:car][:photo]
And that’s all you need to do for a basic file uploader. If you attach a new file or you delete your record, ActiveStorage will remove the old one from your storage and clean up your database.
With a lot of nice little “cherries on the cake”, ActiveStorage covers most of the cases:
ActiveStorage::Service
class.Maintenability: Since ActiveStorage has been merged into Rails, all the features described above are built-in and don’t require any extra dependencies and so less maintenance needs to be scheduled.
Structure:
Most popular gems like Shrine, Paperclip, etc. don’t provide ready-to-use tables and require a migration to add a few fields where you want to store your file information. Even if you feel free to do what you want and you’re not stuck to the ActiveStorage way, from my experience you’ll certainly recreate a polymorphic Asset
model.
Form:
As we seen above, ActiveStorage attaches and detaches files outside ActiveRecord transactions. You need to do it by yourself when you need to, independently from a save
, whereas common gems store your files using ActiveRecord callbacks directly from your params. In my opinion, ActiveStorage provides a better way to handle file attachments by separating two concepts: attributes which go in the database and files which depend on your storage.
Missing features: There’s a few advanced features which are not handled by ActiveStorage (yet?) and you’ll need to develop them if you choose to go with ActiveStorage. For instance, Shrine (currently the most advanced competitor), provides a way to cache uploaded files and avoid re-upload when your form has errors. Shrine also provides a simple way to manipulate and post-process files in the background. And last but not least, the implementation of TUS protocol allow you to do multi-part uploads.
It’s really interresting to see that Thoughbot (the Paperclip maintainers) just announced the deprecation of Paperclip in favor of ActiveStorage and it’s a good example of what makes the Ruby On Rails community so strong.
Major kudos to @thoughtbot for all the work on Paperclip over the years! It was one of the premiere file attachment solutions for Rails for a very long time. The work helped inform and inspire Active Storage 🙏❤️ https://t.co/DGoCDAZS0N
— DHH (@dhh) 15 mai 2018
We are deprecating Paperclip in favor of ActiveStorage. Learn what this means for you. https://t.co/b4MpPhKXaN
— thoughtbot (@thoughtbot) 14 mai 2018
We can be sure they’ll use their experience by contributing to ActiveStorage, as did janko-m (Shrine maintainer) who already improved S3 storage and implemented his own ImageProcessing gem to ActiveStorage.
At Drivy we handle file uploads in a similar way to ActiveStorage. We have our own DSL for models and a lot of methods for controllers and views. We also have some JavaScript for direct upload to our cloud storage. We don’t use post processing libraries for our images and we delegate all these tasks to a third party in order to reduce the impact on our CPUs.
So, should we move to ActiveStorage? We already have all the features we need and there’s currently no need to move to ActiveStorage. Sure it might reduce the maintenance cost and we could take advantage of security fixes and evolution with Rails upgrades, but with a 6-year-old codebase and thousands of attachments the migration would be huge!
In my opinion, ActiveStorage is a really good choice for a new project.
EDIT: janko-m (Shrine maintainer) commented this post via reddit and raised interesting points related to ActiveStorage and future changes for Shrine.