Ruby - автоматически инициализировать переменные экземпляра класса дочерних классов без наследования значения - PullRequest
1 голос
/ 17 апреля 2019

Я ищу решение для автоматической инициализации переменной класса через наследование (сделать ее доступной в качестве средства доступа и инициализировать ее каким-либо значением). Но я НЕ хочу наследовать значение, просто начните с нового свежего объекта каждый раз в каждом классе.

Я смотрел на class_attributes и думал, что нашел обходной путь, но он, кажется, не работает так, как я думал (и даже если бы он работал, он, скорее всего, не сделал бы то, что я хочу, так как тот же массив будет использоваться везде, поэтому он будет вести себя как переменная @@)

class AbstractClass
  class_attribute :metadata
  @metadata = [] # initialize metadata to an empty array

  def self.add_metadata(metadata)
    @metadata << metadata
  end
end

def ChildClass < AbstractClass
  add_metadata(:child_class1)
end

def ChildClass2 < AbstractClass
  add_metadata(:child_class2)
end 

Я хотел бы иметь следующее:

AbstractClass.metadata # Don't really care about this one
ChildClass1.metadata # => [:child_class1]
ChildClass2.metadata # => [:child_class2]

Я могу придумать способ сделать это, используя модули с AS :: Support

module InitializeClassInstanceVars
  extend ActiveSupport::Concern 

  included do 
    class_attribute :metadata
    self.metadata = []
  end
end

... и включить этот модуль в каждый вложенный класс (и я считаю, что именно это, например, и делает mongoid)

но я надеялся, что смогу сделать это напрямую через наследование

Ответы [ 2 ]

3 голосов
/ 17 апреля 2019

Вам не нужно инициализировать переменную класса, когда она наследуется.Стиль Ruby должен возвращать и назначать значение по умолчанию, когда переменная не была установлена ​​и к ней обращаются в первый раз.

Просто создайте для этого другой метод класса:

class AbstractClass
  def self.metadata
    @metadata ||= []
  end

  def self.add_metadata(metadata)
    self.metadata << metadata
  end
end

class ChildClass1 < AbstractClass
  add_metadata(:child_class1)
end

class ChildClass2 < AbstractClass
  add_metadata(:child_class2)
end

AbstractClass.metadata # => []
ChildClass1.metadata # => [:child_class1]
ChildClass2.metadata # => [:child_class2]
0 голосов
/ 17 апреля 2019

Хуки - отличная идея, вы просто работаете не с тем :) Если вы хотите запускать код каждый раз, когда что-то наследует вашего класса, тогда нужно использовать inherited:

class AbstractClass
  class << self
    attr_accessor :metadata

    def inherited(child)
      child.instance_variable_set(:@metadata, [child.name])
    end
  end
end

class ChildClass1 < AbstractClass; end
class ChildClass2 < AbstractClass; end

ChildClass1.metadata
# => ["ChildClass1"]
ChildClass2.metadata
# => ["ChildClass2"]

Учитывая, что вопрос помечен , вы также должны иметь String#underscore в наличии;замените child.name на child.name.underscore.to_s, чтобы получить [:child_class1].

РЕДАКТИРОВАТЬ: Возможно, я неправильно понял вопрос.Если вы просто хотите начать с пустого массива, к которому вы можете добавить, ответ chumakoff более прост.

...