定义世界

摆动的时钟挥动死神之镰

Rails: with_options (Object)

with_options是ActiveSupport为Object类型扩展的一个方法,其作用是为代码块中所有的调用添加一些公共的参数。一个来自Beast的例子如下:

  with_options :if => :password_required? do |u|
u.validates_presence_of :password_hash
u.validates_length_of :password, :minimum => 5, :allow_nil => true u.validates_confirmation_of :password, :on => :create u.validates_confirmation_of :password, :on => :update, :allow_nil => true end

其作用是为代码段中的每一个调用都添加一个“:if => :password_required?”参数。

with_options的源代码如下:

    # File vendor/rails/activesupport/lib/active_support/core_ext/object/misc.rb, line 47
47:   def with_options(options)
48: yield ActiveSupport::OptionMerger.new(self, options)
49: end

也就是说,他会返回一个OptionMerger对象,将当前的对象包装起来,并传递给你的代码段。

OptionMerger代码如下:

# File G:\ruby\lib\ruby\gems\1.8\gems\activesupport-1.4.4\lib\active_support\option_merger.rb
module ActiveSupport
class OptionMerger #:nodoc: instance_methods.each do |method|
undef_method(method) if method !~ /^(__|instance_eval|class)/
end def initialize(context, options)
@context, @options = context, options
end private def method_missing(method, *arguments, &block)
merge_argument_options! arguments
@context.send(method, *arguments, &block)
end def merge_argument_options!(arguments)
arguments << if arguments.last.respond_to? :to_hash
@options.merge(arguments.pop)
else @options.dup end
end end end

OptionMerger的主要目的是包装当前对象。网上一篇文档说OptionMerger使用了Decorator模式,无奈没仔细研究过设计模式……。可以看到在代码最初的部分首先删除了对象的所有instance_method,后面可以看到对所有的方法的执行都是重新定向到@context对象的,也就是上面传递过来的self。难道这就是Decorator模式?

在构造函数里面首先得到被包装的对象@context以及你提供的需要添加到所有调用中的参数。因为最终返回的是一个OptionMerger对象,又由于所有的instance_method都被删除了,因此任何一个调用都会引发异常。在下面的代码可以看到,通过实现method_missing函数,ruby直接处理了异常,并把对参数的调用传递给method_missing函数,包括名称,参数以及代码段。说到这里基本就没有难度了,method_missing把你提供的公共参数加入到参数列表中,然后调用context的相应的方法,实现包装。

哎……不看代码好多年……

posted on 2008-02-23 20:58 光荣之翼 阅读(76) 评论(0)  编辑  收藏 所属分类: Rails, Rails, Rails


标题  
姓名  
主页
验证码 *  
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-04-14 23:35 编辑过