У меня есть расширение 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
, которое можно назвать так:
class MyModel < ActiveRecord::Base
has_publish_dates
...
end
Идея состоит в том, что never_expire=
должен переопределить сеттеропределяется 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'
Я думаю, это потому, что InstanceMethods
определено до того, как attr_accessor :never_expire
вызывается has_publish_dates
.
Хотя я думаю, что class_eval
- это элегантный способ делать вещи, мне также нравится идея выставления моих методов экземпляра для документации, чтобы не было "волшебства", когда другой разработчик пытается использовать мой код.
Можно ли в любом случае использовать подход include InstanceMethods
в этом сценарии?