Module: Cloudflare
- Defined in:
- lib/cloudflare_workers.rb,
lib/cloudflare_workers/ai.rb,
lib/cloudflare_workers/http.rb,
lib/cloudflare_workers/cache.rb,
lib/cloudflare_workers/email.rb,
lib/cloudflare_workers/queue.rb,
lib/cloudflare_workers/stream.rb,
lib/cloudflare_workers/multipart.rb,
lib/cloudflare_workers/scheduled.rb,
lib/cloudflare_workers/durable_object.rb
Overview
backtick_javascript: true
Phase 11A — multipart/form-data receive pipeline.
Why a bespoke parser instead of Rack::Multipart::Parser?
Rack’s parser does work on Opal in principle (strscan is in stdlib), but it relies on Tempfile — which is a stub on Workers since there is no writable filesystem. It also assumes the request body is a true binary ByteString, whereas on Workers we have to cross the JS/Ruby boundary and Opal Strings are JS Strings (UTF-16 code units). The correct way to pass bytes through that boundary is to encode each byte as a single ‘char code 0-255` latin1 character, then `unescape / String.fromCharCode.apply` back into a Uint8Array when we need raw bytes again (e.g. R2.put).
This module exposes:
Cloudflare::Multipart.parse(body_binstr, content_type)
→ Hash[String => Cloudflare::UploadedFile | String]
Cloudflare::UploadedFile
#filename — original filename from the Content-Disposition header
#content_type — part Content-Type (defaults to application/octet-stream)
#name — form field name
#size — byte length
#bytes_binstr — the latin1-encoded byte string (1 char = 1 byte)
#to_uint8_array — JS Uint8Array suitable for fetch body / R2.put
#read — returns bytes_binstr, mirroring Tempfile#read
#rewind — no-op (content is fully in-memory, there is no
writable filesystem on Workers)
Also installs ‘Rack::Request#post?`-path hook: when the Sinatra route calls `params`, `Rack::Request#POST` delegates to `Cloudflare::Multipart.rack_params(env)` which parses once, caches on the env, and hydrates `params` with UploadedFile / String values.
Defined Under Namespace
Modules: AI, DurableObject, HTTP, Multipart, QueueConsumer, Scheduled Classes: AIError, BinaryBody, BindingError, Cache, CacheError, D1Database, D1Error, D1Statement, DurableObjectError, DurableObjectId, DurableObjectNamespace, DurableObjectRequest, DurableObjectRequestContext, DurableObjectState, DurableObjectStorage, DurableObjectStub, Email, HTTPError, HTTPResponse, KVError, KVNamespace, Queue, QueueBatch, QueueContext, QueueError, QueueMessage, R2Bucket, R2Error, RawResponse, SSEOut, SSEStream, ScheduledEvent, UploadedFile
Class Method Summary collapse
-
.js_object_to_hash(js_obj) ⇒ Object
Shallow copy of a JS object’s own enumerable string keys into a Hash.
-
.js_promise?(obj) ⇒ Boolean
Check whether the argument is a native JS Promise / thenable.
-
.js_rows_to_ruby(js_rows) ⇒ Object
JS Array -> Ruby Array of Ruby Hashes (for D1 result.results).
-
.js_to_ruby(js_val) ⇒ Object
Generic JS->Ruby for the common Workers AI response shape: { response: “…”, usage: { prompt_tokens: … } } Recursively converts nested objects + arrays.
Class Method Details
.js_object_to_hash(js_obj) ⇒ Object
Shallow copy of a JS object’s own enumerable string keys into a Hash.
473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/cloudflare_workers.rb', line 473 def self.js_object_to_hash(js_obj) h = {} return h if `#{js_obj} == null` keys = `Object.keys(#{js_obj})` len = `#{keys}.length` i = 0 while i < len k = `#{keys}[#{i}]` v = `#{js_obj}[#{k}]` h[k] = v i += 1 end h end |
.js_promise?(obj) ⇒ Boolean
Check whether the argument is a native JS Promise / thenable. Ruby’s ‘Object#then` (alias of `yield_self`) is a universal method since Ruby 2.6, so `obj.respond_to?(:then)` is always true and is useless as a Promise detector. We must check for a JS function at `.then` instead.
454 455 456 |
# File 'lib/cloudflare_workers.rb', line 454 def self.js_promise?(obj) `(#{obj} != null && typeof #{obj}.then === 'function' && typeof #{obj}.catch === 'function')` end |
.js_rows_to_ruby(js_rows) ⇒ Object
JS Array -> Ruby Array of Ruby Hashes (for D1 result.results).
459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/cloudflare_workers.rb', line 459 def self.js_rows_to_ruby(js_rows) out = [] return out if `#{js_rows} == null` len = `#{js_rows}.length` i = 0 while i < len js_row = `#{js_rows}[#{i}]` out << js_object_to_hash(js_row) i += 1 end out end |
.js_to_ruby(js_val) ⇒ Object
Generic JS->Ruby for the common Workers AI response shape:
{ response: "...", usage: { prompt_tokens: ... } }
Recursively converts nested objects + arrays.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/cloudflare_workers/ai.rb', line 170 def self.js_to_ruby(js_val) return nil if `#{js_val} == null` return js_val if `typeof #{js_val} === 'string' || typeof #{js_val} === 'number' || typeof #{js_val} === 'boolean'` if `Array.isArray(#{js_val})` out = [] len = `#{js_val}.length` i = 0 while i < len out << js_to_ruby(`#{js_val}[#{i}]`) i += 1 end return out end if `typeof #{js_val} === 'object'` h = {} keys = `Object.keys(#{js_val})` len = `#{keys}.length` i = 0 while i < len k = `#{keys}[#{i}]` h[k] = js_to_ruby(`#{js_val}[#{k}]`) i += 1 end return h end js_val end |