Migrating from Refile (possible bug)

I am migrating a legacy app that used refile.

There is a specificity here. I need to do it in one step. Is that possible?

I am getting an error:

Caused by:
NoMethodError: undefined method `to_hash' for #<String:0x000055f8989b99c0>
Did you mean?  to_s
/shrine-3.2.1/lib/shrine/plugins/column.rb:81:in `deserialize_column'
/shrine-3.2.1/lib/shrine/plugins/column.rb:42:in `load_column'
/shrine-3.2.1/lib/shrine/plugins/entity.rb:139:in `read'
/shrine-3.2.1/lib/shrine/plugins/model.rb:101:in `load_model'
/shrine-3.2.1/lib/shrine/plugins/model.rb:63:in `attacher'
/shrine-3.2.1/lib/shrine/plugins/entity.rb:50:in `block in define_entity_methods'
/shrine-3.2.1/lib/shrine/plugins/activerecord.rb:29:in `block in included'

That is my code:

  create_table "content_indications", force: :cascade do |t|
    t.string "file_id"
    t.string "file_filename"
    t.integer "file_size"
    t.string "file_content_type"
    t.string "cover_id"
    t.string "cover_filename"
    t.integer "cover_size"
    t.string "cover_content_type"
    t.jsonb "file_data"
    t.jsonb "cover_data"
  end

class ContentIndication < ApplicationRecord
  include ImageStorage::Attachment(:cover)
  include FileStorage::Attachment(:file)
end

class MigrateToShrine < ActiveRecord::Migration[5.1]
  module RefileShrineSynchronization
    def write_shrine_data(name)
      attacher = Shrine::Attacher.from_model(self, name)

      if read_attribute("#{name}_id").present?
        attacher.set shrine_file(name)
      else
        attacher.set nil
      end
    end

    def shrine_file(name)
      Shrine.uploaded_file(storage: :store, id: send("#{name}_id"), metadata: { 'size' => (send("#{name}_size") if respond_to?("#{name}_size")), 'filename' => (send("#{name}_filename") if respond_to?("#{name}_filename")), 'mime_type' => (send("#{name}_content_type") if respond_to?("#{name}_content_type")) })
    end
  end

  def change
    ContentIndication.include(RefileShrineSynchronization)
    ContentIndication.find_each do |record|
      record.write_shrine_data(:cover)
      record.write_shrine_data(:file)
      record.save!
    end
  end
end

Maybe a bug. For some reason I was not able to discover.
The deserialize_column method does not have the column_serializer setted, it is nil. So I monkey patched.

class Shrine
  module Plugins
    # Documentation can be found on https://shrinerb.com/docs/plugins/column
    module Column
      module AttacherMethods
        def deserialize_column(data)
          column_serializer = Shrine::Plugins::Column::JsonSerializer
          if column_serializer && data && !data.is_a?(Hash)
            column_serializer.load(data)
          else
            data&.to_hash
          end
        end
      end
    end
  end
end

Apparently it did not solve the problem. I’ll continue to dig.

Monkey patch was removed with this change:

  plugin :model, cache: false

changing to

  plugin :model

and added this line to the migration module:

  module RefileShrineSynchronization
    def write_shrine_data(name)
      attacher = Shrine::Attacher.from_model(self, name)

      if read_attribute("#{name}_id").present?
        attacher.set shrine_file(name)
        self["#{name}_data"] = JSON.parse(send("#{name}_data")) # <---- this line
      else
        attacher.set nil
      end
    end

    def shrine_file(name)
      Shrine.uploaded_file(
        storage: :store,
        id: send("#{name}_id"),
        metadata: {
          'size' => (send("#{name}_size") if respond_to?("#{name}_size")),
          'filename' => (send("#{name}_filename") if respond_to?("#{name}_filename")),
          'mime_type' => (send("#{name}_content_type") if respond_to?("#{name}_content_type"))
        }
      )
    end
  end