From c7f00661bf0cc54a73ccdb9d27fa10b0fd806e43 Mon Sep 17 00:00:00 2001 From: Javan Makhmali Date: Thu, 25 Jun 2015 10:21:53 -0400 Subject: Move connection and subscriber code into their own classes --- lib/assets/javascripts/cable/channel.js.coffee | 22 +++++ lib/assets/javascripts/cable/connection.js.coffee | 102 +++++++++++++++++++++ .../javascripts/cable/subscriber_manager.js.coffee | 26 ++++++ 3 files changed, 150 insertions(+) create mode 100644 lib/assets/javascripts/cable/channel.js.coffee create mode 100644 lib/assets/javascripts/cable/connection.js.coffee create mode 100644 lib/assets/javascripts/cable/subscriber_manager.js.coffee (limited to 'lib/assets/javascripts/cable') diff --git a/lib/assets/javascripts/cable/channel.js.coffee b/lib/assets/javascripts/cable/channel.js.coffee new file mode 100644 index 0000000000..645a44e140 --- /dev/null +++ b/lib/assets/javascripts/cable/channel.js.coffee @@ -0,0 +1,22 @@ +class Cable.Channel + constructor: (@cable, params = {}, mixin) -> + @identifier = JSON.stringify(params) + extend(this, mixin) + @cable.subscribers.add(@identifier, this) + + # Perform a channel action with the optional data passed as an attribute + sendAction: (action, data = {}) -> + data.action = action + @sendMessage(data) + + sendMessage: (data) -> + @cable.sendMessage(@identifier, JSON.stringify(data)) + + unsubscribe: -> + @cable.subscribers.remove(@identifier) + + extend = (object, properties) -> + if properties? + for key, value of properties + object[key] = value + object diff --git a/lib/assets/javascripts/cable/connection.js.coffee b/lib/assets/javascripts/cable/connection.js.coffee new file mode 100644 index 0000000000..a318925b97 --- /dev/null +++ b/lib/assets/javascripts/cable/connection.js.coffee @@ -0,0 +1,102 @@ +class Cable.Connection + MAX_CONNECTION_INTERVAL: 5 * 1000 + PING_STALE_INTERVAL: 8 + + constructor: (@cable) -> + @resetPingTime() + @resetConnectionAttemptsCount() + @connect() + + send: (data) -> + if @isConnected() + @websocket.send(JSON.stringify(data)) + true + else + false + + connect: -> + @websocket = @createWebSocket() + + createWebSocket: -> + ws = new WebSocket(@cable.url) + ws.onmessage = @onMessage + ws.onopen = @onConnect + ws.onclose = @onClose + ws.onerror = @onError + ws + + onMessage: (message) => + data = JSON.parse message.data + + if data.identifier is '_ping' + @pingReceived(data.message) + else + @cable.subscribers.notify(data.identifier, "onMessage", data.message) + + onConnect: => + @startWaitingForPing() + @resetConnectionAttemptsCount() + @cable.subscribers.reload() + + onClose: => + @reconnect() + + onError: => + @reconnect() + + isConnected: -> + @websocket?.readyState is 1 + + disconnect: -> + @removeExistingConnection() + @resetPingTime() + @cable.subscribers.notifyAll("onDisconnect") + + reconnect: -> + @disconnect() + + setTimeout => + @incrementConnectionAttemptsCount() + @connect() + , @generateReconnectInterval() + + removeExistingConnection: -> + if @websocket? + @clearPingWaitTimeout() + + @websocket.onclose = -> # no-op + @websocket.onerror = -> # no-op + @websocket.close() + @websocket = null + + resetConnectionAttemptsCount: -> + @connectionAttempts = 1 + + incrementConnectionAttemptsCount: -> + @connectionAttempts += 1 + + generateReconnectInterval: () -> + interval = (Math.pow(2, @connectionAttempts) - 1) * 1000 + if interval > @MAX_CONNECTION_INTERVAL then @MAX_CONNECTION_INTERVAL else interval + + startWaitingForPing: -> + @clearPingWaitTimeout() + + @waitForPingTimeout = setTimeout => + console.log "Ping took too long to arrive. Reconnecting.." + @reconnect() + , @PING_STALE_INTERVAL * 1000 + + clearPingWaitTimeout: -> + clearTimeout(@waitForPingTimeout) + + resetPingTime: -> + @lastPingTime = null + + pingReceived: (timestamp) -> + if @lastPingTime? and (timestamp - @lastPingTime) > @PING_STALE_INTERVAL + console.log "Websocket connection is stale. Reconnecting.." + @reconnect() + else + @startWaitingForPing() + @lastPingTime = timestamp diff --git a/lib/assets/javascripts/cable/subscriber_manager.js.coffee b/lib/assets/javascripts/cable/subscriber_manager.js.coffee new file mode 100644 index 0000000000..4f46efe817 --- /dev/null +++ b/lib/assets/javascripts/cable/subscriber_manager.js.coffee @@ -0,0 +1,26 @@ +class Cable.SubscriberManager + constructor: (@cable) -> + @subscribers = {} + + add: (identifier, subscriber) -> + @subscribers[identifier] = subscriber + if @cable.sendCommand(identifier, "subscribe") + @notify(identifier, "onConnect") + + reload: -> + for identifier in Object.keys(@subscribers) + if @cable.sendCommand(identifier, "subscribe") + @notify(identifier, "onConnect") + + remove: (identifier) -> + if subscriber = @subscribers[identifier] + @cable.sendCommand(identifier, "unsubscribe") + delete @subscribers[identifier] + + notifyAll: (event, args...) -> + for identifier in Object.keys(@subscribers) + @notify(identifier, event, args...) + + notify: (identifier, event, args...) -> + if subscriber = @subscribers[identifier] + subscriber[event]?(args...) -- cgit v1.2.3