Skip to content

Commit

Permalink
Sync with SVN (we'll do a better job at this in the future)
Browse files Browse the repository at this point in the history
  • Loading branch information
maccman committed Sep 2, 2008
1 parent 408da88 commit 948b2ab
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 82 deletions.
59 changes: 58 additions & 1 deletion README
Expand Up @@ -165,7 +165,64 @@ You'll need to add the Rails IP to juggernaut.yml, like so:
:allowed_ips: :allowed_ips:
- 127.0.0.1 - 127.0.0.1
- 192.168.0.4 # IP of the Rails app - 192.168.0.4 # IP of the Rails app


===============================================
Jquery
===============================================

To get Juggernaut working with Jquery (Prototype is used by default) follow the tutorial above with the following differences.

>>Javascripts

You must have jquery.js (version 1.2.6 tested) and the jquery-json plugin (http://www.jdempster.com/wp-content/uploads/2007/08/jquery.json.js) in the /javascripts directory.

You need the jquerynaut.js file in the /javascripts/juggernaut directory (found in /lib in the media directory)


>>The chat controller:

class ChatController < ApplicationController
def index
end

def send_data
render :juggernaut do |page|
page["#chat_data"].prepend "<li>#{h params[:chat_input]}</li>"
end
render :nothing => true
end

end


>>The index.html.erb

<html>
<head>
<%= javascript_include_tag 'jquery', 'json', 'juggernaut/juggernaut', 'juggernaut/jquerynaut', 'juggernaut/swfobject' %>
<%= juggernaut %>
</head>
<body>
<form action="/chat/send_data" method="get">
<div style="margin:0;padding:0">
<input id="chat_input" name="chat_input" size="20" type="text" value="" />
<input name="commit" type="submit" value="Add" />
</form>

<script>
$(document).ready(function(){
$('form').submit(function(){
$.get('/chat/send_data', { chat_input: $('#chat_input').val() } )
return false;
})
})
</script>
<ul id="chat_data" style="list-style:none"></ul>
</body>
</html>



=============================================== ===============================================
Troubleshooting Troubleshooting
=============================================== ===============================================
Expand Down
52 changes: 44 additions & 8 deletions lib/juggernaut.rb
Expand Up @@ -3,6 +3,7 @@


module Juggernaut module Juggernaut
CONFIG = YAML::load(ERB.new(IO.read("#{RAILS_ROOT}/config/juggernaut_hosts.yml")).result).freeze CONFIG = YAML::load(ERB.new(IO.read("#{RAILS_ROOT}/config/juggernaut_hosts.yml")).result).freeze
CR = "\0"


class << self class << self


Expand Down Expand Up @@ -71,26 +72,61 @@ def remove_all_channels(channels)
} }
send_data(fc) send_data(fc)
end end

def show_clients
fc = {
:command => :query,
:type => :show_clients
}
send_data(fc, true).flatten
end

def show_client(client_id)
fc = {
:command => :query,
:type => :show_client,
:client_id => client_id
}
send_data(fc, true).flatten[0]
end

def show_clients_for_channels(channels)
fc = {
:command => :query,
:type => :show_clients_for_channels,
:channels => channels
}
send_data(fc, true).flatten
end
alias show_clients_for_channel show_clients_for_channels


def send_data(hash) def send_data(hash, response = false)
hash[:channels] = hash[:channels].to_a if hash[:channels] hash[:channels] = hash[:channels].to_a if hash[:channels]
hash[:client_ids] = hash[:client_ids].to_a if hash[:client_ids] hash[:client_ids] = hash[:client_ids].to_a if hash[:client_ids]


CONFIG[:hosts].select {|h| !h[:environment] or h[:environment] == ENV['RAILS_ENV'].to_sym }.each do |address| res = []
hosts.each do |address|
begin begin
hash[:secret_key] = address[:secret_key] if address[:secret_key] hash[:secret_key] = address[:secret_key] if address[:secret_key]


@socket = TCPSocket.new(address[:host], address[:port]) @socket = TCPSocket.new(address[:host], address[:port])
# the \0 is to mirror flash # the \0 is to mirror flash
@socket.print(hash.to_json + "\0") @socket.print(hash.to_json + CR)
@socket.flush @socket.flush
# rescue => e res << @socket.readline(CR) if response
# puts e
# false
ensure ensure
@socket.close if @socket and !@socket.closed? @socket.close if @socket and !@socket.closed?
end end
end end
res.collect {|r| ActiveSupport::JSON.decode(r.chomp!(CR)) } if response
end

