CityHelper是一个提供国家,省,市选择框的小插件,这次更新增加了国家和国际化支持,目前只支持简体中文和英语。

安装

$ script/plugin install git://github.com/yzhang/city_helper.git

这会在你的config/locales目录下安装一个cities目录,这个目录包含了i18n的信息,还会在config/initializers目录增加一个city_helper.rb来设置i18n的加载目录。

使用


country_select(:user, :country, {
       :only => [:china], # 指定要显示的国家
       :update => 'state_select_id', # 指定国家改变后要更新的省份选择框的id
    }, html_options)
state_select(:user, :state, {
       :country => :china, # 指定默认显示那个国家的省份
       :countries => [:china], # 只有在country_select中设置了update属性时才需设置这个属性
             # 这个属性应该与country_select中的:only属性保持一致
       :update => 'city_select_id', # 指定省份变动时要更新的城市选择框ID
     }, html_options)
city_select(:user, :city, {
       :country => :china, # 设置默认的城市列表所在国家
       :state => :beijing, # 设置默认的城市列表所在省份
     }, html_options)

下面是一个国家,省,市联动的使用范例:


country_select(:user, :country,
       :only => [:china, :japan, :united_states],
       :update => 'state')
state_select(:user, :state,
       :country => :china,
       :countries => [:china, :japan, :united_states],
       :update => 'city')
city_select_tag(:user, :city, :country => :china, :state => :beijing)

你也可以通过Formbuilder或者xxx_tag使用这些helper:

form.country_select :country
form.state_select :state
form.city_select :city

country_select_tag :country
state_select_tag :state
city_select_tag :city

如果需要增加更多城市,可以通过修改插件中的city_helper中的常量来实现。

最后,最近Blog迁移,不小心把另一个Blog的内容混进了RSS,给大家造成困扰,实在抱歉。

Rseg是一个基于纯Ruby的中文分词插件,内置了CC-CEDICT和维基百科两本字典,目前只支持Ruby 187以上版本及UTF8编码。

项目地址:http://github.com/yzhang/rseg
分词算法:http://xiecc.blog.163.com/blog/static/14032200671110224190/

安装

sudo gem install rseg –source=http://gemcutter.org

使用

Rseg目前支持两种用法,本地调用或者C/S模式调用。

1. 本地调用


> require 'rubygems'
> require 'rseg'
> Rseg.segment("需要分词的文章")
['需要', '分词', '的', '文章']

第一次执行需要加载字典,大概需要30秒,不过你也可以调用Rseg#load来手动加载字典。

2. C/S方式

$ rseg_server

这条命令会监听4100端口,你可以通过http://localhost:4100/ 访问Web界面,也可以通过rseg命令远程调用:

$ rseg ‘需要分词的文章’
需要 分词 的 文章

还可以调用Rseg#remote_segment


> require 'rubygems'
> require 'rseg'
> RSeg.remote_segment("需要分词的文章")
['需要', '分词', '的', '文章']

这次就不需要加载字典了。

性能

很多人比较关心性能,用我的小白测了一下,大概在5M/s,欢迎大家多提意见。

4th Nov, 2009

快钱Rails插件

演示站点:http://kqdemo.zaituu.com
演示代码:http://github.com/yzhang/kuaiqian_demo
插件地址:http://github.com/yzhang/kuaiqian

安装

$ script/plugin install git://github.com/yzhang/kuaiqian.git

使用

1. 安装完成后,插件会自动在你的应用的config目录创建一个kuaiqian.yml文件,默认是快钱提供的测试帐号(关于测试帐号的使用方法请参看测试章节)。将kuaiqian.yml中的商户ID和密钥替换为快钱提供给你的真实ID和密钥然后进行下一步。

2. 下面的代码会创建一笔新订单:


@request = Kuaiqian::Request.new('产品名称', # 产品名称
            1, # 订单ID,必须全局唯一
            Time.now.strftime("%Y%m%d%H%M%S"), # 订单生成时间,格式为20091104174132
            4500, # 订单金额,以分为单位
            'http://return', # 通知地址,用户支付成功后快钱会通过此地址通知商户支付结果
            '00', # 支付类型,00显示所有方式,10只显示银行卡方式,11只显示电话银行方式,12只显示快钱帐户支付方式,13只显示线下方式
            'attach') #自定义数据,会在返回URL中原样返回
redirect_to @request.url

上面的代码会将用户重定向到快钱的支付页面。

3. 在用户完成支付后,快钱会调用你在支付请求中提供的返回URL:


@response = Kuaiqian::Response.new(params)
if @response.successful?
  # 支付成功
else
  # 支付失败
end

