
Introduction
Well that picture pretty much sums, it up, this gem my friends, is bad ass. Look, at the picture and think about it, Juggy is pushing up right at you, and you didn’t do anything to provoke him. Well that’s kind of like the Juggernaut gem.
We have all heard of and used AJAX. The browser sends a request for to pull some data, and the server updates the web page without refreshing the whole page. Which is really efficient. But it is two whole requests, and we can do better, so lets cut that down to one.
So how could we cut this bandwidth in half? By having the server just push the data straight up to the client, the browser. The user doesn’t have to ask to pull the info, they just get it. Now Juggernaut uses a pretty clever method of opening and keeping alive Flash socket up for a particular webpage. The server then uses this pipe to push data, in our case usually javascript, up to that smart browser.
Juggernaut follows the Observer or Publisher/Subscriber design pattern. This pattern really lends its self to a chat like application, which is what we are going to create today.
Installation
First off you need to install some prerequisites for the Juggernaut gem to work. Juggernaut uses the very scalable EventMachine and also Json. After you install those, you can install the gem.
1 sudo gem install eventmachine json
2
3 sudo gem install juggernaut
Next you also have to install the Rails plugin too. You can almost think of the plugin as the subscriber(browser/client), and the gem as the publisher(on the server). So let’s create a little chat_sandbox to play around in, and then install the plugin, then run a command to generate a config yaml file.
1 rails chat_sandbox
2
3 cd chat_sandbox
4
5 script/plugin install http://juggernaut.rubyforge.org/svn/trunk/juggernaut
6
7 juggernaut -g juggernaut.yml
Now the plugin will generate a file in config/ called juggernaut_hosts.yml, which is not to be confused with the other configuration file that we generated on the last line which resides at the root of chat_sandbox. They should not need any editing while we are playing around in our sandbox on localhost, but once we get into some heavier apps and deployments, its a whole nother’ blog post.
Now that we have our Juggernaut environment, I will pause and let you setup your Rails environment. Do your database junk, delete the index.html, and any other housekeeping chores.
Next we need to go ahead and set up restful_authentication, which is a great way to facilitate the chat app and usernames. After restful_auth installation, we can create an authenticated user, and then a ChatRoom scaffold.
1 script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/
2
3 script/generate authenticated user sessions
4
5 script/generate scaffold chat_room name:string description:string
6
7 rake db:create
8
9 rake db:migrate
10
11 mv app/views/layouts/chat_rooms.html.erb app/views/layouts/application.html.erb
Code
1 script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/
2
3 script/generate authenticated user sessions
4
5 script/generate scaffold chat_room name:string description:string
6
7 rake db:create
8
9 rake db:migrate
10
11 mv app/views/layouts/chat_rooms.html.erb app/views/layouts/application.html.erb
Finally, we get to write some code now. Lets take care of our routes file really quick, just one minor move of adding a member action. Pretty standard stuff so far.
1 #add this in your routes.rb file 2 map.root :controller => "chat_rooms" 3 map.resources :chat_rooms, :member => {:send_data => :post} 4 map.signup '/signup', :controller => 'users', :action => 'new' 5 map.login '/login', :controller => 'sessions', :action => 'new' 6 map.logout '/logout', :controller => 'sessions', :action => 'destroy'
1 #add this in your routes.rb file
2 map.root :controller => "chat_rooms"
3 map.resources :chat_rooms, :member => {:send_data => :post}
4 map.signup ‘/signup’, :controller => ‘users’, :action => ‘new’
5 map.login ‘/login’, :controller => ‘sessions’, :action => ‘new’
6 map.logout ‘/logout’, :controller => ‘sessions’, :action => ‘destroy’
Continuing on, add the javascript tags for prototype and juggernaut. Now here is the important link that creates the ‘subscriber’. In the top of the views, we call the juggernaut helper, and subscribe to the chat_room.id channel. we also register our client_id for uniqueness. The javascript below is what keeps the chat box scrolling down. And towards the bottom a simple ajax call. Finally at the bottom we have our only css needed to be added to help with the chat room feel inside of a div.
1 #app/views/layouts/application.html.erb, just add this somewhere in the header
2 <%= javascript_include_tag ‘prototype’, :juggernaut %>
3
4 #app/views/chat_rooms/show.html.erb
5 <%= juggernaut(:channels => [chat_room</span>.id], <span style="color:#A60">:client_id</span> => session[<span style="color:#A60">:user_id</span>]) <span style="font-weight:bold;color:#777">%></span></span> <span class="no"> 6</span> <span class="no"> 7</span> <span style="color:#070"><script</span> <span style="color:#007">type</span>=<span style="background-color:#fff0f0;color:#D20"><span style="color:#710">"</span><span style="">text/javascript</span><span style="color:#710">"</span></span><span style="color:#070">></span> <span class="no"> 8</span> function scrollChatPanel(){ <span class="no"> 9</span> div = $('chat_room'); <span class="no"><strong>10</strong></span> div.scrollTop = 0xffff; <span class="no">11</span> } <span class="no">12</span> <span style="color:#070"></script></span> <span class="no">13</span> <span class="no">14</span> <span style="color:#070"><p></span><span style="background:#eee;color:black"><span style="font-weight:bold;color:#777"><%=</span> link_to <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">'</span><span style="">Back</span><span style="color:#710">'</span></span>, chat_rooms_path <span style="font-weight:bold;color:#777">%></span></span><span style="color:#070"></p></span> <span class="no">15</span> <span class="no">16</span> <span style="color:#070"><h1></span><span style="background:#eee;color:black"><span style="font-weight:bold;color:#777"><%=</span>h <span style="color:#33B">chat_room.name %></h1>
17
18 <p><%=h @chat_room.description %></p>
19
20 <div id="chat_room"></div>
21
22
23 <div id="chat_form">
24 <%= form_remote_tag(
25 :url => { :action => :send_data },
26 :complete => "$(‘chat_input’).value = ‘’" ) %>
27
28 <%= text_field_tag( ’chat_input‘, ’‘, { :size => ’50‘, :id => ’chat_input’} ) %>
29 <%= submit_tag "Chat" %>
30 </form>
31 </div>
1 #and for the CSS, just add it at the bottom of scaffold.css or something…
2 #chat_room{
3 width:600px;
4 height:400px;
5 border:1px solid black;
6 overflow-y: scroll;
7 scrollbar-arrow-color:000000;
8 scrollbar-track-color:000000;
9 scrollbar-shadow-color:B1D0F0;
10 scrollbar-face-color:B1D0F0;
11 scrollbar-highlight-color:B1D0F0;
12 scrollbar-darkshadow-color:B1D0F0;
13 scrollbar-3dlight-color:B1D0F0;
14 }
And for the last bit of code, the controller that ties it all together. First off add the before filter to make sure people dont goto a chat page unless they are already logged in. Then toward the bottom add the send_data action which handles the ajax call we set up earlier. The send_data only renders some javascript that it passes off to the Juggernaut push server to get sent out to all the subscribers.
1 #app/controllers/stories_controller.rb, add this at the top
2 before_filter :is_logged_in?, :except => :index
3
4 …code…
5
6 #and this at the bottom
7 def send_data
8 render :juggernaut => {:type => :send_to_channels, :channels => [params[:id].to_i] } do |page|
9 page.insert_html :bottom, ‘chat_room’, "<p>#{current_user.login}: #{h params[:chat_input]}</p>"
10 page.call :scrollChatPanel
11 end
12 render :nothing => true
13 end
14
15 private
16
17 def is_logged_in?
18 if logged_in?
19 true
20 else
21 flash[:error] = "You must be logged in to chat"
22 redirect_to("/login")
23 false
24 end
25 end
Ok, now to start the engines.
1 juggernaut -c juggernaut.yml -d #d is for daemon, c chooses which juggernaut yaml config
2
3 script/server
Juggernaut for the Win?
1 juggernaut -c juggernaut.yml -d #d is for daemon, c chooses which juggernaut yaml config
2
3 script/server
Hope everything went well, and you can create a chat room, and then enter and talk in it. Try opening two different browsers to test out. Remember this uses Adobe Flash version 8, so check the Juggernaut Homepage for more specfic details. Thanks to Alex MacCaw for creating the wonderful gem.

