Created
April 23, 2013 06:42
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(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() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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