Class: Dommy::WebSocket

Inherits:
Object
  • Object
show all
Includes:
EventTarget
Defined in:
lib/dommy/web_socket.rb

Overview

‘WebSocket` polyfill. Real implementations open a TCP-then-frame connection; dommy exposes an in-memory transport tests drive via the `__*` seams:

ws.__simulate_open__               — fires `open`
ws.__simulate_message__(data)      — fires `message`
ws.__simulate_close__(code, reason) — fires `close`
ws.__simulate_error__              — fires `error`
ws.__sent_messages__               — array of sent payloads

By default a ‘new WebSocket(url)` auto-opens via microtask so the common pattern (`ws.onopen = …; ws.send(…)`) works without extra setup.

Spec: websockets.spec.whatwg.org/

Defined Under Namespace

Classes: Error

Constant Summary collapse

CONNECTING =
0
OPEN =
1
CLOSING =
2
CLOSED =
3
INLINE_HANDLERS =
%w[open message close error].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from EventTarget

#__deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener

Constructor Details

#initialize(window, url, protocols = nil) ⇒ WebSocket

Returns a new instance of WebSocket.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/dommy/web_socket.rb', line 32

def initialize(window, url, protocols = nil)
  @window = window
  @url = url.to_s
  @ready_state = CONNECTING
  @buffered_amount = 0
  @extensions = ""
  @binary_type = "blob"
  @protocol = Array(protocols).first.to_s
  @sent_messages = []
  @inline_handlers = {}

  # Auto-open via microtask unless tests disable.
  auto_open = window.globals["__ws_auto_open__"]
  @window.scheduler.queue_microtask(proc { __simulate_open__ }) unless auto_open == false
end

Instance Attribute Details

#binary_typeObject

Returns the value of attribute binary_type.



30
31
32
# File 'lib/dommy/web_socket.rb', line 30

def binary_type
  @binary_type
end

#buffered_amountObject (readonly)

Returns the value of attribute buffered_amount.



29
30
31
# File 'lib/dommy/web_socket.rb', line 29

def buffered_amount
  @buffered_amount
end

#extensionsObject (readonly)

Returns the value of attribute extensions.



29
30
31
# File 'lib/dommy/web_socket.rb', line 29

def extensions
  @extensions
end

#protocolObject (readonly)

Returns the value of attribute protocol.



29
30
31
# File 'lib/dommy/web_socket.rb', line 29

def protocol
  @protocol
end

#ready_stateObject (readonly)

Returns the value of attribute ready_state.



29
30
31
# File 'lib/dommy/web_socket.rb', line 29

def ready_state
  @ready_state
end

#urlObject (readonly)

Returns the value of attribute url.



29
30
31
# File 'lib/dommy/web_socket.rb', line 29

def url
  @url
end

Instance Method Details

#__event_parent__Object



154
155
156
# File 'lib/dommy/web_socket.rb', line 154

def __event_parent__
  nil
end

#__js_call__(method, args) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/dommy/web_socket.rb', line 139

def __js_call__(method, args)
  case method
  when "send"
    send(args[0])
  when "close"
    close(args[0] || 1000, args[1] || "")
  when "addEventListener"
    add_event_listener(args[0], args[1], args[2])
  when "removeEventListener"
    remove_event_listener(args[0], args[1])
  when "dispatchEvent"
    dispatch_event(args[0])
  end
end

#__js_get__(key) ⇒ Object

— JS bridge ————————————————-



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/dommy/web_socket.rb', line 100

def __js_get__(key)
  case key
  when "url"
    @url
  when "readyState"
    @ready_state
  when "bufferedAmount"
    @buffered_amount
  when "extensions"
    @extensions
  when "protocol"
    @protocol
  when "binaryType"
    @binary_type
  when "CONNECTING"
    CONNECTING
  when "OPEN"
    OPEN
  when "CLOSING"
    CLOSING
  when "CLOSED"
    CLOSED
  else
    @inline_handlers[inline_event_for(key)]
  end
end

#__js_set__(key, value) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/dommy/web_socket.rb', line 127

def __js_set__(key, value)
  case key
  when "binaryType"
    @binary_type = value.to_s
  else
    event = inline_event_for(key)
    set_inline_handler(event, value) if event
  end

  nil
end

#__sent_messages__Object

— Test seams ————————————————



65
66
67
# File 'lib/dommy/web_socket.rb', line 65

def __sent_messages__
  @sent_messages.dup
end

#__simulate_close__(code = 1000, reason = "", was_clean: true) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/dommy/web_socket.rb', line 82

def __simulate_close__(code = 1000, reason = "", was_clean: true)
  @ready_state = CLOSED
  dispatch_event(
    CloseEvent.new(
      "close",
      "code" => code,
      "reason" => reason,
      "wasClean" => was_clean
    )
  )
end

#__simulate_error__Object



94
95
96
# File 'lib/dommy/web_socket.rb', line 94

def __simulate_error__
  dispatch_event(Event.new("error"))
end

#__simulate_message__(data) ⇒ Object



76
77
78
79
80
# File 'lib/dommy/web_socket.rb', line 76

def __simulate_message__(data)
  return if @ready_state != OPEN

  dispatch_event(MessageEvent.new("message", "data" => data))
end

#__simulate_open__Object



69
70
71
72
73
74
# File 'lib/dommy/web_socket.rb', line 69

def __simulate_open__
  return if @ready_state != CONNECTING

  @ready_state = OPEN
  dispatch_event(Event.new("open"))
end

#close(code = 1000, reason = "") ⇒ Object



55
56
57
58
59
60
61
# File 'lib/dommy/web_socket.rb', line 55

def close(code = 1000, reason = "")
  return if @ready_state == CLOSED || @ready_state == CLOSING

  @ready_state = CLOSING
  @window.scheduler.queue_microtask(proc { __simulate_close__(code, reason) })
  nil
end

#send(data) ⇒ Object

Raises:



48
49
50
51
52
53
# File 'lib/dommy/web_socket.rb', line 48

def send(data)
  raise Error, "WebSocket not OPEN" if @ready_state != OPEN

  @sent_messages << data
  nil
end