终于搞定了这个 blog!

周末的时候买下自己的域名 hozaka.com,再赖上 Weston 的空间,终于可以给自己的 blog 安个家。

WordPress 的安装出乎意料的简单,界面也很友好,先赞一个。默认的模板比较水,可以在 http://wordpress.org/extend/themes 找到很多精美的设计。只不过对我来说,这些模板的代码是在是很糟糕……

在小破 PowerBook G4 上配置好 apache+php,开始动手 redesign,无奈我是 PHP 菜鸟级人物,只能凭经验和语感去写代码,不过似乎运气不错,没怎么见到 php 的 500 页面,哈哈 :)

折腾了半天,终于把几个页面都做好了,马上上传,本来还想导入以前的博客来着,不过 blogbus 的 rss 系统不是怎么友好,还没办法得到所有的 feeds。

庆祝庆祝!谢谢 CCTV,谢谢 Channel[V],谢谢 Weston

ActionView 的魔术:ERB & Binding

Rails 作为一个 MVC 框架,其核心包括三个模块:ActiveRecord,ActionController 和 ActionView。今天这篇博文的主角是 ActionView,解开模板系统的魔术。通常情況下,通过 scaffold 已经能够建立简单的、包含CRUD基本功能的页面,完全不需要手动修改 view 的代码。即使不使用 scaffold ,Rails 也提供了众多的辅助方法,创造一个功能丰富的动态页面简直是易如反掌。但是,会用不代表深入理解,最近有朋友问我这些问题:

  1. 为什么编辑一个对象需要在 Controller 创造一个实例变量
  2. View 通过什么方式访问这些实例变量的
  3. 那么多表单辅助方法,都是需要提供 object_name, method 两个参数,怎么就变成实例变量的值了

相信大部分的 Rails 程序员手边的书都是《Agile Web Development with Rails》,书中提到这一点的时候一笔带过,只是说 Rails 在这里用了一个小魔术。这里,我们就来揭开这个魔术吧!

Part 1. Template Files – 模板文件

当一个 action 需要返回一段 html 片段的时候,我们需要建立一个模板文件。根据不同的版本、请求类型,模板文件的文件名也各不相同,从早期版本的 action_name.rhtml ,到现在的 action_name.text.html.erb ,以及扩展的 rjs,在扩展名中都包含了一个关键字:r / erb。它就是 Rails 模板系统的关键:ERb。

Part 2. ERb – Ruby Templating

