tri*_*anm 7 ruby ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.1
我有一个ActiveRecord扩展名(缩写):
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
Run Code Online (Sandbox Code Playgroud)
可以像这样调用:
class MyModel < ActiveRecord::Base
has_publish_dates
...
end
Run Code Online (Sandbox Code Playgroud)
我们的想法是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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,another_instance_method正在被包含并正在按预期工作,但never_expire=并未像我预期的那样覆盖了setter.
如果我更改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'
Run Code Online (Sandbox Code Playgroud)
我想这是因为InstanceMethods之前定义的attr_accessor :never_expire是被调用的has_publish_dates.
虽然我认为这class_eval是一种优雅的做事方式,但我也喜欢将我的实例方法暴露给文档,因此当另一个开发人员试图使用我的代码时,没有"魔力".
无论如何我可以include InstanceMethods在这种情况下使用这种方法吗?
Joh*_*ohn 12
在继续使用包含模块和超类方法的方法之前,Ruby中的调用顺序以普通实例方法开始.never_expire=由attr_accessor最后创建的方法是一个实例方法,所以它被调用而不是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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9425 次 |
| 最近记录: |