Module: Cloudflare::Scheduled
- Defined in:
- lib/cloudflare_workers/scheduled.rb
Overview
Dispatcher singleton. The Rack handler installs the JS hook on boot; user code never calls anything in this module directly.
Class Attribute Summary collapse
-
.app ⇒ Object
Override the dispatch target.
Class Method Summary collapse
-
.await_promise(promise) ⇒ Object
Await a JS Promise from inside ‘# await: true` code without exposing the `__await__` keyword to callers.
-
.dispatch(cron, scheduled_time = Time.now, js_env = nil, js_ctx = nil) ⇒ Object
Test-friendly direct entry point.
-
.dispatch_js(js_event, js_env, js_ctx) ⇒ Object
Called from the JS hook.
-
.install_dispatcher ⇒ Object
Install the JS-side dispatcher on globalThis.
-
.resolve_app ⇒ Object
Resolve which app class should receive the dispatch.
Class Attribute Details
.app ⇒ Object
Override the dispatch target. By default the dispatcher uses ‘Rack::Handler::CloudflareWorkers.app`, which is whatever the user passed to top-level `run app`. Tests use this to plug a fake Sinatra subclass without booting the full handler.
97 98 99 |
# File 'lib/cloudflare_workers/scheduled.rb', line 97 def app @app end |
Class Method Details
.await_promise(promise) ⇒ Object
Await a JS Promise from inside ‘# await: true` code without exposing the `__await__` keyword to callers. Re-throws Promise rejections as synchronous Ruby exceptions so callers can rescue. Sinatra::Scheduled uses this to bridge per-job exceptions out of the async block boundary.
Implemented as ‘__await__` directly so Opal compiles this method itself as async — callers in `# await: true` files that do `await_promise(p)` get the resolved value back the same way they would from a bare `p.__await__`. Rejections re-throw as Ruby exceptions thanks to ES8 `await`’s built-in throw semantics.
151 152 153 |
# File 'lib/cloudflare_workers/scheduled.rb', line 151 def self.await_promise(promise) promise.__await__ end |
.dispatch(cron, scheduled_time = Time.now, js_env = nil, js_ctx = nil) ⇒ Object
Test-friendly direct entry point. Lets ‘test/scheduled_smoke.rb` exercise the same dispatch logic without going through the JS hook. `cron` / `scheduled_time` are plain Ruby values. Returns the awaited result Hash (callers can still `__await__` the outer Promise — this method is async since it uses `__await__`).
133 134 135 136 137 138 |
# File 'lib/cloudflare_workers/scheduled.rb', line 133 def self.dispatch(cron, scheduled_time = Time.now, js_env = nil, js_ctx = nil) event = ScheduledEvent.new(cron: cron, scheduled_time: scheduled_time) target = resolve_app raise 'no app registered for Cloudflare::Scheduled' if target.nil? target.dispatch_scheduled(event, js_env, js_ctx).__await__ end |
.dispatch_js(js_event, js_env, js_ctx) ⇒ Object
Called from the JS hook. Resolves the Ruby app, builds a ScheduledEvent, runs ‘dispatch_scheduled` on the app class, and returns the result Hash for diagnostics. We `__await__` the inner dispatch so the returned object is the resolved Hash, not a Promise — the JS hook then `await`s our return promise once.
118 119 120 121 122 123 124 125 126 |
# File 'lib/cloudflare_workers/scheduled.rb', line 118 def self.dispatch_js(js_event, js_env, js_ctx) event = ScheduledEvent.from_js(js_event) target = resolve_app if target.nil? warn '[Cloudflare::Scheduled] no app registered; ignoring scheduled event' return { 'fired' => 0, 'total' => 0, 'results' => [], 'error' => 'no_app' } end target.dispatch_scheduled(event, js_env, js_ctx).__await__ end |
.install_dispatcher ⇒ Object
Install the JS-side dispatcher on globalThis. Idempotent — safe to call multiple times (last writer wins).
NOTE: kept as a single-line backtick x-string. Opal’s parser treats multi-line backtick strings as raw statements that don’t return a value AND it tries to lex-as-Ruby everything inside, so JS comments containing the Ruby backtick delimiter (‘…`) crash the build. Single-line form sidesteps both pitfalls.
108 109 110 111 |
# File 'lib/cloudflare_workers/scheduled.rb', line 108 def self.install_dispatcher mod = self `globalThis.__HOMURA_SCHEDULED_DISPATCH__ = async function(js_event, js_env, js_ctx) { try { return await #{mod}.$dispatch_js(js_event, js_env, js_ctx); } catch (err) { try { globalThis.console.error('[Cloudflare::Scheduled] dispatch failed:', err && err.stack || err); } catch (e) {} return { error: String(err && err.message || err) }; } };(function(){var g=globalThis;g.__OPAL_WORKERS__=g.__OPAL_WORKERS__||{};g.__OPAL_WORKERS__.scheduled=g.__HOMURA_SCHEDULED_DISPATCH__;})();` end |
.resolve_app ⇒ Object
Resolve which app class should receive the dispatch. Priority:
1. `Cloudflare::Scheduled.app = SomeApp` (explicit override)
2. `Rack::Handler::CloudflareWorkers.app` (set by `run app`)
Returns the class itself (not an instance), because ‘dispatch_scheduled` is a class method on Sinatra apps.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/cloudflare_workers/scheduled.rb', line 160 def self.resolve_app candidate = @app if candidate.nil? && defined?(::Rack::Handler::CloudflareWorkers) candidate = ::Rack::Handler::CloudflareWorkers.app end return nil if candidate.nil? # Sinatra app classes respond to `dispatch_scheduled` (added by # Sinatra::Scheduled). Plain Rack apps would be instances and # lack the class method — we return them as-is and let the # caller's `dispatch_scheduled` raise NoMethodError so the # mistake is visible. if candidate.respond_to?(:dispatch_scheduled) candidate elsif candidate.respond_to?(:class) && candidate.class.respond_to?(:dispatch_scheduled) candidate.class else candidate end end |