Ruby-自动初始化子类的类实例变量,而无需继承值

时间:2019-04-17 09:49:46

标签: ruby-on-rails ruby ruby-on-rails-5 metaprogramming

我正在寻找一种通过继承自动初始化类变量的解决方案(将其用作访问器并将其初始化为某个值)。但是我不想继承该值,而只是每次在每个类上都从一个新的新对象开始。

我一直在查看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 个答案:

答案 0 :(得分:3)

在继承类变量时,不必初始化它。 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]

答案 1 :(得分:0)

挂钩是个好主意,您只是在解决一个错误的主意:)如果您想在每次继承类时都运行代码,那么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的答案就更简单了。