# The channel is the connection between the front end and the backend. require 'volt/reactive/events' require 'json' class Channel include ReactiveTags attr_reader :status, :error, :reconnect_interval def initialize @socket = nil @status = :opening @connected = false @error = nil @retry_count = 0 @queue = [] connect! end def connected? @connected end def retry_count @retry_count end def connect! %x{ this.socket = new SockJS('/channel'); this.socket.onopen = function() { self.$opened(); }; this.socket.onmessage = function(message) { self['$message_received'](message.data); }; this.socket.onclose = function(error) { self.$closed(error); }; } end def opened @status = :open @connected = true @reconnect_interval = nil @retry_count = 0 @queue.each do |message| send_message(message) end trigger!('open') trigger!('changed') end def closed(error) @status = :closed @connected = false @error = `error.reason` trigger!('closed') trigger!('changed') reconnect! end def reconnect! @status = :reconnecting @reconnect_interval ||= 0 @reconnect_interval += (2000 + rand(5000)) @retry_count += 1 # Trigger changed for reconnect interval trigger!('changed') interval = @reconnect_interval %x{ setTimeout(function() { self['$connect!'](); }, interval); } end def message_received(message) message = JSON.parse(message) trigger!('message', nil, *message) end tag_method(:send_message) do destructive! end def send_message(message) if @status != :open @queue << message else # TODO: Temp: wrap message in an array, so we're sure its valid JSON message = JSON.dump([message]) %x{ this.socket.send(message); } end end def close! @status = :closed %x{ this.socket.close(); } end end