Переопределение метода установки для attr_accessor при включении модуля InstanceMethods - PullRequest
7 голосов
/ 08 ноября 2011

У меня есть расширение 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 в этом сценарии?

Ответы [ 2 ]

12 голосов
/ 08 ноября 2011

Порядок вызовов в 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
1 голос
/ 08 ноября 2011

Ну, вы могли бы просто не беспокоиться об attr_accessor ... в конце концов, вам просто нужно добавить:

def never_expire
  @never_expire
end

, и без этого все будет работать нормально.

Однако, если это фактический столбец AR в БД, я бы рекомендовал использовать

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

вместо переменной @never_expire.Вам также не понадобится attr_accessor в этом случае.

В качестве окончательного варианта вы можете использовать class-eval просто в операторе включения, например:

module ClassMethods
  def has_publish_dates(*args)
    attr_accessor :never_expire

    class_eval do
      include InstanceMethods
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...