- 使用环境
rails3, jquery, faye,jquery-rails, nofity-generator
- 资料
http://faye.jcoglan.com/
https://github.com/jcoglan/faye
http://railscasts.com/episodes/260-messaging-with-faye
https://github.com/ryanb/railscasts-episodes/blob/master/episode-260/chatter-after
- init project
rails new chatter --database=mysql -J
- 修改database.yml 和 Gemfile文件
gem 'mysql'
gem 'jquery-rails'
gem 'nifty-generators', :group => :development
- bundle
bundle install
- 运行jquery-rails
rails generate jquery:install --ui
- 修改config/application.rb
config.action_view.javascript_expansions[:defaults] = %w(jquery jquery-ui jquery_ujs)
- 生成脚手架
rails g nifty:scaffold Message content:string
- 修改表单
<%= form_for @message, :remote => true do |f| %>
<p>
<%= f.text_field :content %>
</p>
<p><%= f.submit "Send Message" %></p>
<% end %>
增加remote,ajax访问
- 增加_message.html.erb页面
<li>
<span class="created_at"><%= message.created_at.strftime("%H:%M") %></span>
<%= message.content %>
</li>
- 修改new.html.erb页面
<% title "Chat" %>
<ul id="chat">
<%= render @messages %>
</ul>
<%= render 'form' %>
- 修改controller
def new
@message = Message.new
@messages = Message.all
end
def create
@message = Message.create!(params[:message])
end
- 新建create.js.erb文件
$("#chat").append("<%= escape_javascript render(@message) %>");
$("#new_message")[0].reset();
- install faye
gem install faye
- vim faye.ru
require 'faye'
faye = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)
faye.listen(9292)
run faye
#注意,mount参数会决定文件的名称,比如 :mount => "/test", 那么引用js文件的时候,应该是 http://host.name:port/test.js
- 运行faye服务器
rackup faye.ru -s thin -E production
- 加入js代码
http://192.168.1.21:9292/faye.js
- 修改application.js文件
$(function(){
var client = new Faye.Client('http://192.168.1.21:9292/faye');
client.subscribe("/messages/new",function(data){
eval(data);
});
});
- 修改application_helper.rb
require "net/http"
module ApplicationHelper
def broadcast(channel,&block)
message = {:channel => channel, :data => capture(&block)}
uri = URI.parse("http://192.168.1.21:9292/faye")
Net::HTTP.post_form(uri, :message => message.to_json)
end
end
capture方法,是把一段block转换成一个变量
模拟发送消息的方法:
curl http://localhost:9292/faye -d 'message={"channel":"/messages/new","data":"hello"}'
- 修改create.js.erb文件
<% broadcast "/messages/new" do %>
$("#chat").append("<%= escape_javascript render(@message) %>");
<% end %>
$("#new_message")[0].reset();
- 加入token,增强安全
新建config/initializers/faye_token.rb
- 修改faye.ru服务器,增加验证
require 'faye'
require File.expand_path('../config/initializers/faye_token.rb', __FILE__)
class ServerAuth
def incoming(message, callback)
if message['channel'] !~ %r{^/meta/}
if message['ext']['auth_token'] != FAYE_TOKEN
message['error'] = 'Invalid authentication token'
end
end
callback.call(message)
end
end
faye = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)
faye.add_extension(ServerAuth.new)
faye.listen(9292)
run faye
- 修改applicaton_helper.rb的broadcast方法
def broadcast(channel,&block)
message = {:channel => channel, :data => capture(&block),:ext => {:auth_token => FAYE_TOKEN}}
uri = URI.parse("http://192.168.1.21:9292/faye")
Net::HTTP.post_form(uri, :message => message.to_json)
end
- 思路总结:
1.ruby代码向faye服务器发送一段js代码
2.faye在监听,扩送到其他的监听client
3.其他的client执行该段js代码
以前项目一直用juggernaut,但是juggernaut实在太笨重了,安装起来特别麻烦,而且对服务器端没有任何的控制。faye是一个非常不错的轻量级替代方案。
至于性能和稳定性,还需要额外的测试
- 这个项目可以扩展的
- 项目源代码地址
https://github.com/chucai/chatter
http://faye.jcoglan.com/