Module: RactorRailsShim::ActiveStorageAttachedPatch::BlobMetadataTypePatch
- Defined in:
- lib/ractor_rails_shim/patches/active_storage.rb
Overview
ActiveStorage::Blob#metadata is declared via store :metadata, which
should register a Type::Serialized cast type. When ActiveStorage::Blob
is freshly autoloaded in a worker Ractor's empty constant namespace (before
the DB connection/schema is established), cast_types is built from an empty
columns_hash, the :metadata decorator is silently skipped, and
type_for_attribute(:metadata) returns a plain Type::Text/Type::Value.
That makes store_accessor_for raise "the column 'metadata' has not been
configured as a store". Force a Type::Serialized for :metadata so
worker uploads work regardless of load order.
read_attribute(:metadata) does NOT go through type_for_attribute; it
uses the cached @attributes set built from _default_attributes →
attribute_types → cast_types, which memoizes a plain Type::Value if
the class was loaded without the serialized decorator. We patch
attribute_types to return Type::Serialized for :metadata, but since
the frozen shared graph prevents writing class ivars in a worker, the
REAL fix is to rebuild _default_attributes in MAIN at
prepare_for_ractors! time (see _fix_blob_metadata_type! below), so the
frozen graph carries the correct Type::Serialized from the start. The
attribute_types / type_for_attribute overrides are fallbacks for the
case where the class is freshly autoloaded in a worker (separate process).
Instance Method Summary collapse
Instance Method Details
#attribute_types ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ractor_rails_shim/patches/active_storage.rb', line 105 def attribute_types types = super return types if types[:metadata].is_a?(::ActiveRecord::Type::Serialized) # Worker-safe: don't try to memoize (can't write class ivars in a # non-main Ractor). Return a fresh Hash with the corrected type each # call. The overhead is negligible (this path is worker-only). corrected = types.dup coder = ::ActiveRecord::Coders::JSON.new ind_coder = ::ActiveRecord::Store::IndifferentCoder.new(:metadata, coder) corrected[:metadata] = ::ActiveRecord::Type::Serialized.new( types[:metadata] || ::ActiveModel::Type::Value.new, ind_coder ) corrected end |
#type_for_attribute(name = :__none__) ⇒ Object
96 97 98 99 100 101 102 103 |
# File 'lib/ractor_rails_shim/patches/active_storage.rb', line 96 def type_for_attribute(name = :__none__) return super if name != :metadata t = super(name) return t if defined?(::ActiveRecord::Type::Serialized) && t.is_a?(::ActiveRecord::Type::Serialized) coder = ::ActiveRecord::Coders::JSON.new ind_coder = ::ActiveRecord::Store::IndifferentCoder.new(:metadata, coder) ::ActiveRecord::Type::Serialized.new(t, ind_coder) end |