Class: RactorRailsShim::Callbacks::SymbolicTransport

Inherits:
Object
  • Object
show all
Defined in:
lib/ractor_rails_shim/callbacks/symbolic_transport.rb

Constant Summary collapse

DEFAULT_KINDS =

The callback chain kinds this transport owns by default. Controllers use :process_action; models use :save/:create/:update/:destroy and their sub-kinds (:validation, :commit, :rollback). Filter methods defined via define_method(&block) with an un-shareable Proc (e.g. AR's autosave association callbacks) are rescued and skipped so app def callbacks still run.

[
  :process_action,
  :save, :create, :update, :destroy,
  :validation, :commit, :rollback
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(source:, kinds: DEFAULT_KINDS) ⇒ SymbolicTransport

source: a Hash { class_object_id => [entry, …] } as described above, OR a callable returning that Hash, OR a Symbol naming the shareable constant to resolve via const_get (so a worker reads the frozen constant after prepare, not a stale snapshot). kinds: the set of chain kinds this transport owns (default DEFAULT_KINDS, which includes model lifecycle kinds). Unshareable-Proc filters are rescued and skipped automatically.



53
54
55
56
# File 'lib/ractor_rails_shim/callbacks/symbolic_transport.rb', line 53

def initialize(source:, kinds: DEFAULT_KINDS)
  @source = source
  @kinds = kinds.is_a?(::Set) ? kinds : ::Set.new(kinds.to_a)
end

Instance Method Details

#after(context, kind) ⇒ Object

Run the matching :after filters for kind, ancestor-first.



94
95
96
97
98
99
100
101
# File 'lib/ractor_rails_shim/callbacks/symbolic_transport.rb', line 94

def after(context, kind)
  each_applicable_filter(context, kind, :after) do |entry|
    next unless condition_allows?(context, entry)
    context.send(entry[:filter]) if context.respond_to?(entry[:filter], true)
  rescue RuntimeError => e
    raise e unless unshareable_proc_error?(e)
  end
end

#applies_to?(kind) ⇒ Boolean

Whether this transport owns kind. Configured by kinds: so the registry can compose multiple symbolic transports for different kind sets without editing this class (Open/Closed).

Returns:

  • (Boolean)


70
71
72
# File 'lib/ractor_rails_shim/callbacks/symbolic_transport.rb', line 70

def applies_to?(kind)
  @kinds.include?(kind)
end

#before(context, kind) ⇒ Object

Run the matching :before filters for kind, ancestor-first, respecting only/except. respond_to? guards each send so a stale capture never raises NoMethodError (matches the original controller replay behavior).

A filter defined via define_method(&block) with an un-shareable Proc raises "defined with an un-shareable Proc in a different Ractor" when send-ed in a worker. ActiveRecord generates such methods for autosave associations (e.g. autosave_associated_records_for_*). We SKIP those filters and continue the chain so app-defined def callbacks still run.



83
84
85
86
87
88
89
90
91
# File 'lib/ractor_rails_shim/callbacks/symbolic_transport.rb', line 83

def before(context, kind)
  each_applicable_filter(context, kind, :before) do |entry|
    next unless condition_allows?(context, entry)
    context.send(entry[:filter]) if context.respond_to?(entry[:filter], true)
  rescue RuntimeError => e
    raise e unless unshareable_proc_error?(e)
    # Skip the unshareable-Proc filter; the chain continues.
  end
end

#sourceObject



58
59
60
61
62
63
64
65
# File 'lib/ractor_rails_shim/callbacks/symbolic_transport.rb', line 58

def source
  s = @source
  return s.call if s.respond_to?(:call)
  return ::RactorRailsShim.const_get(s) if s.is_a?(::Symbol)
  s
rescue StandardError
  nil
end