June 8th, 2011, 9:29 AM

Tutorial: Ruby on Rails 3 and Faye

by José Corcuera

Faye is a publish-subscribe messaging system that provides message servers for Node.js and Rack. We can take advantage of this system in many ways, for example by creating a chat, sending notifications, even playing games.

Fraye and Ruby on Rails

Previously I created a basic to-do application using Rails 3, so we’ll use this as our starting point. The application is on https://github.com/BellandWhistle/rails3-todo-app. In a future post I’ll show you how to create this by yourself, but for now my goal is to show you how to integrate rails with faye.

TO-DO App Review:

In this application you can create projects and tasks inside projects, with each task having a state like initial, in-progress, verify, done. The app uses ajax to create and update the tasks.

This app works fine for a single person but when several users try to use it at the same time, it’s not reliable. If I update a task, other people using the app won’t see my changes. They’ll be working with the wrong information. That’s where faye comes in. The idea is to subscribe people on the same project so they can see the changes other people make in real-time. So let’s do it!

Setting up the project:

$ git clone https://github.com/BellandWhistle/rails3-todo-app
$ bundle install
$ bundle exec rake db:create && db:migrate
$ bundle exec rake db:seed
$ bundle rails s

Go to http://localhost:3000/projects. There are some tasks (Things to check for rails 3.1) created in the initial state. If we change the state this will remove the current task and add it to the new state list.

Faye Installation:

Add faye to your Gemfile. It requires eventmachine, em-http-request, rack, thin and json, which should all be automatically installed.

gem 'faye', '0.6.1'
view raw Gemfile This Gist brought to you by GitHub.

*Faye gem version 0.6.0 has a bug with Firefox, so I’d recommend using 0.5.5 until the 0.6.1 release. (Update: faye 0.6.1 was release on June 6th).

All Faye clients communicate with a central server that processes the request and sends the response to the subscribers.

Create a file faye.ru in root app:

require 'faye'
faye_server = Faye::RackAdapter.new(:mount => '/faye', :timeout => 45)

run faye_server
view raw faye.ru This Gist brought to you by GitHub.

We’re using rack to run the faye server and mounting the application on /faye so it’s accessible through http://localhost:9292/faye to interact with the server if we run it (this will run on port 9292 by default).

$ bundle exec rackup faye.ru -s thin -E production

Finally, if you go to http://localhost:9292/faye.js you’ll be able to see the faye client script.

Using Faye on Rails:

Let’s use faye client on our views and add faye client on the application layout:

<head>
...
<%= javascript_include_tag 'http://localhost:9292/faye.js' %>
</head>

On projects/show.html.erb, create a new faye client pointing to the faye server and subscribe it to the project path.

<% javascript_tag do %>
  $(function() {
    var client = new Faye.Client('http://localhost:9292/faye');
    client.subscribe("<%= project_path(@project) %>", function(data) {
      // do stuff here when receives the data
    });
  });
<% end %>

When a user updates a task, we have 3 options:

1. Use faye client to update the other clients. If the operation doesn’t need to store data I’d recommend this option. In our case it’s essential to communicate with the other clients only when a successful operation happens so I’d probably use this option for a chat.

2. Send request to rails server and if the operation is success, create a faye client on the server and send the response to all the clients:

#Using Faye client on server

require 'eventmachine'

class TasksController < ApplicationController
  ...

  def update
  ...
    if @task.update_attributes(params[:task])
     client = Faye::Client.new('http://localhost:9292/faye')
     client.publish(project_path(@project), 'text' => 'Update html view!')
    else
     #show errors
    end
    ...
  end

  ...
end

But this approach has a problem, it requires eventmachine for subscribe and publish. In this case I don’t see the need to publish async. But the advantage is that if the connection fails it will try to send the request again.

3. Use HTTP interface to post messages. I like this solution because in our case we are not subscribing (in the server) so we don’t need async to publish, we just have to make a post.

#Using Faye HTTP interface

def update
  @task = Task.find(params[:id])
  if @task.update_attributes(params[:task])
    message = {:channel => project_path(@task.project), :data => { :task_id => @task.id, :task_state => @task.state}}
    uri = URI.parse("http://localhost:9292/faye")
    Net::HTTP.post_form(uri, :message => message.to_json)
  end
  render :nothing => true
end

And handle the data on the view:

<% javascript_tag do %>
  $(function() {
    var client = new Faye.Client('http://localhost:9292/faye');
    client.subscribe("<%= project_path(@project) %>", function(data) {
      var task_view = $('#task-'+ data.task_id).remove();
      $('#'+ data.task_state + '-tasks').append(task_view).fadeIn();
    });
  });
<% end %>
view raw show.html.erb This Gist brought to you by GitHub.

Authentication: One thing to keep in mind is that our current application is not safe because any person can make a post to our channel. We need to add an extension to protect against this. If you want to take a look to extensions, visit the Faye site or look Ryan Bate’s screencast about faye.

You can download the whole project here: https://github.com/BellandWhistle/rails3-faye-example

That’s it! It’s really easy to use faye to communicate with other clients, so give it a try and let us know how it goes in the comments.

2 Responses to Tutorial: Ruby on Rails 3 and Faye

  1. Great tutorial, thanks for sharing. I had to add add (require ‘net/http’) to my config/application.rb file to get it working.

  2. Thanks for posting. Do you know of any online tutorials / examples of people using Faye for games?

Leave a Reply

Enter your information in the form below or Login with Facebook