Module: RactorRailsShim::WorkerAppFactory
- Defined in:
- lib/ractor_rails_shim/roles/worker_app_factory.rb
Class Method Summary collapse
- .autoloaders ⇒ Object
-
.build(frozen_app) ⇒ Object
Build the shareable Rack app handed to kino.
-
.capture_constants! ⇒ Object
Capture a frozen name -> object map for every constant the application's Zeitwerk loaders manage.
- .configure(autoloaders: nil, const_get: nil, worker_app_class: nil) ⇒ Object
- .const_get_callable ⇒ Object
- .reset_configuration ⇒ Object
- .worker_app_class ⇒ Object
Class Method Details
.autoloaders ⇒ Object
40 41 42 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 40 def self.autoloaders @autoloaders end |
.build(frozen_app) ⇒ Object
Build the shareable Rack app handed to kino. Captures the
application's constants in the main Ractor and wraps the frozen,
shareable app in a WorkerApp that rebinds those constants (and
initializes the worker's ActiveRecord connection) on the first
request served by each worker Ractor. Returns a shareable WorkerApp
instance — frozen and Ractor.make_shareable'd — so it can be passed
directly to Ractor.new(WorkerAppFactory.build(app)) { |a| a.call(env) }
without the caller having to know the shareability contract.
117 118 119 120 121 122 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 117 def self.build(frozen_app) bindings = capture_constants! wa = worker_app_class.new(frozen_app, bindings) wa.freeze Ractor.make_shareable(wa) end |
.capture_constants! ⇒ Object
Capture a frozen name -> object map for every constant the application's Zeitwerk loaders manage. Runs in the MAIN Ractor, after eager load, where all app constants are defined. The map travels to worker Ractors, which use it to (re)bind the constant names into their own namespaces.
Why this is needed: a Ractor boundary does NOT share top-level
constant names — only the class/module objects reachable from the
frozen shared app graph cross the boundary. A worker Ractor therefore
sees RactorRailsShim, ActiveRecord, ApplicationRecord, the
controllers, etc. (objects reachable from the app), but NOT the
application's own model constants (e.g. Post): the object is in the
graph, but its name is not bound in the worker, so
PostsController#index's Post reference raises NameError. Rebinding
the captured names fixes it without re-running autoloading (which is
itself impossible in a worker, since Zeitwerk::Loader.new raises
IsolationError off the main Ractor).
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 69 def self.capture_constants! map = {} al = autoloaders cget = const_get_callable || Object.method(:const_get) if al.nil? # Backward compat: fall back to Rails.autoloaders when no explicit # autoloaders injected (seam not yet called). if defined?(::Rails) && Rails.respond_to?(:autoloaders) al = Rails.autoloaders else return map.freeze end end loaders = if al.respond_to?(:main) && al.respond_to?(:once) [al.main, al.once] elsif al.respond_to?(:each) al.to_a else [] end loaders.each do |loader| next unless loader && loader.respond_to?(:all_expected_cpaths) begin loader.all_expected_cpaths.values.each do |cpath| obj = cget.call(cpath) rescue next begin Ractor.make_shareable(obj) unless Ractor.shareable?(obj) rescue StandardError next end map[cpath] = obj if Ractor.shareable?(obj) end rescue StandardError => e warn "[ractor_rails_shim] capture_app_constants!: #{e.class}: #{e.}" end end map.freeze end |
.configure(autoloaders: nil, const_get: nil, worker_app_class: nil) ⇒ Object
28 29 30 31 32 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 28 def self.configure(autoloaders: nil, const_get: nil, worker_app_class: nil) @autoloaders = autoloaders @const_get_callable = const_get @worker_app_class = worker_app_class end |
.const_get_callable ⇒ Object
44 45 46 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 44 def self.const_get_callable @const_get_callable end |
.reset_configuration ⇒ Object
34 35 36 37 38 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 34 def self.reset_configuration @autoloaders = nil @const_get_callable = nil @worker_app_class = nil end |
.worker_app_class ⇒ Object
48 49 50 |
# File 'lib/ractor_rails_shim/roles/worker_app_factory.rb', line 48 def self.worker_app_class @worker_app_class || WorkerApp end |