ERb – 嵌入式 Ruby (http://ruby-doc.org/stdlib/libdoc/erb/rdoc/index.html ),是 Ruby 语言提供的一个基本扩展。它支持在字符串中嵌入 ruby 代码片段。看上去似乎很神秘,其实我们每天都用到,下面这种形式一定不陌生吧?

<h1><%= @user.name %></h1>

没错,正是因为 ERb 的存在,使得模板中可以动态地引用对象的属性。

Part 3. Instance Variables of Ruby – Ruby 的实例变量

让我们回顾一下 Ruby 语言的基本要素之一:实例变量。通常我们通过

@time = Time.now

的形式创造一个实例变量。这里我们不重新解释对于“实例变量”的定义,但是必须牢记一点,正如字面所见,实例变量的作用域是当前实例内,也就是说, 只有在实例的内部,才可以直接对实例变量进行读写操作(扩展的访问子方法等等不在讨论范围之内)。那么,为什么 Rails Controller 里创建的实例变量能够在 View 里面访问呢,不是自相矛盾吗?

Ruby 作为一种动态语言,因为其“开放”的特点,使很多原本不可能的编程模式变为可能。比如,通过 Open Class 的特性,你可以动态的为对象注入新的方法定义,或者改写方法的逻辑,或者,通过不同的方法可以在对象的外部访问对象的内部的实例变量。经常使用 console 的朋友可能会了解其中的一种方式:Object#instance_variable_get / Object#instance_variable_set 方法。举个简单的例子:

class User
  def initialize( name )
    @name = name
  end
end

user = User.new( "Jack" )
user.name #=> raise NoMethodError
user.instance_variable_get('@name') #=> "Jack"
user.instance_variable_set('@name', 'Tom')
user.instance_variable_get('@name') #=> "Tom"

可以看到,在没有任何访问子的情况下,我们用这种方式对一个实例变量进行读写操作。

除了这种简单的方式,还有另外一种进阶的方式,也是 ERb 常用的一种方式:Binding

Part 4. Binding

Binding (http://ruby-doc.org/core/classes/Binding.html ) 是 Ruby 语言的自身的一个特性,在任何对象内,self.binding 方法都会返回一个当前对象关联的 binding 实例。不精确的说,binding 对象可以理解成为当前对象的完整的上下文环境。文档中已经包含了一些示例代码,帮助大家理解 Binding 对象的作用。最重要的一点是:既然是当前对象的完整上下文环境,自然就包括了对象的实例变量。

那么 Binding 在 ERb 中扮演一个什么样的角色?是一个运行环境的提供者。

回到我们最初的问题,Rails 在 ActionView 中使用了什么样的魔法?答案就是 ERb 和 Binding。首先,获得当前实例的 binding,自然,binding 内也包括了实例变量;紧接着,ERb 允许将模板的内容动态地,绑定到另一个环境中运行。我们依然用刚才的 User 的例子来说明这一点:

# A simple template string
template = "Hello, "
@user = User.new( "Jack" )
# Get binding
binder = self.send( :binding ) # calling a private method# Rendering template
puts ERB.new( template ).result( binder )
#=> "Helo, Jack"

虽然例子不是非常恰当,但是足以展示 Binding 和 ERB 的用法。我们可以看到,ERB#result 方法将模板字符串绑定在另外一个环境中运行,而这个环境包含了我们创建的 @user 实例变量,因此,模板中的 @user.name 得到了正确的值。

这就是 ERB 的真面目,也是为什么在 ActionView 中能够访问到 Controller 里实例变量的原因。感叹一下动态语言的强大吧!以上只是非常粗糙的讲述ERB的使用,Rails 所做的魔法远不止如此,如果有兴趣,可以查看 Rails 源代码,对于深入学习 Rails 框架也有很大的好处。

最后,补充一下 ERB 的应用场景。虽然在普通的需求中,ActionView 所做的已经足够,但是某些情况还是需要创造独立的模板系统。比如某个场景,客户要求提供一个完全自定义的模板系统,这个时候 ERB 就大显身手了。只需要将数据源载入实例变量中,并且在使用手册里列出可以访问的方法,即使完全不懂 Ruby 语言也可以写出使用这套简单的模板系统了。

Think & Express / 思考与表达

炎炎夏日,在车站等车实在不是一件美差,百无聊赖,只能一边等车,一边观察身边的人群。

这次观察的对象是一个中年妇女,正在打电话。根据通话的内容来判断,应该是对徐汇区的道路不是很熟悉,打电话给他丈夫问路。尽管听别人打电话不是很文明的行为,但是既然当事人不避讳什么,在车站扯开嗓门,也不设计敏感话题,我也没什么必要觉得自己是个坏叔叔 :)

好了,闲扯这么多,还是来看看她通话的内容吧。(经过整理)

  1. “去xxxx到底怎么走啊?”
  2. “我就在上次我们去的那条路上”
  3. “旁边?车站啊”
  4. “732,931,205……”(在抬头看着站牌)
  5. “哦,对了,旁边还有个工商银行”

这个场景发生的地点是在我家楼下的公共汽车站。很显然这个妇女没有得到她想要的答案,而她丈夫也没有搞清楚她现在到底在哪里。听着这种对话我不仅哑然失笑。为什么她的思考与表达能力如此之差呢?相信全世界没有人能根据她的这些描述知道她到底在哪里。我们来一句一句的分析:

2. 上次去的那条路,抛开人的记忆力不谈,即使是按照同一条路线,如果你是在公共汽车站等车的话,怎么也不可能一条马路笔直走吧?那么上次那条路到底是哪条?要知道,条条大路通罗马 3. 回答你在车站旁边并不能给对方的判断带来任何帮助。不过如果你回答在马路上,可能会有冷笑话的效果 4. 如果你仔细得看一下你报的车,会发现他们在徐家汇这一块几乎每个站都是一起的,谁知道你在哪一站?但是同样的,你仔细看看站牌,应该能看到这一站叫什么名字 5. 上海的工商银行遍地开花,除非你在另一个空间

唠叨了这么多废话,还是没有提供对方一些有用的信息。最简单的,刚才提到的站牌,每个公交站都有名字,路线+站名在全市是唯一的;其次,在刚才提到的工商银 行对面是一所医院,医院的名字自然也是唯一的;再其次,头顶上有一块牌子,清清楚楚的写着这是xx路和xx路交叉口,这也是全市唯一的。

身边有着如此之多可以作为唯一地点标识的信息,却还是无头苍蝇一般逮到啥说啥。唏哩哗啦说了半天,倒不如花一分钟时间仔细观察一下再打电话,相信就能得到一点指点了。

有时候,难的是在遇到问题,还能保持一定清醒的头脑来分析问题,解决问题。人类的思考能力,是建立在理性的基础上的。

该死的小偷

今天一早上班,女朋友的手机就被偷掉了。放下自己的文明,不得不骂一句:该死的小偷。

其实上海的治安也不见得好到哪里去,每天成百上千的手机被偷,不知道多少夜归人在回家的路上被抢。还和谐社会,至少我作为一个平民百姓没有感觉,每天上下班依然是提心吊胆,左顾手机右顾钱包,我可以肯定最起码在上海,没有几个人可以说没被第三只手光顾过。这样的日子让人怎么过的安心?

话说回来,和小偷讲良心是跟自己过不去。但是,难道小偷们就没有自己的父母没有自己的孩子?看到自己的家人在社会中被瞧不起,对得起他们么?

又碰到这种事情,心里是在觉得很无奈,很纠结

Gettext 的内存泄漏 / Memory Leaks in Gettext

Rails 支持 L10N 的插件并不算非常多,而其中的 Gettext 由于 GNU 的缘故应该算是比较普遍的了。相信很多人在对 Rails 项目进行本地化的时候都会接触过这个插件。

但是不知道有多少人在部署 Rails 项目的时候留意过 mongrel 的内存使用情况(这里以 mongrel 举例仅仅是因为比较常见,其他部署方式也会有类似的情况)。至少在我的项目中,mongrel 占用的内存会不停的增长,不得不把所有 mongrel 加入 monit 并把内存占用作为监视条件。

空闲的时候稍微分析过它的源代码。导致内存泄漏的原因其实很简单,插件内部使用一个 Class 的实例对象作为 Hash 的 Key,这部分的资源没有回收导致每次调用方法都会产生泄漏(bound_target)。虽然可以可以用 MonkeyPatching 的方式解决这个问题,但是我始终认为这种方式会带来不可预测的风险。

昨天想起以前做过的 GettextDb 的项目,于是检查了一下 Gettext 的版本情况,发现最近升级的 Gettext 1.91.0 修复了这个漏洞,改用实例对象的 object_id 作为 hash 的 key,避免了过多无法回收的对象产生的泄漏。

相关链接:http://gettext.rubyforge.org

# how to install gettext gem
gem install gettext

Uploading with Safari

前段时间在项目中加入一个新的 Upload Attachment ,很顺利的部署到了 LIVE。但是在使用过程中,很意外的收到了 Exception Notification Mails ,总结下来都具有以下特点

  1. 访问 Upload Attachment
  2. 浏览器都是 Safari
  3. Raw Post 数据段有空白
  4. 报告 undefined method `read' for "":String

根据这些线索,在 dev 环境下模拟了各种可能的情况,终于重现了这个 BUG:不选择任何文件。这个结果很让人感到意外,Rails 在处理一个请求的时候会自动的将 multipart 段进行封装,返回一个 Template File 对象。但是在处理来自 Safari 的请求,却返回一个空白字符串。

之后查过很多 tickets ,不少国外用户都遇到过这个问题(国内 mac 用户比较少?),而 Rails 团队一直都没有做出过修改,所以自己写了一个 patch 放在项目里。今天工作的时候无意中又看到了这个 patch,心血来潮检查了一下 dev.rubyonrails.org 上是否有解决方案了,得到一个 changeset: http://dev.rubyonrails.org/changeset/7759

没有去查证这个 bug 发现以后时隔多久才被修正,记上一笔,也作为 Rails 历史的一部分 :)