Class: Dommy::Blob

Inherits:
Object
  • Object
show all
Includes:
Dommy::Bridge::Methods
Defined in:
lib/dommy/blob.rb

Overview

Blob — opaque binary chunk with a MIME type, mirroring the File API's Blob interface. Used by File, FormData, and any code that needs to round-trip bytes through the DOM (e.g. a <input type="file"> test scenario).

Spec: https://w3c.github.io/FileAPI/#blob-section

Direct Known Subclasses

File

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Dommy::Bridge::Methods

included

Constructor Details

#initialize(parts = [], options = {}, window = nil) ⇒ Blob

Construct a Blob from a list of parts. Each part can be:

- String (treated as binary bytes)
- Blob / File (their bytes are concatenated)
- Array<Integer> (byte values, like ArrayBuffer)
- anything else: coerced via to_s

options["type"] sets the MIME type (lowercased per spec). window (optional) lets the JS-facing text()/arrayBuffer() return real Promises (they need a scheduler). A window-less Blob falls back to a synchronous result, which await still handles.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/dommy/blob.rb', line 23

def initialize(parts = [], options = {}, window = nil)
  # WebIDL: an omitted / `undefined` blobParts argument defaults to an empty
  # sequence (`new Blob()` / `new Blob(undefined)` is a zero-length Blob), so
  # it must not be coerced to the string "undefined".
  parts = [] if parts.nil? || (defined?(Bridge::UNDEFINED) && parts.equal?(Bridge::UNDEFINED))
  parts = [parts] unless parts.is_a?(Array)
  @data = collect_bytes(parts)
  @size = @data.bytesize
  raw_type = (options["type"] || options[:type] || "").to_s
  # A type string with any code point outside U+0020..U+007E is discarded
  # (→ ""); otherwise it is ASCII-lowercased (FileAPI "parse a MIME type"
  # gate, applied to both the constructor and slice's contentType).
  @type = raw_type.match?(/[^ -~]/) ? "" : raw_type.downcase
  @window = window
end

Instance Attribute Details

#sizeObject (readonly)

Returns the value of attribute size.



11
12
13
# File 'lib/dommy/blob.rb', line 11

def size
  @size
end

#typeObject (readonly)

Returns the value of attribute type.



11
12
13
# File 'lib/dommy/blob.rb', line 11

def type
  @type
end

Instance Method Details

#__dommy_bytes__Object

Raw binary bytes (Ruby ASCII-8BIT string). Used by FormData / fetch when serializing multipart bodies.



70
71
72
# File 'lib/dommy/blob.rb', line 70

def __dommy_bytes__
  @data
end

#__js_call__(method, args) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/dommy/blob.rb', line 89

def __js_call__(method, args)
  case method
  when "slice"
    # An omitted / `undefined` start|end uses the default (0 / size); map
    # UNDEFINED to nil so the `|| default` fallbacks apply (a bare UNDEFINED
    # is truthy and has no #to_i). contentType is a plain DOMString: omitted
    # / undefined → "" (default), but an explicit JS null coerces to "null".
    a = args.map { |v| v.equal?(Bridge::UNDEFINED) ? nil : v }
    ctype = if args.length < 3 || args[2].equal?(Bridge::UNDEFINED)
      ""
    else
      args[2].nil? ? "null" : args[2].to_s
    end
    slice(a[0] || 0, a[1] || @size, ctype)
  when "text"
    # WHATWG: Blob.text() returns a Promise<string>.
    promise_or_value(text)
  when "arrayBuffer"
    # WHATWG: Blob.arrayBuffer() returns a Promise<ArrayBuffer>.
    promise_or_value(array_buffer)
  when "bytes"
    # WHATWG: Blob.bytes() returns a Promise<Uint8Array>.
    promise_or_value(bytes)
  end
end

#__js_get__(key) ⇒ Object



74
75
76
77
78
79
80
81
82
83
# File 'lib/dommy/blob.rb', line 74

def __js_get__(key)
  case key
  when "size"
    @size
  when "type"
    @type
  else
    Bridge::ABSENT
  end
end

#array_bufferObject

Read the bytes as a real ArrayBuffer (the spec return type, wrapped so it crosses the JS boundary as a bare ArrayBuffer rather than an Array/typed array). The DOM spec returns a Promise; Dommy is synchronous.



57
58
59
# File 'lib/dommy/blob.rb', line 57

def array_buffer
  Bridge::ArrayBuffer.new(@data.bytes)
end

#bytesObject

Read the bytes as a Uint8Array (the spec return type). The DOM spec returns a Promise; Dommy is synchronous. Bridge::Bytes crosses the JS boundary as a Uint8Array (vs ArrayBuffer for #array_buffer).



64
65
66
# File 'lib/dommy/blob.rb', line 64

def bytes
  Bridge::Bytes.new(@data.bytes)
end

#slice(start = 0, last = @size, content_type = "") ⇒ Object

Return a new Blob over a byte range of this one. Negative indices are treated as offsets from the end (per spec).



41
42
43
44
45
46
# File 'lib/dommy/blob.rb', line 41

def slice(start = 0, last = @size, content_type = "")
  s = clamp_index(clamp_long_long(start), @size)
  e = clamp_index(clamp_long_long(last), @size)
  e = s if e < s
  Blob.new([@data.byteslice(s, e - s) || ""], {"type" => content_type.to_s}, @window)
end

#textObject

Read the bytes as UTF-8 text. The DOM spec returns a Promise, but Dommy is synchronous, so callers can use the result directly.



50
51
52
# File 'lib/dommy/blob.rb', line 50

def text
  @data.dup.force_encoding(Encoding::UTF_8)
end