Rails Internationalisation for ActiveStorage & ActionText

43 Views Asked by At

Rails has internal patterns for which it generates tables to handle polymorphic associations

has_one_attached  :document
has_many_attached :pictures
has_rich_text     :body

However, this polymorphism is hard to wrangle with the polymorphism that the mobility gem provides, where one would declare:

  translates :caption, type: :string
  translates :description, type: :text

Firstly, there is an unexpected behaviour in migrations: migrating backwards before migration set-up for ActiveStorage, the tables were found to not be deleted. Migrating + restarting the server = objects were still being rendered. Only purges delete them.
I cannot fathom where I went wrong, not having modified the migration. I do notice however, for a Rails 7 application, that the class is declared as
class CreateActiveStorageTables < ActiveRecord::Migration[5.2] maybe it works on up but not down...

The following does create columns that render, can be queried and updated via the console.

class UpdateActiveStorageTables < ActiveRecord::Migration[7.0]
  def change
    add_column :active_storage_blobs, :caption, :string
    add_column :active_storage_blobs, :description, :text
  end
end

The controller action can update the blob

  def add_image
    images = params[:individual][:image]
    if images
      images.each do |image|
        @individual.images.attach(image)
      end
    end
    @individual.save
    new_image = @individual.images.last  # in the hope of no near-simultaneous addition of an image to said user
    new_image.blob.update(caption: params[:individual][:caption], description: params[:individual][:description])
  end

yes this is somewhat odd, but with the form, the added attributes (caption and description) are not part of the image data array

Can one declare this at a model level for Mobility and integrate Mobility into this action (and how, if so)?

An alternative would be to create a new class, polymorphic assuming multiple classes use ActiveStorage, to create attributes that belong to the attachment, translate those as subsequent data inputs to the attachment creation.
But how would on define the migration and model (belongs_to ActiveStorage::Attachment ?) to create such classes?

2

There are 2 best solutions below

2
mechnicov On BEST ANSWER

On my opinion has_one_attached is always more preferable over has_many_attached because you can add to "parent" model any needed columns, sort by them, etc.

class Picture < ApplicationRecord
  extend Mobility

  belongs_to :imageable, polymorphic: true

  has_one_attached  :file

  translates :caption, type: :string
  translates :description, type: :text
end

class User < ApplicationRecord
  has_many :pictures, as: :imageable
end

In this case you can use these caption or description for Picture model, not for blobs

0
Jerome On

@mechnicov 's answer is marked as accepted and upvoted, as it points into a valid direction.

Additional factors come into play:

  1. a form that has a multipart attachment component cannot be made to handle multiple locale inputs in a simple UI manner: too many errors are possible, leading to user frustration
  2. Attempting to wrangle Mobility around Rails polymorphism for ActiveStorage objects is long and costly. Given point 1, it gives the jury an immediate verdict.

Thus, simply:

  • creating a migration with the added localisable (and/or indexable) columns for a Picture class
  • define the Picture class with has_one_attached :file
  • execute everything as prescribed by Rails' ActiveStorage
    will do just fine.

This observer recognises that the Rails way is straightforward and robust.