Juggernaut =========== =CONTACT DETAILS Author: Alex MacCaw E-Mail Address: info@alexmaccaw.co.uk License: MIT Website: http://juggernaut.rubyforge.org Blog: http://www.eribium.org =DESCRIPTION The Juggernaut plugin for Ruby on Rails aims to revolutionize your Rails app by letting the server initiate a connection and push data to the client. In other words your app can have a real time connection to the server with the advantage of instant updates. Although the obvious use of this is for chat, the most exciting prospect for me is collaborative cms and wikis. What Happens: 1. Client A opens socket connection to the socket server 2. Client B makes Ajax call to Rails 3. Rails sends message to the socket server 4. Socket server broadcasts message to clients Juggernaut Features: * Allows a real time connection with a client - Rails can literally push javascript in real time to the client which is then evaluated. * Push server - written in Ruby. * Integrated, as a plugin, into Rails. * Subscribers can subscribe to multiple channels, and broadcasters can broadcast to multiple channels. Channels are sent as a base64 encoded array - from the session. * Uses Flash 6 - installed on more than 95% of computers. * Supports all the major browsers (uses fscommand): Firefox 1+, IE 6+ and Safari 2+. Requirements: * Rails 1.1+ * json gem (gem install json) * eventmachine gem (gem install eventmachine OR gem install eventmachine-win32) If you're having trouble installing/starting the eventmachine on windows, try eventmachine-win32-0.5.2.gem =============================================== INSTALLATION =============================================== 1. Get the plugin from: http://rubyforge.org/frs/?group_id=1884 or alternatively get it from the svn: svn://rubyforge.org//var/svn/juggernaut/trunk/juggernaut and put it in the dir: vendor/plugins 2. Make sure to include either juggernaut.js or the default JavaScripts (including juggernaut.js) in your views <%= javascript_include_tag 'juggernaut' %> or <%= javascript_include_tag :defaults %> 3. Add this to your view/layout head: <%= listen_to_juggernaut_channels %> 4. Make sure the JSON library is installed (gem install json). 7. Configure the ports and host. This is very important as flash will only allow you to make a socket connection to the same host as the address the client is viewing. Configure the ports in config/juggernaut.yml. And you are all set! ### description below needs to be updated Usage To demonstrate Juggernaut I'll walk you through building a simple chat. Start the push server going by running: ruby script/push_server The chat controller: class ChatController < ApplicationController def index session[:juggernaut_channels] = ["chat_channel"] end def send_data input_data = CGI.escapeHTML(params[:chat_input]) data = "new Insertion.Top('chat_data', '
  • #{input_data}
  • ');" Juggernaut.send_data(data, session[:juggernaut_channels]) render :nothing => true end end The index.rhtml <%= javascript_include_tag :defaults %> <%= listen_to_juggernaut_channels :chat_channel %> <%= form_remote_tag( :url => { :action => :send_data }, :complete => "$('chat_input').value = ''" ) %> <%= text_field_tag( 'chat_input', '', { :size => 20, :id => 'chat_input'} ) %> <%= submit_tag "Add" %> Start the webserver going with: ruby script/server Try it and see what you think. If it doesn't work please visit the faq. As soon as the page loads the 'flash plugin' opens a socket to the push server and subscribes to the appropriate channels. In this case I'm storing the channels I want to subscribe to in the session. When one client initiates the 'send data' command the javascript is created and sent to the push server with the command Juggernaut.send(data,channels). The push server then broadcasts the message to all clients on the selected channels. Advanced Usage Let's take a simple ajaxified rails app with Juggernaut - and then add it. Controller: def milestone_create begin @milestone = Milestone.new(params[:milestone]) @successful = @milestone.save rescue end end RJS (view) if @successful page.remove "#{@milestone.id}_milestone_view_container" end Now the controller with Juggernaut added: def milestone_create begin @milestone = Milestone.new(params[:milestone]) @successful = @milestone.save partial_to_string = Juggernaut.html_and_string_escape(render_to_string(:partial => "milestone")) Juggernaut.send_data(" createMilestone('#{partial_to_string}','#{cookies[:_session_id]}') ", session[:juggernaut_channels]) rescue "alert('Request failed');" end end Add some javascript to application.js: function createMilestone(data, ses_id) { if (getCookie('_session_id') != ses_id) { new Insertion.Top('milestone_list', data); } } Juggernaut Helpers As you can see, Juggernaut provides some helper methods to easily parse strings from 'render_to_string' etc. Juggernaut.parse_string(string) This takes a string, usually a rendered partial. It then escapes the string, and parses it onto one line so it's understood by JavaScript. Juggernaut.string_escape(string) This does exactly what it says on the tin, escapes a string (not url encoding). Juggernaut.html_escape(string) Same as the rails helper h(). Juggernaut.html_and_string_escape(string) Just combines the two methods above - usefull for chats. =============================================== FAQ =============================================== Juggernaut doesn't seem to be connecting. Firstly make sure you're not behind a firewall. My school only lets data through port 80 (which is useless for xmlsocket connections). Or that you're trying to communicate to a different domain. The XMLSocket.connect method can connect only to computers in the same subdomain where the SWF file (movie) resides. Obviously the port number can be different than the one that the swf is served on. You can check to see what ports and addresses flash is trying to communicate with by looking at the html source. The port, address and channels are sent to flash via parameters in the swf address. The host in the parameters should be the same as the host the html file is served from (or at least in the same subdomain). Likewise you can check the port number is over 1024 here too. What flash version does Juggernaut use? Flash socket uses version 8 which is supported by more than 90% of users. Does it work in all browsers? It has been tested in all the major ones: Firefox 1+, IE 6+ and Safari 2+. What are the advantages/disadvantages of using a flash socket over other methods? It's better than comet because: * It's much less of a hack * It doesn't crash your browser (Comet can do this after a while) * 90% of browsers support it (flash 8). * It's much easier to implement * It can use a different port - unlike comet - so you don't need any custom dispatch servlets for forwarding messages through rails to the push server - it can connect directly. It's better than polling because: * Much cleaner * Doesn't use as many server resources Why need an external push server - can't you make everything part of rails? Rails uses FastCGI , each HTTP request to a rails app is handled by a whole rails process. Each rails process takes up more than ten megabytes, so for 100 push connections, more than 1000 mb would be needed. Because of this we need a webserver, external to Rails. Doesn't a Ruby webserver take up a lot of resources and is quite slow? With many threads you might see performance issues. My advice is to re-write the push server in C - please let me know if you've done this. What are you doing to extend this? Extending it to other technologies apart from html, such as Apollo.