注意,快钱可能会多次调用你的返回URL,并将结果展现给用户,因此你的代码要考虑多次执行后的输出对用户的有好度。

同时Response还提供了以下方法供用户检查响应的有效性:


pay_amount # 用户实际支付金额
bank_name # 银行名称
bank_deal_id # 银行交易ID
deal_id # 订单在快钱系统中的ID
order_id # 订单ID
order_time # 订单时间
order_amount # 订单金额
fee # 快钱收取的手续费,以分为单位
deal_time # 快钱对交易进行处理的时间
ext1 # 用户自带的数据

测试

要使用快钱提供的测试商户ID和密钥,需要修改你的hosts文件,将快钱的域名指向测试服务器:

218.242.247.5 www.99bill.com

然后使用下面的帐号进行支付:

用户名:kquser02@sina.com
密码:99bill

最后,必须也抱怨一下快钱,开发者社区没有忘记密码功能,提供的测试工具不能用,FAQ没有排版,所有问答都在一行,并且测试环境的信息不在开发文档里,而是在FAQ的最后一个问题里,实在是愁人的很。

Rails的save和update方法默认都会将updated_at更新为当前时间,并且不允许手动设置,但是有些情况下,我们可能希望只更新记录的某个字段,而不希望updated_at被更新,比如显示帖子的时候增加帖子的查看次数,这可以通过设置model的record_timestamps属性实现:

Post.record_timestamps=false

但是因为record_timestamps是一个类变量,因此改变这个值会影响到所有Post的保存,如果在同一时间有用户确实修改了Post的内容或者标题,updated_at也将不会更新。

Neeraj在这篇帖子里提供了一个方法,只修改当前对象的record_timestamps属性,在保存完后再进行恢复:

module ActiveRecord
  class Base
    def save_without_timestamping
      class << self
        def record_timestamps; false; end
      end
      save
      class << self
        def record_timestamps; super ; end
      end
    end
  end
end

这段代码将打开当前对象的metaclass,并为当前对象定义一个record_timestamps方法来覆盖全局的设置,你可以将这段代码放到initializer中,然后在不需要更新时间戳的时候调用save_without_timestamping来更新记录,就像下面这样:


post = Post.find(:id)
post.view_count += 1
post.save_without_timestamping

上面一个实现是通过super方法恢复record_timestamps设置的,Neeraj还提供了一个使用remove_method的版本,我个人更喜欢这个版本:


module ActiveRecord
  class Base
    def save_without_timestamping
      class << self
        def record_timestamps; false; end
      end
      save
      class << self
        remove_method :record_timestamps
      end
    end
  end
end

21st Sep, 2009

财付通Ruby插件更新

财付通插件做了更新,增加了查询接口,可以通过此接口查询订单状态:


@query = Tenpay::Query.new(1, # 订单ID
     Date.today) # 订单生成日期
@query.response.successful? # true => 已支付, false => 未支付

不过必须得发一下牢骚,财付通实在是太难用了,虽然提供了返回URL,但是有用户反映支付完成后窗口直接关闭,导致订单没有更新,还有前段时间收了笔款,明明是用QQ号码的帐户发的收款申请,但是款居然到了我的GMail信箱帐户,而且我没有用信箱申请过财付通帐号,只是把QQ帐号和EMail做了绑定。还有选择用财付通余额支付的时候,它会记住QQ号,导致无法用其它帐户的余额付款,还有他们的客服电话基本上要等5-6分钟才有人接,还有就是不支持招行信用卡。。。。

罪状简直数不胜数,总之一句话:财付通有风险,集成需谨慎!

同一套代码,部署在同一台服务器上,但是很诡异的是一套工作,另外一套登录的时候总是出现 ActionController::InvalidAuthenticityToken 的异常,即使加上skip_before_filter :verify_authenticity_token,虽然没有异常了,但是仍然无法登陆,追踪进去发现是Rails的Session保存失败,Google了一下“IE rails session can’t be save”,找到这篇帖子,似乎只是IE7独有的一个Bug,当你的子域名包含下划线时,IE7会拒绝接受Session Cookie,从而导致Rails的Session保存失败,而我的两个应用唯一的区别恰恰就在一个应用的域名包含有下划线。

这个问题困扰了我一下午,希望这个帖子可以给遇到同样问题的人节省一点时间。

19th Aug, 2009

财付通Rails插件

最近需要给(ZT)增加在线支付功能,比较了国内的各种支付方案,最后选择了财付通,申请比较简单而且没有年费,但是腾讯官方只提供了PHP,JSP和ASP的代码,只能自己写了一个Rails的

安装


