Module: RactorRailsShim::ActiveModelAttributeRegistrationPatch::InstanceMethods

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

Instance Method Summary collapse

Instance Method Details

#_default_attributesObject

:nodoc:



71
72
73
74
75
76
77
# File 'lib/ractor_rails_shim/patches/active_model_attribute.rb', line 71

def _default_attributes # :nodoc:
  key = :"rrs_default_attributes_#{object_id}"
  RactorRailsShim.storage[key] ||=
    ::ActiveModel::AttributeSet.new({}).tap do |attribute_set|
      apply_pending_attribute_modifications(attribute_set)
    end
end

#attribute_typesObject

:nodoc:



79
80
81
82
83
84
85
86
# File 'lib/ractor_rails_shim/patches/active_model_attribute.rb', line 79

def attribute_types # :nodoc:
  key = :"rrs_attribute_types_#{object_id}"
  RactorRailsShim.storage[key] ||= begin
    types = _default_attributes.cast_types
    types.default = ::ActiveModel::Type.default_value
    types
  end
end

#generated_attribute_methodsObject

generated_attribute_methods (ActiveModel::AttributeMethods::ClassMethods) memoizes @generated_attribute_methods ||= Module.new.tap { |mod| include mod } — a class ivar holding a Module. In a worker Ractor the class ivar is not shared with main, so the worker creates a NEW empty Module (no attribute methods), and name= / id= etc. are missing — they fall through to method_missing (e.g. ActiveStorage::Attachment's delegate_missing_to :blob raises DelegationError because blob is nil). Route the module through per-Ractor IES, seeded from a shareable snapshot of every AR model's generated_attribute_methods module, captured in main at prepare time.



61
62
63
64
65
66
67
68
69
# File 'lib/ractor_rails_shim/patches/active_model_attribute.rb', line 61

def generated_attribute_methods
  if ::Ractor.main?
    super
  else
    key = :"rrs_generated_attribute_methods_#{object_id}"
    RactorRailsShim.storage[key] ||=
      (::RactorRailsShim::SHAREABLE_GEN_ATTR_METHODS[object_id] || ::Module.new.tap { |m| include m })
  end
end

#pending_attribute_modificationsObject

pending_attribute_modifications (activemodel/attribute_registration.rb) does @pending_attribute_modifications ||= [] — a class-ivar write that raises can not set instance variables of classes/modules by non-main Ractors in kino's worker Ractors (which do not share main's class-ivar space, unlike Ractor.new workers). Route the cache through per-Ractor IES so each worker keeps its own (seeded from main at prepare time via _share_pending_attribute_mods!, falling back to [] for models with no custom attribute macros).



96
97
98
99
100
101
102
103
104
# File 'lib/ractor_rails_shim/patches/active_model_attribute.rb', line 96

def pending_attribute_modifications
  if ::Ractor.main?
    @pending_attribute_modifications ||= []
  else
    key = :"rrs_pending_attr_mods_#{object_id}"
    RactorRailsShim.storage[key] ||=
      (::RactorRailsShim::SHAREABLE_PENDING_ATTR_MODS[object_id] || []).dup
  end
end

#reset_default_attributes!Object

reset_default_attributes! (called by reload_schema_from_cache via the Attributes → Timestamp → ModelSchema super chain) writes @default_attributes = nil and @attribute_types = nil class ivars. In a worker Ractor those writes raise Ractor::IsolationError. The shim routes the readers above through IES, so in a worker we clear the IES slots instead (the next read rebuilds lazily). In main we keep the original class-ivar-clearing behavior.



113
114
115
116
117
118
119
120
121
# File 'lib/ractor_rails_shim/patches/active_model_attribute.rb', line 113

def reset_default_attributes!
  if Ractor.main?
    @default_attributes = nil
    @attribute_types = nil
  else
    RactorRailsShim.storage.delete(:"rrs_default_attributes_#{object_id}")
    RactorRailsShim.storage.delete(:"rrs_attribute_types_#{object_id}")
  end
end