Push is where its at. A.k.a Reverse AJAX, Comet. I can only image the possiblites coming into play with cloud computing and push servers. The webserver could take in a send_message action, then it could notifiy 10 juggernaut servers, who then in turn notify all 1000 of there clients, scale from there. Sounds complicated eh? Luckily ezmobius is leading the way for us with Vertebra. His version will be using XMPP protocol instead of the Flash connection, so I am really excited to see whats comes out of the Yard.
I hope to have comments up soon, just migrated the site over to merb and slicehost, so its kinda slowing me down some.
Now I know this app is a little barebones, but to see what a little more work can do, head over to ShovelChat, a Web Chat Application for Digg Users. There you can (hopefully) see where I have hacked my way through a chat room user list system. I have a little bit I added to the Juggernaut Gem, but it needs some polishing, and I hope to write about it soon. Until next time, I’m dun-skees.

Hey, what do you guys think about the font size/color/style for the code?
Very impressive stuff. Re: the font/color/style, I'd like to see real color coding. The green on black is a little too 1980s terminal for me.
Good job.
Very nice writeup. I look forward to playing around with Juggernaut. One small typo - Ezra's cloud computing framework is called Vertebra.
I've been using Juggernaut for a couple of years, and it's quite nice. If only Flash stopped crashing browsers left and right under Linux/OSX, it'd be perfect. :-)
A few small fixes. one, in your example you say to post in #app/controllers/stories_controller.rb, but I think to be consistent with earlier intructions it should be #app/controllers/chat_rooms_controller.rb Do you have a zip, or git repo of the demo app? Perhaps that would clear up any questions? Thanks
app/views/chat_rooms/show.html.erb I think the text for this page is a bit messed up - can you pls check it and update it? Thanks
is it also possible to create dinamically new channels? and add a user dinamically to a channel? if not, are there alternatives to juggernaut???
Hi all Installed Juggernaut on my server.It's running on my srever.Now what are the changes I have to do in the server and my rails code.Can I follow this tutorial.Pleas help me.I am new to this Thnaks
FYI - you might want to check out StreamHub Push Server.
Great Tutorial. Everything worked great until I got to app/views/chat_rooms/show.html.erb. It looks like some of your site code got mixed in with the code you wrote for the app and it is unreadable. Would love to see what you have. Thanks!