$ script/plugin install git://github.com/yzhang/tenpay.git

使用

1. 插件安装后,会在你的config目录下创建一个tenpay.yml文件(默认是腾讯提供的测试帐户,往这个帐户付款它居然真的就收了,虽然只是1分钱),你需要修改这个文件,填入你从腾讯申请的商户号(spid)和密钥(key)。

2. 下面的代码演示如何创建一个新交易:


@request = Tenpay::Request.new('测试产品', # 产品名称
      1, # 订单编号,需要保证全局唯一
      4500, # 产品价格,以分为单位
      'http://return', # 用户完成支付后返回的URL, 支持Get/Post
      request.remote_ip, #用户IP,用户production模式下检验用户,development模式忽略。
      'attach data') # 商户数据,会原样传递给返回URL,可不填
redirect_to @request.url

用户访问这个action的时候会被引导到支付页面。

3. 用户完成支付后,需要返回商户页面,也就是你在请求中填写的返回URL,下面的代码用于验证请求是否有效:


@response = Tenpay::Response.new(params)
if @response.successful?
  # 支付成功
else
  # 不合法的请求或支付失败
end

目前还不支持查询接口,欢迎大家的意见和建议。

相信很多人都曾经被面试官问过这样的问题:”你对自己未来5年的职业规划是怎样的?“,每当我被问起这个问题时,我的脑海中总是会浮现出《Twisted Sister》(一部拍摄与1984年的电影)中的这个场景,一名老师对着他的学生大喊。

我想要你告诉我,不,是告诉全班的学生

twisted-sister-i-wanna-rock-video-still-frame.jpg

 

你究竟想要怎样的生活?

一般情况下,你的很自然的想法就是:你要变得很牛,或者至少变成一个很牛的程序员。尽管这个问题看起来并不像其它一些同样老掉牙的问题(比如,”你觉得你最大的弱点是什么?“)那么严肃,但很可能你还是会觉得难于回答,回避这个问题明显不是一个明智的选择。

但是,在我看来,这个问题同样是一个相当严肃的问题,只不过大部分人都没有注意到他的重要性,不是对面试官,而是对你自己。

对于这个问题,大部分人都会选择一个不痛不痒的答案来敷衍面试官,但如果你深入的思考这个问题,你会发现这实际上是一个关于软件开发人员可能的职业 生涯轨迹的问题,当然,我们选择这一行是因为我们喜欢这行,并且幸运的得到老天的眷顾,我们干上了这行,但你能在50岁的时候依然坐在电脑前面敲代码 嘛?60岁呢?所以,我们必须在还年轻的时候先思考一下这个问题:作为一个程序员,最完美的职业生涯应该是什么样?

如果你懒得自己思考,那就让我来告诉你吧,基本上,这个世界的程序员可以分为8种类型:

1. 不朽的程序员

这是最高的级别,虽然你的人已经挂了,但是你的代码却依然活着,你成为计算机博物馆中的一个永久收藏,为数众多的程序员持续的学习和阅读你的作品。 你在生前已经赢得了许多的赞誉,发表了大量非常重要的论文或者是创造了许多影响到计算机专业课程设置的基础技术。你已经不仅仅只是拥有一个维基百科的条 目,会有许多专门的网站讲述你的作品以及你生平的事迹。

能够达到这一级别的程序员屈指可数,这一级别的代表人物是:Dijkstra, Knuth, Kay

2. 成功的程序员

成功的程序员不仅拥有广泛的知名度,并且还运营着一个不错的公司——甚至是整个产业链。他们拥有绝对的自由可以做他们想做的事情。

这一级别的程序员是最多人羡慕的,到达这一级别更多的是需要商业上的才能而不是你的编程能力。

代表人物:Gates, Carmack, DHH

3. 知名程序员

成为这个级别的程序员也不错,当然前提是,你得有一份不错的工作。

这个级别的程序员虽然在圈子里很有名,但是成为名人并不意味着你可以靠这个获取收入甚至是养活你自己。知名固然不错,但无疑成为成功的程序员更加的 好。你的公司可能是一家非常知名的大技术公司,也可能是一家很有影响力的小公司或者是一个最新潮的创业团队,其它的程序员都应该或多或少的听说过你,并且 你对你所从事的领域可以起到积极的影响。

4. 工作杰出的程序员

你做为一个软件工程师的职业生涯非常的成功,因为你的工作能力相当杰出,你从来不会为找不到一份你满意的好工作好发愁,你的同事也非常尊敬你,每一家你工作过的公司都因为你的加盟而在某些方面得到了增强。

但问题是:你接下来的方向在哪呢?

5. 能力一般的程序员

