Module: RactorRailsShim::ActiveRecordStoreInstancePatch

Defined in:
lib/ractor_rails_shim/patches/active_record_store.rb

Overview

ActiveRecord::Store#store_accessor_for resolves the store coder via type_for_attribute(store_attribute).tap do |type| ... end. The tap block is compiled in the main Ractor and is un-shareable, so a worker Ractor raises "defined with an un-shareable Proc in a different Ractor" when writing any store accessor (e.g. ActiveStorage::Blob#identified=). Reimplement without the block. This is an instance method of the ActiveRecord::Store module, so it is prepended to ActiveRecord::Store itself.

Instance Method Summary collapse

Instance Method Details

#store_accessor_for(store_attribute) ⇒ Object



124
125
126
127
128
129
130
# File 'lib/ractor_rails_shim/patches/active_record_store.rb', line 124

def store_accessor_for(store_attribute)
  type = type_for_attribute(store_attribute)
  unless type.respond_to?(:accessor)
    raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
  end
  type.accessor
end

#write_store_attribute(store_attribute, key, value) ⇒ Object

ActiveRecord::Store::HashAccessor#write mutates the hash returned by object.<attribute> and relies on that read returning a cached, mutable object so the mutation is reflected on later reads. In a worker Ractor the serialized attribute read is not cached (each read re-deserializes), so the in-place mutation is lost and store accessors such as ActiveStorage::Blob#identified= silently fail to persist. Re-implement write_store_attribute to read the current value, merge the key, and write it back through write_attribute — which always persists regardless of whether the deserialized read is cached. Store accessors use string keys, so the key is normalized with to_s.



142
143
144
145
146
147
# File 'lib/ractor_rails_shim/patches/active_record_store.rb', line 142

def write_store_attribute(store_attribute, key, value)
  current = read_attribute(store_attribute)
  current = current ? current.dup : {}
  current[key.to_s] = value
  write_attribute(store_attribute, current)
end