Как копировать поведение class_inheritable_accessor в Rails 3.1?

Начиная с Rails 3.1, class_inheritable_accessor выдает предупреждения об устаревании, вместо этого говорит мне использовать class_attribute. Но class_attribute ведет себя по-разному так, что я продемонстрирую.

Типичным использованием class_inheritable_attribute будет класс презентатора, например:

module Presenter
  class Base
    class_inheritable_accessor :presented
    self.presented = {}

    def self.presents(*types)
      types_and_classes = types.extract_options!
      types.each {|t| types_and_classes[t] = t.to_s.tableize.classify.constantize }
      attr_accessor *types_and_classes.keys
      types_and_classes.keys.each do |t|
        presented[t] = types_and_classes[t]
      end
    end
  end
end

class PresenterTest < Presenter::Base
  presents :user, :person
end

Presenter::Base.presented => {}
PresenterTest.presented => {:user => User, :person => Person}

Но используя class_attribute, подклассы загрязнят своих родителей:

Presenter::Base => {:user => User, :person => Person}

Это нежелательное поведение вообще. Есть ли другой тип доступа, который ведет себя правильно, или мне нужно вообще переключиться на другой шаблон? Как мне повторить одно и то же поведение без class_inheritable_accessor?

Ответы

Ответ 1

class_attribute не будет загрязнять его родительский объект, если он используется по назначению. Убедитесь, что вы не меняете изменяемые элементы на месте.

types_and_classes.keys.each do |t|
  self.presented = presented.merge({t => types_and_classes[t]})
end

Ответ 2

Попробуйте это, отличный способ иметь атрибуты, установленные в классе, и не загрязнять их наследованием или экземплярами.

class Class

  private

  # Sets Unique Variables on a resource
  def inheritable_attr_accessor(*attrs)
    attrs.each do |attr|

      # Class Setter, Defining Setter
      define_singleton_method "#{attr}=".to_sym do |value|
        define_singleton_method attr do
          value
        end
      end

      # Default to nil
      self.send("#{attr}=".to_sym, nil)

      # Instance Setter
      define_method "#{attr}=".to_sym do |value|
        eval("@_#{attr} = value")
      end

      # Instance Getter, gets class var if nil
      define_method attr do
        eval("defined?(@_#{attr})") ? eval("@_#{attr}") : self.class.send(attr)
      end

    end
  end

end