这个级别的程序员,一般来说,由于天赋的差异,通常很难成为杰出的程序员,但天赋跟成功的关系其实不是很大,如果你有很好的商业或者是与人沟通的技 能,你依然可以直接跳升到成功的程序员级别。如果你是一个能力一般的程序员,但你却靠这行当过上了不错的生活,那说明你必须在其它方面有很好的天赋(除了 编码)。

不要怀疑自知的价值,通常你的能力都会比你认为的要少,但缺乏天赋并不是什么大不了的事情。要勇敢一点,发掘自己的特长,并充分利用,你也会成功的!

6. 业余的程序员

业余程序员通常都很喜欢编码,一般来说以学生和实习生为主,他们通常会向开源项目共享代码,或者利用空闲时间凭自己的兴趣开发一些应用,他们的代码和创意通常看起来都很有激情。

成为一个业余程序员是个好事情:这个级别的程序员可以很快速的成为一名工作杰出的程序员。

7. 未知的程序员

还有一些很知名的程序员,比如Joe Coder,有能力但是不太引人注目,很可能在为某个大公司工作。对于这类人来说,写代码仅仅只是工作,并非他们生活的全部,这没什么错,挺好的。

8. 差劲的程序员

这个级别的程序员通常是因为某些意外阴差阳错的成为了程序员,但他们本身并不具备写代码的技能,所有他们做的东西都成了他们同事的噩梦——当然有一种例外,就是他的同事也是十分差劲的程序员,差到都不具备与他的同事进行基本的沟通。

基本上,如果要给差的程序员下一个定义,那就是:没有金刚钻,却揽了瓷器活。

当然,这个级别排的不是很严肃,并且每个人对自己的职业规划都不一样。它只是想给你一点启示,那就是你在未来的10年,20年,30年甚至是你整个一生究竟能够完成哪些事情,你最为崇拜那个程序员,他身上的哪些特点最为吸引你?

简而言之,你究竟想要过怎样的生活?

此插件已更新,请移步这里: http://www.letrails.cn/archives/city-helper-update-i18n-and-country-support/

city_helper提供了几个简单的helper来实现省及城市的二级联动select,目前只支持国内城市,经过测试的浏览器:FF3, Chrome 2 beta, IE7,
Safari 4。

安装


$ script/plugin install git://github.com/yzhang/city_helper.git

使用
安装之后,你就可以在View中直接使用以下Helper了:


state_select :user, :state # 选择省份
capital_select :user, :city # 选择省会城市
city_select :user, :city # 选择所有地级及地级以上城市
state_and_city_select :user, :state, :city # 省市二级联动select

当然,你也可以在FormBuilder中使用,同时还有一个不需要object的_tag版本:


form.city_select :city
city_select_tag :city

你也可以传递options给city_helper,参数定义同select:


city_select :user, :city, options, html_options
state_and_city_select :user, :state, :city, {:city => city_options, :state => state_options}, {:city => city_html_options, :state => state_html_options}

state_and_city_select稍微复杂一点,下面给个例子:


state_and_city_select :user, :state, :city, {:state => {:include_blank => '请选择省'}, :city => {:include_blank => '请选择城市'}}

TODO

  1. 支持其它国家城市及国际化
  2. 和country_select集成,实现三级联动

最近在做的一个项目遇到严重的性能问题,主要是数据库查询过多,同事向我推荐了rails_reviewer插件,它是我的前同事(不确定我进公司时他还在不在) David Stevenson 写的一个帮助你优化SQL查询的插件,它可以帮助你定位数据库查询的瓶颈所在,从而通过编制索引已经缓存关键变量来改进性能。

rails_reviewer的安装很简单:

$ script/plugin install git://github.com/dsboulder/query_reviewer.git
$ rake query_reviewer:setup

现在重启服务器,你就会在屏幕的左上角看到一个小窗口,点击这个窗口,就会显示加载当前页面的所有SQL查询,并且会告诉你那些是没问题的,那些是需要优化的,如下图:

picture-1.png

可以看到,在优化前这个请求执行了1101条SQL查询,并且大部分都是没有cache的,你可以通过rails_reviewer查看每条查询的SQL语 句以及代码的调用栈,优化主要从两方面进行,一个是提高查询性能,这个可以通过添加索引,另一个就是减少查询数量,这个可以通过缓存变量,比如:


def production(name)
  @production_cache ||= {}
  @production_cache[name] ||= buildings.map{|b| b.production(name)}.sum
end

下面是优化后的结果,可以看到,优化后查询数从1101变成了94,时间则从800多毫秒减到了59毫秒,大约只有原来的10分之一不到:

picture-2.png