Orator

Orator is a websocket client/server combination that uses events in order to talk to each other. It is still early in its development.
This project uses web-socket-js as well as em-websocket.
Ruby on Rails Integration
Orator integrates easily with Ruby on Rails. Just add
gem 'orator'
to your Gemfile and bundle install away. In order to use it, you'll need to
add the following line to your application.js file:
//= require orator
Now you can use the orator client in your JavaScript! If the browser doesn't support JavaScript, it'll fall back to a flash object.
Server Orators
We'll start with the server-side stuff. A simple rails g orator:config will
place the file orator_config.yml in config/, and an example orator in
app/orators. Feel free to modify the configuration file (I'll add
documentation to it). Let's set up a orator here:
class TestOrator < Orator::Base
This is important. Orator will not accept the Handler unless it is a child
of Orator::Base. Orator::Base implements methods that
are used by Orator.
before do
self.number_of_messages ||= 0
self.number_of_messages += 1
if self.number_of_messages > 9_000
prevent_event
end
end
Here we have a basic before block that increments a number every time we get a message. If the number of messages is over 9,000, it will prevent the event from occuring (but any after blocks will still run).
on :ping
def ping(data)
send message('test.pong', message: 'pong')
end
Here we define a basic event named ping. When the server receives it, it
sends back an event named test.pong, with the message pong. Cool, eh?
on :pong
def pong(data)
puts data["message"] # => "pong"
end
In this we're assuming that somewhere else in our code the client sent us a
test.pong event; maybe they did so in response to a ping that we sent.
Either way, we're outputting a message that we received.
end
Orator.add_orator(TestOrator)
Here we register our orator with Orator, so it can use it for routing events.
But say we also want to handle a socket.open event. Our orator above can't
handle that, sadly, so we use this method:
Orator.add_orator do |events|
events.on('socket.open') do |data|
self.user = {}
send mesasge('test.ping', message: 'ping')
end
events.on('user.alias') do |data|
self.user[:name] = json["name"]
end
end
So when the socket opens in socket.open, we set #user to an empty hash and
send a ping message (whose response will be handled by our TestOrator). When
we receive the user.alias message, we set the key-pair :name => json["name"]
on the #user hash. Cool, eh?
To run the server, run orator --command start in the root rails directory.
To daemonize it, run orator --command start -d. To stop, run
orator --command stop.
JavaScript Client
Let's go through an example together.
$(document).ready(function() {
Orator.setup("ws://host:port/path?query", function(events){
Orator.setup handles setting up our socket for us. It also sets up the
events and routing, as well. There are some locally defined events that are
triggered even if the server hadn't sent them, such as socket.open and
socket.close.
events.on('some.event', function() {})
Here we're binding some event named some.event to an empty function. Boring.
Note that the name of the event doesn't matter here.
events.on('test.ping', function() {
this.server.send('test.pong', { message: 'pong' });
});
Here we're responding to a ping that a server might send to this client. We
send back a test.pong event, with the message 'pong'. The contents of the
event can be anything.
events.on('test.pong', function(data) {
console.log(data.message) // => "pong"
});
Here we received a pong back from the server - this was triggered by the server in response to our ping, which we could have sent at any time.
events.on('socket.open', function() {
this.user = {}
this.server.send('user.alias', { name: some_name })
});
Here we defined an event that will set up a an empty object on the property
user of this. This will become important. We then send the event
user.alias to the server (which probably changes the name of the user) with
the new name (assuming some_name has a string value).
events.on('user.alias', function(data) {
this.user.name = data.name;
});
It is common place for the server's response events to be named the same as the request events (although the response events may be sent at any time, with no context). Here we see setting the name of the user object to the name the server sent back. This is the same user object that we defined in the event above.
});
});
And we close our braces.