Archive for the ‘ Ruby on Rails ’ Category

发现“里克”

睡觉之前,习惯性的打开 google reader 看看国外有没有最新的 feed。空白的列表,唯有推荐中小字一行:里克的自习室,关注Ruby和Rails学习与开发

发现里克。

发现了一位创业者,一个普通的程序员,一个与生活做斗争的坚强的父亲。我无法描绘出心里的感受,所能做的,只是默默的记下这些,在心底祝福。

里克,一切都会好的!

生活并不平台,里克在努力

生活并不平坦,里克在努力

扩展 Paperclip

Paperclip 是 Rails 的一个处理 attachment 的插件,相对于以往的 FileColumn 在灵活性和效率上更胜一筹,而且代码也比较好看。这个视频 简单的介绍了 Paperclip 的使用方法。

默认的设置,URL 的占位符中与模型本身相关的只有 id,但是一些情况下,你可能会更希望以其他形式来组织你的附件目录 – 比如以 SKU 来代替数据库记录的 id。这里我们暂不讨论这种做法的好坏,双方面的,好处是目录结构更具有维护性,坏处是万一以后升级数据库,SKU 加个前缀什么的……

Here we go!

使用 paperclip 需要在 model 中调用 has_attached_file 方法,检查文档,有一些简单的使用样例,但是没有我们需要的。通过方法描述我们知道这个方法建立了一个 Paperclip::Attachment 对象,我们继续看文档。对象的方法很少,首先思考:应为我们需要配置的是 attachment 的 url 规则,那么应当是对应整个类而不是单个实力,因此我们只关注 Peperclip::Attachment 的类方法,只有两个。default_options 没有描述,而且展开代码发现并不是我们需要的。

# Paperclip::Attchment.interpolation

A hash of procs that are run during the interpolation of a path or url. A variable of the format :name will be replaced with the return value of the proc named “:name”. Each lambda takes the attachment and the current style as arguments. This hash can be added to with your own proc if necessary.

这正是我们需要的,接下来的扩展就非常方便了:


# app/models/product.rb
class Product < ActiveRecord::Base
  has_attached_file :photo,
    :style => { :thumb => '64x64>' },
    :url => '/images/products/:to_param.:extension'

  def to_param
    return self.sku
  end
end

# config/initializers/paperclip.rb
Paperclip::Attachment.interpolations.merge!(
  :to_param => lambda { |attachment, style| attachment.instance.to_param }
)

在这里不直接使用 :sku 作为占位符而使用 :to_param 是为了在其他模型中更加的灵活。

Rails 之一月动态

一个多月之前,Rails 2.2.2 发布了,同时,官方紧接着发表声明,称 Rails 2.3 正在开发中。看到这个消息,一边感叹于 Rails Core Team 进度之紧凑,同时开始做一件很有意思的事情:在接下来的事件里,每天都检查一次 http://github.com/rails/rails/tree/master 上的所有 commit。除了常规的性能优化调整、更独立的代码结构之外,的一系列提交揭示了 Rails 的下一步:

  • Joshua Peek 初次提到 middleware (commit),接下来似乎是他一直负责这部分的开发
  • 官方博客的每周更新,Rack integration 开始与 Rack 整合的步伐,middleware 开始频繁出现在以后的代码中
  • 紧接着的 This Week in Edge Rails,开始移除与 CGI 相关的代码
  • 官方博客介绍 Rails Metal
  • Session Storage 也迁移至 Rack (commit, commit)
  • 随后的几个大 commits 将 Rails 从 CGI 解放
  • Merb 将与 Rails 3 合并

可以看出,高扩展性会是 Rails 2.3 的卖点之一,同时,开发团队似乎有想法把 Rails 设计的更加“热插拔”,通过引入 middleware,在将来会有更多的选择;而与 Merb 的整合,则可以借鉴其中优秀的设计思想,在更高的版本中加以实现和整合。

于此同时,Ruby 1.9.1 也在紧密开发之中,不过道路并不平坦,RC1 的发布延迟到 09 年的 1 月份。正如 Robbin 所说,09 年的 Rails 很灿烂

Merb 将合并至 Rails 3!

原文地址:Merb gets merged into Rails 3!

一直一来,关于 Merb 与 Rails 之间的优劣与取舍之间的争论,伴随着两个框架的不断发展而没有停息。终于,在圣诞前夕,Merb 终于要于 Rails 3 合并了!

原文同时指出,这次的合并不是大规模的 Rails 代码重写(This is not a big bang rewrite),相对的,作为第一个合并的版本,两个团队更倾向于让两者并存,用户可以根据实际需求选择适合的组件(比如 ActiveRecord / Sequel / DM 作为 ORM)。无论如何,我个人都觉得这是一个非常好的开端,Rails 有了 Rails 以外的思想,将来必定会更加精彩!

最后,在 timeline 安排上,目前开发团队的主要精力还是在 Rails 2.3(我自己连续 3 个星期在关注于 Rails 2.3 Edge 的 commits,准备了另一篇博文),2.3 将会在 09 年的 1 月份放出,then all eyes will be on Rails 3!

Rails 2.2 正式发布

Rails 2.2 从 RC1 到 RC2,终于发布了正式版(版本号 2.2.2),需要 gem 1.3.1。带来的新特性包括 I18N / Thread Safety / Connection Pool / Ruby 1.9 Compatibility 等等。

自己从 edge 一路紧跟 rails 步伐研究过来,随后会发表一些个人的实践和看法。

前段时间的热点:DHH 回应 Mongrel 作者的质疑,撰写系列文章:The Rails Myths 。原本打算一同翻译这6篇博文以及 rails 2.2 的新特性,因为种种原因不了了之……

Rails 2.2 官方博客:http://weblog.rubyonrails.org/2008/11/21/rails-2-2-i18n-http-validators-thread-safety-jruby-1-9-compatibility-docs

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 语言也可以写出使用这套简单的模板系统了。

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 历史的一部分 :)