2011-11-07 62 views
7

当我有一个ActiveRecord扩展名(略):重写setter方法包括InstanceMethods模块

module HasPublishDates 
    def self.included(base) 
    base.send :extend, ClassMethods 
    end 

    module ClassMethods 
    def has_publish_dates(*args) 
     attr_accessor :never_expire 

     include InstanceMethods 
    end 
    end 

    module InstanceMethods 
    def never_expire=(value) 
     @never_expire = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 

    def another_instance_method 
     'something to return' 
    end 
    end 
end 

ActiveSupport.on_load(:active_record) do 
    include HasPublishDates 
end 

可以称为像这样:

class MyModel < ActiveRecord::Base 
    has_publish_dates 
    ... 
end 

的想法是,never_expire=应重写setter由attr_accessor :never_expire定义。然而,这似乎并不奏效:

m = MyModel.new 
m.never_expire   #=> nil 
m.never_expire = '1'  #=> '1' 
m.never_expire   #=> '1' should be true if never_expire= has been overridden 
m.another_instance_method #=> 'something to return' works as expected 

正如你所看到的,another_instance_method被包括在内,按预期工作,但never_expire=未覆盖二传手如我所料。

如果我改变HasPublishDates使用class_eval然后它按预期工作:

module HasPublishDates 
    ... 
    module ClassMethods 
    def has_publish_dates(*args) 
     ... 
     class_eval do 
     def never_expire=(value) 
      @never_expire = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
     end 

     def another_instance_method 
      'something to return' 
     end 
     end 
    end 
    end 
end 
... 

m = MyModel.new 
m.never_expire   #=> nil 
m.never_expire = '1'  #=> true 
m.never_expire   #=> true 
m.another_instance_method #=> 'something to return' 

我想,这是因为之前attr_accessor :never_expire被称为has_publish_datesInstanceMethods定义。

尽管我认为class_eval是做的事情我也喜欢有暴露我的实例方法的文档,所以没有“神奇”时,另一位开发人员正试图用我的代码的想法的一种优雅的方式。

反正我有可以使用include InstanceMethods办法在这种情况下?

回答

10

电话以便与正常情况下的方法开始着手包含的模块和超类方法的方法之前, 。通过attr_accessor创建的never_expire=方法卷起成为一个实例方法,所以它被称为而非InstanceMethods模块的方法。如果您使用attr_reader代替,这样就没有never_expire=实例方法被定义的,它会为你准备工作。

这就是说,你正在做的事情复杂得多,他们需要用这些额外的ClassMethods和InstanceMethods模块。只要使用的模块,比如他们的目的:

module HasPublishDates 
    attr_reader :never_expire 

    def never_expire=(value) 
    @never_expire = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 
end 

class MyModel < ActiveRecord::Base 
    include HasPublishDates 
end 
1

嗯,你可能只是不attr_accessor中费心......毕竟,你只需要添加:

def never_expire 
    @never_expire 
end 

,它会工作得很好没有这种。

如果它是在DB的实​​际AR列,不过,我推荐使用

set_attribute(:never_expire, ActiveRecord::ConnectionAdapters::Column.... 

而非@never_expire变量。在这种情况下,你也不需要attr_accessor。

A中的最后一个选项,你可以在包含语句如使用类-EVAL 只是:在Ruby中

module ClassMethods 
    def has_publish_dates(*args) 
    attr_accessor :never_expire 

    class_eval do 
     include InstanceMethods 
    end 
    end 
end