Skip to content

Instantly share code, notes, and snippets.

@digitalextremist
Created April 23, 2013 06:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save digitalextremist/5441315 to your computer and use it in GitHub Desktop.
Save digitalextremist/5441315 to your computer and use it in GitHub Desktop.
Partial ( somewhat messy )snippet of WebSocket support implemented in Reel using Rack hijack_io's pre-header form. Client side is using portal.js and jQuery Mobile. Mu is a base class of utilities (such as Mu.debug to output data to the console) and Mu.socket to manipulate the socket itself, once captured. Ma is an instance of Sinatra::Base with…
require 'forwardable'
require "websocket/protocol/hybi"
module ProtocolAlias
def << data
text data
end
end
class Mu
module Maai
class Sync
include Celluloid
include Celluloid::Notifications
include Celluloid::Logger
extend Forwardable
attr_reader :env, :url
attr_accessor :handler
def_delegators :@io, :closed?
def initialize( env )
env['rack.hijack'].call
@env = env
@io = env['rack.hijack_io']
@url = Mu.request.url.sub( "http://", "ws://" )
@handler = WebSocket::Protocol.rack self
@handler.extend(ProtocolAlias)
@handler.start
async.listen
@handler
end
def close
@io.close
end
def write( data ); @io.write data end
def listen
loop {
begin
@handler.parse @io.read #de partial(1024)
rescue => e
Mu.debug e, "closing socket // ..in listen?"
end
sleep 0.1
}
end
def connected?
begin
return !@io.closed?
rescue
false
end
end
end
end
end
class Mu
class Sync < Ma
before do; Mu.protected! end
get "/" do
case Mu.params['when']
when 'open'
begin
Mu.socket! Maai::Sync.new( env ).handler if !Mu.socket? and Is.member?
rescue => e
Mu.debug e, "Error saving socket //"
end
if Mu.socket?
Mu.socket.onmessage { |event|
data = JSON.parse event.data
begin
case data["type"]
when 'participant'
Maai::Participant.event data["data"]
else
Mu.debug data, "unknown event //"
end
rescue => e
Mu.debug e, "Closed on message //"
Mu.socket.close
end
Mu.socket.onerror { |event| Mu.socket.close }
Mu.socket.onclose { |status,event| Mu.socket.close }
Mu.thread! {
loop {
if Mu.present?
if ( sending = Maai::Alert.syncing ).count > 0
if Mu.present?
begin
checking = Time.now
Mu.socket << JSON.generate( :type => "sync", :data => sending.merge({ :moment => Time.now }) )
rescue => e
Mu.debug e, "Closed on activity //"
Mu.socket.close
end
Mu.alive[:notifications].select! { |alert| alert[:moment] >= checking }
end
end
end
sleep 0.1
}
}
}
sleep 0.1
end
when 'abort'
begin
Mu.socket.close if Mu.socket?
rescue => e
Mu.debug e, "Fail on abort //"
end
else
Mu.debug "fell through 'when' //"
end
end
end
end
$(document).on( 'pagebeforeshow', function(event, ui) {
if ( !mu_waiting && is_mu_authorized && socketReady() ) { synchronization( mu_key ) }
$( window ).off( "focus" ).on( "focus", iamPresent );
$( window ).off( "blur" ).on( "blur", function() { if ( mu_awake ) iamDormant() } );
})
function socketSend( code, data, on_done, on_fail ) {
if ( code == undefined ) code = "sync";
if ( on_done == undefined ) on_done = function() {}
if ( on_fail == undefined ) on_fail = function() {}
if ( socketLink != undefined && socketLink.state() == "opened" ) {
socketLink.send( code, data, on_done, on_fail )
} else {
setTimeout( function() { socketSend( code, data, on_done, on_fail ) }, <%= JS_TIMEOUT_LENGTH * 10 %> )
}
}
function iamPresent( e ) { updateAwake( mu_awake = true ) }
function iamDormant( e ) { updateAwake( mu_awake = false ) }
function updateAwake( state ) { if ( is_mu_authorized ) socketSend( "participant", { code: 'presence', present: state } ) }
if ( socketLink == undefined ) var socketLink
function handleSyncEvent( data ) {
if ( !mu_awake && 'notifications' in data ) {
delete data['notifications']
}
processAlerts( data )
}
function socketReady() { return ( ( sync = portal.find( "/!sync" ) ) == undefined || sync.state() == 'closed' ) }
function synchronization() {
socketLink = portal.open( "/!sync", { notifyAbort: true } ).on( "sync", handleSyncEvent );
updateAwake()
}
#de ... snippet of route.
#de bypass Lint to deal with the WebSocket directly. Not necessary, per se.
module Rack
class Lint
def call(env = nil)
@app.call(env)
end
end
end
map "/!sync" do; run Mu::Sync end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment