If you’ve messed around with HTTP push/Comet solutions you’ve probably discovered are a fair amount of free and commercial choices, most solutions a lot to be desired.

For Ruby and Rails specific solutions I tried juggernaut and shooting_star. The client component in both projects frequently didn’t know when they were disconnected from the server and consequently never tried to reestablish the connection. Meh…..

After a few weeks of frustration I discovered Orbited, a really good push server. It’s built with Python, but that matters little. It’s an agnostic daemon that integrates with whatever you like. Orbited ships with support for many protocols out of the box, including IRC, XMPP, and STOMP.

My needs were simple for the time being, so I went with STOMP. So if you want replicate my setup you’ll need to have Orbited + dependencies, a STOMP server and the stomp gem.

Before you dive in, this example is not a guide to proper architecture. It’s purpose is to show you how all the moving parts fit together.

I’m on Ubuntu 8.04, so lets rock n’ roll!!!!!

Orbited + dependencies

sudo apt-get install python2.5-dev

sudo apt-get install python2.5-twisted

Now follow these instructions:

http://orbited.org/wiki/Installation

On the last step I went with simplejson

STOMP Server



easy_install morbid



You can use any STOMP server you like though

Orbited Config

# Example Orbited Configuration file [global] reactor=select # reactor=kqueue # reactor=epoll proxy.enabled = 1 proxy.keepalive = 0 session.ping_interval = 40 session.ping_timeout = 30 [listen] http://:8500 # uncomment to enable ssl on port 8043 using given .key and .crt files #https://:8043 # #[ssl] #key=orbited.key #crt=orbited.crt [static] [access] * -> localhost:61613 * -> your.public.ip.or.domain.com:61613 #localhost:8000 -> irc.freenode.net:6667 [logging] debug=STDERR,debug.log info=STDERR,info.log access=STDERR,info.log warn=STDERR,error.log error=STDERR,error.log #Don't enable debug by default enabled.default=info,access,warn,error # Turn debug on for the "Proxy" logger [loggers] #Proxy=debug,info,access,warn,error

Star ‘Em up!

sudo orbited –config=/path/to/your/orbited.cfg

sudo morbid

NOTE – you’ll have to find your own way to run these in the background in production. I use screen. There are better options and I plan to change that soon.

The Gem

sudo gem install stomp

Ok..that should be all the requirements…..I make no promises. This is all from memory

Rails Integration

This is quick and dirty. Take care in your integration.

In a controller….

require 'stomp' class ChatController < ApplicationController

def send_data unless params[:chat_input].empty? javascript = render_to_string :update do |page| page.insert_html :top, 'chat_data', "MESSAGE : #{h params[:chat_input]}" page[:chat_input].clear page[:chat_input].focus s = Stomp::Client.new s.send(params[:channel],javascript) s.close end end

render :nothing => true end end

Global Rails App Settings (However you prefer to handle them)

APP_SETTINGS[:orbited_host] = ‘localhost’

APP_SETTINGS[:orbited_port] = ‘8500’

APP_SETTINGS[:site_address] = ‘localhost:3000’

In your app_helper.rb

def orbited_javascript [ "<script src=\"http://#{APP_SETTINGS[:orbited_host]}:#{APP_SETTINGS[:orbited_port]}/static/Orbited.js\"></script>", '<script>', ' document.domain = document.domain;', " Orbited.settings.port = #{APP_SETTINGS[:orbited_port]};", " Orbited.settings.hostname = '#{APP_SETTINGS[:orbited_host]}';", ' TCPSocket = Orbited.TCPSocket;', '</script>', "<script src=\"http://#{APP_SETTINGS[:orbited_host]}:#{APP_SETTINGS[:orbited_port]}/static/protocols/stomp/stomp.js\"></script>" ].join("

") end

In a view…

<html> <head> <%= javascript_include_tag :defaults %> <%= orbited_javascript %> <script> onload = function() { // set up shell. var output = $('chat_data'); // set up stomp client. stomp = new STOMPClient(); stomp.onopen = function() { //output.insert({"top" : "Transport openned"}); }; stomp.onclose = function(code) { output.insert({"top" : "Transport closed (code: " + code + ")"}); }; stomp.onerror = function(error) { alert("onerror: " + error); }; stomp.onerrorframe = function(frame) { alert("onerrorframe: " + frame.body); }; stomp.onconnectedframe = function() { output.insert({"top" : "Connected to chat room"}); }; stomp.onmessageframe = function(frame) { eval(frame.body); //output.insert({"top" : frame.body}); }; stomp.connect(document.domain, 61613, 'UNIQUE_ID_PER_CLIENT', ''); setTimeout("stomp.subscribe('CHANNEL_1', {exchange:''})",5000); };

</script>

</head> <body> <%= form_remote_tag( :url => { :controller => 'chat', :action => :send_data } ) %> <%= hidden_field_tag('channel','CHANNEL_1')%> <%= text_field_tag( 'chat_input', '', { :size => 70, :id => 'chat_input'} ) %> <%= submit_tag("Say") %> <%= ('<a href="/login">Login</a> to chat.')%> </form> <div id="chat_data" style="height:300px;border:1px solid black;overflow:scroll;"></div>

</body> </html>

Congrats and Good Luck !

Remember, this is quick and dirty, cut and pasted from my own app. There may be a few syntax errors, but this should get you on the right path.