private

def hosts
CONFIG[:hosts].select {|h|
!h[:environment] or h[:environment].to_s == ENV['RAILS_ENV']
}
end end


end end
Expand Down
169 changes: 96 additions & 73 deletions media/juggernaut.js
@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2007 Alexander MacCaw Copyright (c) 2008 Alexander MacCaw
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
Expand All @@ -21,103 +21,129 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */


if (typeof Prototype == 'undefined') throw("Juggernaut error. Prototype could not be found."); function Juggernaut(options) {
if (Prototype.Version < "1.6") throw("Juggernaut error. Prototype 1.6.0 is required."); this.is_connected = false,
this.attempting_to_reconnect = false
this.ever_been_connected = false
this.hasFirebug = "console" in window && "firebug" in window.console
this.options = options
this.bindToWindow();
}


var Juggernaut = Class.create({ Juggernaut.fn = Juggernaut.prototype
is_connected: false,
attempting_to_reconnect: false,
ever_been_connected: false,
hasFirebug: "console" in window && "firebug" in window.console,


logger: function(msg) { Juggernaut.fn.logger = function(msg) {
if (this.options.debug) { if (this.options.debug) {
msg = "Juggernaut: " + msg + " on " + this.options.host + ':' + this.options.port; msg = "Juggernaut: " + msg + " on " + this.options.host + ':' + this.options.port;
this.hasFirebug ? console.log(msg) : alert(msg); this.hasFirebug ? console.log(msg) : alert(msg);
} }
}, }


fire_event: function(fx_name) { Juggernaut.fn.initialized = function(){
$(document).fire("juggernaut:" + fx_name);
},

initialize: function(options) {
this.options = options;
Event.observe(window, 'load', function() {
juggernaut = this;
this.appendFlashObject()
}.bind(this));
},

initialized: function(){
this.fire_event('initialized'); this.fire_event('initialized');
this.connect(); this.connect();
}, }


broadcast: function(body, type, client_ids, channels){ Juggernaut.fn.broadcast = function(body, type, client_ids, channels){
var msg = new Hash(); var msg = {command: 'broadcast', body: body, type: (type||'to_channels')}
msg.set('command', 'broadcast'); if(channels) msg['channels'] = channels;
msg.set('body', body); if(client_ids) msg['client_ids'] = client_ids;
msg.set('type', (type||'to_channels')); this.sendData(Juggernaut.toJSON(msg));
if(channels) msg.set('channels', channels); }
if(client_ids) msg.set('client_ids', client_ids);
this.sendData(msg.toJSON());
},


sendData: function(data){ Juggernaut.fn.sendData = function(data){
this.swf().sendData(escape(data)); this.swf().sendData(escape(data));
}, }


connect: function(){ Juggernaut.fn.connect = function(){
if(!this.is_connected){ if(!this.is_connected){
this.fire_event('connect'); this.fire_event('connect');
this.swf().connect(this.options.host, this.options.port); this.swf().connect(this.options.host, this.options.port);
} }
}, }


disconnect: function(){ Juggernaut.fn.disconnect = function(){
if(this.is_connected) { if(this.is_connected) {
this.swf().disconnect(); this.swf().disconnect();
this.is_connected = false; this.is_connected = false;
} }
}, }

connected: function(e) { Juggernaut.fn.handshake = function() {
var handshake = new Hash(); var handshake = {};
handshake.set('command', 'subscribe'); handshake['command'] = 'subscribe';
if(this.options.session_id) handshake.set('session_id', this.options.session_id); if(this.options.session_id) handshake['session_id'] = this.options.session_id;
if(this.options.client_id) handshake.set('client_id', this.options.client_id); if(this.options.client_id) handshake['client_id'] = this.options.client_id;
if(this.options.channels) handshake.set('channels', this.options.channels); if(this.options.channels) handshake['channels'] = this.options.channels;
if(this.currentMsgId) { if(this.currentMsgId) {
handshake.set('last_msg_id', this.currentMsgId); handshake['last_msg_id'] = this.currentMsgId;
handshake.set('signature', this.currentSignature); handshake['signature'] = this.currentSignature;
} }
this.sendData(handshake.toJSON());
return handshake;
}

Juggernaut.fn.connected = function(e) {
var json = Juggernaut.toJSON(this.handshake())
this.sendData(json);
this.ever_been_connected = true; this.ever_been_connected = true;
this.is_connected = true; this.is_connected = true;
setTimeout(function(){ setTimeout(function(){
if(this.is_connected) this.attempting_to_reconnect = false; if(this.is_connected) this.attempting_to_reconnect = false;
}.bind(this), 1 * 1000); }.bind(this), 1 * 1000);
this.logger('Connected'); this.logger('Connected');
this.fire_event('connected'); this.fire_event('connected');
}, }


receiveData: function(e) { // OVERRIDE FOR CHAT STYLE APPS - POSSIBLE MALICIOUS CONTENT CAN BE EVALED
var msg = unescape(e.toString()).evalJSON(); Juggernaut.fn.receiveData = function(e) {
var msg = Juggernaut.parseJSON(unescape(e.toString()));
this.currentMsgId = msg.id; this.currentMsgId = msg.id;
this.currentSignature = msg.signature; this.currentSignature = msg.signature;
this.logger("Received data:\n" + msg.body + "\n"); this.logger("Received data:\n" + msg.body + "\n");
eval(msg.body); eval(msg.body);
}, }

var juggernaut;

/*** START PROTOTYPE SPECIFIC - OVERRIDE FOR OTHER FRAMEWORKS ***/
Juggernaut.fn.fire_event = function(fx_name) {
$(document).fire("juggernaut:" + fx_name);
}

Juggernaut.fn.bindToWindow = function() {

Event.observe(window, 'load', function() {
juggernaut = this;
this.appendFlashObject()
}.bind(this));

}

Juggernaut.toJSON = function(hash) {
return Object.toJSON(hash);
}

Juggernaut.parseJSON = function(string) {
return string.evalJSON();
}

Juggernaut.fn.swf = function(){
return $(this.options.swf_name);
}


appendFlashObject: function(){ Juggernaut.fn.appendElement = function() {
this.element = new Element('div', { id: 'juggernaut' });
$(document.body).insert({ bottom: this.element });
}

/*** END PROTOTYPE SPECIFIC ***/

Juggernaut.fn.appendFlashObject = function(){
if(this.swf()) { if(this.swf()) {
throw("Juggernaut error. 'swf_name' must be unique per juggernaut instance."); throw("Juggernaut error. 'swf_name' must be unique per juggernaut instance.");
} }
this.element = new Element('div', { Juggernaut.fn.appendElement();
id: 'juggernaut'
});
$(document.body).insert({ bottom: this.element });
swfobject.embedSWF( swfobject.embedSWF(
this.options.swf_address, this.options.swf_address,
'juggernaut', 'juggernaut',
Expand All @@ -129,36 +155,33 @@ var Juggernaut = Class.create({
{}, {},
{'id': this.options.swf_name, 'name': this.options.swf_name} {'id': this.options.swf_name, 'name': this.options.swf_name}
); );
}, }

swf: function(){
return $(this.options.swf_name);
},


refreshFlashObject: function(){
Juggernaut.fn.refreshFlashObject = function(){
this.swf().remove(); this.swf().remove();
this.appendFlashObject(); this.appendFlashObject();
}, }


errorConnecting: function(e) { Juggernaut.fn.errorConnecting = function(e) {
this.is_connected = false; this.is_connected = false;
if(!this.attempting_to_reconnect) { if(!this.attempting_to_reconnect) {
this.logger('There has been an error connecting'); this.logger('There has been an error connecting');
this.fire_event('errorConnecting'); this.fire_event('errorConnecting');
this.reconnect(); this.reconnect();
} }
}, }


disconnected: function(e) { Juggernaut.fn.disconnected = function(e) {
this.is_connected = false; this.is_connected = false;
if(!this.attempting_to_reconnect) { if(!this.attempting_to_reconnect) {
this.logger('Connection has been lost'); this.logger('Connection has been lost');
this.fire_event('disconnected'); this.fire_event('disconnected');
this.reconnect(); this.reconnect();
} }
}, }


reconnect: function(){ Juggernaut.fn.reconnect = function(){
if(this.options.reconnect_attempts){ if(this.options.reconnect_attempts){
this.attempting_to_reconnect = true; this.attempting_to_reconnect = true;
this.fire_event('reconnect'); this.fire_event('reconnect');
Expand All @@ -179,4 +202,4 @@ the first in ' + (this.options.reconnect_intervals || 3) + ' seconds');
} }
} }


});

0 comments on commit 948b2ab

Please sign in to comment.