How to Make Action Text Work with Rails 5.2
When DHH announced Action Text, I really like the idea of making rich text editors easier to use especially when throwing photos in the mix. It’s come up multiple times in projects over the years and it’s never been easy to do.
It just so happens, I was working on a project that needed a simple blog and would be perfect match for Action Text. Only problem was it was a Rails 5.2 application and Action Text is really slated for Rails 6, which won’t be released until next year. So I decided to see if I can make it work. Long story, short - it works.
1. Installation
In Rails 5.2, The ActionStorage performs image processing on the fly and uses MiniMagick by default. However in Rails 6, ActiveStorage is moving to the ImageProcessing gem instead. But for our purposes, we just need the Action Text gem installed.
gem "actiontext", github: "rails/actiontext", require: "action_text"
And then run the installer and migrations:
rails action_text:install
rails db:migrate
2. Update Action Text’s Blob Partial
The Action Text installer will add a view partial _blob.html.erb
that’s
responsible for displaying the images. We’ll need to modify this partial since
Action Text assumes we’re using ImageProcessing’s resize_to_fit
option to
resize photos, but that’s not available to us yet. We need to use the
variant
option available to us in Rails 5.2 ActiveStorage. These variants are
used to create thumbnails, fixed-size avatars, or any other derivative image
from the original.
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
<% if blob.representable? %>
<%= image_tag(blob.variant(resize: "800x600")) %>
<% end %>
<figcaption class="attachment__caption">
<% if caption = blob.try(:caption) %>
<%= caption %>
<% end %>
</figcaption>
</figure>
Now, this worked for the most part. I was able to click an drag photos over to the trix editor and verified that it was uploading files via ActiveStorage. Pretty cool!
3. Avoid PG:UniqueViolation Errors
But, I noticed some issues with other ActiveStorage enabled models unrelated to Action Text, in
particular, models with has_many_attached
included. Looking at the logs, I saw some
exceptions:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_active_storage_attachments_uniqueness" DETAIL: Key (record_type, record_id, name, blob_id)=(ActionText::RichText, 349d05dd-cc7c-4e51-973d-24ff9e382e10, embeds, 1e794d34-6a14-4a80-9aeb-d31e6e1a1494) already exists. : INSERT INTO "active_storage_attachments" ("name", "record_id", "record_type", "blob_id", "created_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
It wasn’t saving all the attachments for a model. I had no idea why this was
happending, but I verified it worked fine in Rails 5.2. Digging deeper, it looks
like this entire part of ActiveStorage was rewritten for Rails 6 and that means
Action Text was written to work with that, understandably. The exception is
being raised
here
in activestorage
. It’s attempting to create new records when the records
already exists, which unfortunately means we’re going to have to override this
method with a little monkey patching:
# config/initializers/monkey_patches.rb
module ActiveStorage
class Attached::Many < Attached
def attach(*attachables)
attachables.flatten.collect do |attachable|
if record.new_record?
attachments.build(record: record, blob: create_blob_from(attachable))
elsif !record.is_a? ActionText::RichText
attachments.create!(record: record, blob: create_blob_from(attachable))
end
end
end
end
end
We simple check if the record is of type ActionText:Rich
before attempting to create new attachments, otherwise we just skip it. And that did the trick.