===广告部分,想看技术部分的可以直接跳过
===
GuruDigger是一个面向web开发者的社区,能够从用户认证通过的Email 出发,自动爬遍互联网,根据用户在互联网上的活动进行分析,对掌握的每项编程语言技能进行评分和排名:
还有头脑风暴板块和Guru燃料功能能够帮助用户实现他的各种好玩,甚至是稀奇古怪的Idea:
===广告结束,以下是迁移经验分享部分===
GuruDigger之前是用C#.net开发的,最近迁移到了rails 3(3.0.9),迁移的主要原因:rails生产力高,便于后续维护。
因为是业余时间做的,前后差不多用了2个月的时间,统计了一下,我累计用了120多个小时
另外一个开发者
linjun,因为是第一次接触ruby的关系,包括学习和开发,累计用了200多个小时。
对于后台开发,rails的效率没的说,但是前端还不给力,虽然和2.0 相比, 3.0的jquery + ujs好用了很多,可是很多时间还是花费在了和js/html/css上,这和我们2个开发人员都不擅长前端也有关系,目前用的css都是照扒旧版本的,后来还有专门的前端工程师帮我们改了很多。
1. 用户认证
Rails项目用户认证当然首选
devise,短短的几行配置和代码,就可以搞定一切关于注册,登录,忘记密码等一系列用户认证相关功能,再配合
omniauth,还可以实现Google/Twitter/QQ/新浪微薄等第3方帐号登录。
但是GuruDigger旧版本记录的用户密码是MD5加密,不够安全,所以在迁移的时候对devise做了一个扩展,将旧系统的MD5密码导入数据库(legacy_password_hash),用户在新系统第一次登陆的时候先用旧密码判断一下,进行密码迁移:
module Devise
module Models
module DatabaseAuthenticatable
def valid_password_with_legacy?(password)
if self.legacy_password_hash.present?
if ::Digest::MD5.hexdigest(password).upcase == self.legacy_password_hash
self.password = password
self.legacy_password_hash = nil
self.save!
return true
else
return false
end
else
return valid_password_without_legacy?(password)
end
end
alias_method_chain :valid_password?, :legacy
end
module Recoverable
def reset_password_with_legacy!(new_password, new_password_confirmation)
self.legacy_password_hash = nil
reset_password_without_legacy!(new_password, new_password_confirmation)
end
alias_method_chain :reset_password!, :legacy
end
end
end
2. 权限
使用的是cancan,它能够让你在一个集中的地方定义权限(Ability 类),而不需要分散在controller,views或者数据库查询中添加各种重副代码,比如我们定义了用户只能编辑、删除自己的idea或者project,只用在Ability里面定义如下:
can :manage, [Idea, Project, UserEmail], :user_id => user.id
在Idea Controller里面,就只用cancan提供的helper method load_and_authorize_resource加载当前用户有权限的idea,非常方便:
class IdeasController < ApplicationController
load_and_authorize_resource
def update
@idea.update_attributes!(params[:idea])
end
end
3. 文本编辑
因为是面向web开发者的社区,所以文本编辑器方面用了和Github一样的markdown语法,在前端编辑器用的是
markitup,在后端markdown render html的功能上,使用了
redcarpet,因为他采用了Ruby wrap C代码,所以性能很不错。但是markdown本身不支持嵌入flash等功能,有时候用户还是需要贴个视频的,所以做了一个扩展支持flash,利用markdown的img标签,先生成一个flash为前缀的地址:
{name:'Flash', key:'F', placeHolder:'![600x450](flash:http://www.youtube.com/v/9I9SkGwJNOA)'}
然后后端用正则表达式转换一下:
class MarkdownExt < Redcarpet
def to_html
super.gsub(/<img src="flash:(.*?)" alt="(\d+).(\d+)">/) do |match|
flash_tag $1, $2, $3
end
end
protected
def flash_tag(url, width, height)
#...
end
end
4. 任务队列
GuruDigger有很多功能需要用到异步的任务队列,比如调用爬虫分析数据,同步到Twitter,发送电子邮件等,我们采用了
resque,它基于Redis,提供了非常方便的任务队列框架,另外还有完善的维护功能,以同步到Twitter为例子,在需要同步的时候,只需要调用Resque的入队列方法(enqueue),而另外写一个perform方法来处理队列内的数据:
module TwitterUpdater
def self.included(model)
model.class_eval {
attr_accessor :update_twitter
def update_to_twitter(message)
if self.update_twitter.to_i == 1 && user.twitter_access_token.present?
Resque.enqueue(TwitterUpdater, user.id, message)
end
end
}
end
@queue = :twitter_updater
def self.perform(user_id, message)
p "TwitterUpdater processing #{user_id}, #{message}"
user = User.find(user_id)
token, secret = user.twitter_access_token.split(":")
client = TwitterOAuth::Client.new(
:consumer_key => TwitterConfig['consumer_key'], :consumer_secret => TwitterConfig['consumer_secret'],
:token => token, :secret => secret
)
if client.authorized?
client.update(message + " via @GuruDigger")
else
user.update_attributes(:twitter_access_token => nil)
end
end
end
5. 全文搜索
使用了
sunspot插件,他后台服务器是solr(基于Java的Lucene),solr对于小规模应用来说安装和配置相当简单,并且有很好的扩展性,对于大数据量的全文搜索,可以使用它的cluster功能,默认支持的中文是单字分词,我们可以使用mmseg分词算法:
mmseg4j。
Sunspot是通过ActiveRecord的create/update/destroy callback来更新/删除索引,只需要在model里面定义需要索引的属性就可以了:
searchable do
text :name
text :introduction
text :address
end
如果想让索引的工作变成异步,还可以使用
sunspot_index_queue这个addon,也可以使用前面提到的Resque来自己扩展一下。
6. 其他用到的杂项gem
A. 客户端校验
使用了
client_side_validations插件,好处是只需要在model上定义validate rule,就能够被客户端的js重用,不过和纯客户校验的jquery validate相比,不够灵活。
B. Google Map插件
gmaps4rails
C. 用户头像gravatar显示插件gravatar-ultimate
D. Tag插件acts-as-taggable-on
E. 评论插件acts_as_commentable_with_threading
F. 支付插件activemerchant
===总结===
迁移框架是个体力活,要保证数据正确,功能一致,同时还要加上一些新功能,不过RoR的框架越来越成熟,各种gem,plugin经过这几年的发展,组合和扩展非常方便,完成一个项目很像搭乐高玩具的感觉。同时期待Rails 3.1在前端的改进(coffeescript sass)能够提高前端的开发效率。