Willian Fernandes

Faye – Como eu criei um simples chat usando Rails

Comente »

Recentemente precisei criar um chat para integrar com a aplicação da empresa que trabalho. Sim, mais um desenvolvimento de chat. Como dizem por ai, chat é o novo Hello World.

Já tinha ouvido falar do Faye, até o Ryan Bates fez um Railscasts sobre o assunto.

O Faye tem módulos para Ruby e Node.js.

Minha primeira tentativa foi utilizar a versão ruby do Faye. Essa versão nada mais é que uma aplicação Rack. Funcionou muito bem, até chegar em produção. O servidor que ele utiliza é o thin, e para a quantidade de requests que minha aplicação faz ao chat o servidor não durava 3 minutos em pé.

Depois de muito quebrar a cabeça resolvi partir para a versão em Node.js do Faye que resolveu perfeitamente esse problema. Vou mostrar abaixo como fiz para utilizando Faye, Node.js e Rails:

Vamos ao código

Primeiro você precisa ter o Node.js e o NPM instalados.

Na raiz da aplicação, instale o faye utilizando o npm:

$ npm install faye

Logo em seguida, também na raiz da aplicação, criei o arquivo abaixo:
faye_server.js

var http = require('http')
  , faye = require('faye');
 
var Logger = {
	incoming: function(message, callback) {
		console.log(message);
		console.log('====================');
 
		callback(message);
	}
};
 
var bayeux = new faye.NodeAdapter({mount: '/faye', timeout: 60});
bayeux.addExtension(Logger);
bayeux.listen(9292);
 

Para testar se o faye está corretamente instalado e funcionando, rode o servidor:

$ node faye_server.js

E abra no seu nevegador a URL http://localhost:9292/faye.js. Se um arquivo JavaScript for carregar, então o Faye está funcionando corretamente. #WIN

Client-side

No seu arquivo de layouts (ex.: application.html.erb) você precisa adicionar o JavaScript do Faye:

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

Agora basta criar uma instância para o Client do Faye e o canal que receberá as mensagens do nosso chat:
application.js

$(function() {
	var faye_client = new Faye.Client('http://localhost:9292/faye');
	faye_client.subscribe('/chat' , function(data) {
		eval(data);
	});
});
 

Back-end

Criei um simples formulário que faz um POST para MessagesController::create utilizando AJAX:
app/views/messages/index.html.erb

<h1>Messages</h1>
 
<div id="messages">
	<ol></ol>
</div>
 
<%= form_for @message, remote: true do |f| %>
	<%= f.label :content %>
	<%= f.text_field :content %>
	<%= f.submit "Send" %>
<% end %>
 

E no controller MessagesController:

class MessagesController < ApplicationController
  def index
    @message = Message.new
  end
 
  def create
    @message = Message.create(params[:message])
  end
end
 

Simples, certo?

O segredo está na view app/views/messages/create.js.erb:

<% broadcast "/chat" do %>
	$("#messages ol").append($("<li><%= @message.content %></li>"));
<% end %>
$("#message_content").val('');
 

Seguindo a dica do Ryan Bates, criei um helper para facilitar o envio das mensagens para o Faye Server. Ele será responsável em fazer o broadcast para todos os usuários conectados no chat.

app/helpers/messages_helper.rb

module MessagesHelper
  def broadcast(channel, &block)
    message = { channel: channel, data: capture(&block) }
    uri     = URI.parse("http://localhost:9292/faye")
    Net::HTTP.post_form(uri, message: message.to_json)
  end
end
 

Como este helper está usando o Net::HTTP, precisamos adicioná-lo na nossa aplicação. Eu normalmente faço o require dessa lib no arquivo config/application.rb:

# ...
 
require 'rails/all'
require 'net/http'
 
# ...

Com isso nosso chat está pronto!

Lógico que na minha aplicação existem processos um pouco mais complexos, lidando com chat privado (estilo Facebook) e autenticação de usuários. Mas para exemplo, esse caso mais simples vale.

Você pode baixar a aplicação completa diretamente do meu GitHub:
https://github.com/willian/faye_chat_example

Fácil, não!? :)