父类中子类的 Attr_accessor

时间:2021-07-22 12:58:56

标签: ruby metaprogramming

我正在尝试找出一种基于父类动态生成子类的方法。在我的特定情况下,我希望每个实例变量都有 attr_accessor,在我的父类中初始化并在子类上继承。 我的类是代表数据库中三个不同表的三个不同模型。

“Record”是我的父类,我想在其中存储和编写我的所有代码。 “Post”和“User”是继承的子类。

我的代码

class Record
 
  attr_reader :id
  # attr_accessor

  def initialize(**params)
    @id = params[:id]
    instance_variable_set("@#{params.keys[0]}", params.values[0])
    instance_variable_set("@#{params.keys[1]}", params.values[1])
    instance_variable_set(:@votes, params["votes"] || 0) if instance_of?(Post)
    # p self.title
  end

我想要实现的是将 attr_accessor 设置为例如在我想调用的子类“Post”中

post = Post.new(title: "New post", url: "some url")
puts post.title

我可以在不引发 NoMethodError 的情况下访问 title 实例变量

有人可以指导我,或者给我一些提示吗? 谢谢

1 个答案:

答案 0 :(得分:0)

你正在倒退。父类不应该知道或实现其子类的特定逻辑。

class Record
  attr_reader :id

  def initialize(**attributes)
    attributes.each do |key, value|
      send("#{key}=", value)
    end
  end
end
class Post < Record
  attr_accessor :title
  attr_accessor :votes
end
irb(main):066:0> Post.new(id: 1, votes: 10, title: "Hello World").title
=> "Hello World"

attr_accessor 只是用于定义方法的元编程便利,因此无论如何都可以继承访问器方法。但是,如果您正在编写诸如对象关系管理器之类的东西,您将需要定义自己的宏方法来定义属性,以便跟踪类的属性:

module Attributes
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      @attributes ||= {}
    end
  end

  # assigns the passed attributes to the instance
  def initialize(**attributes)
    attributes.each do |key, value|
      send "#{key}=", value
    end
  end

  # gets all the attributes of an instance
  def attributes
    self.class.attributes.keys.each_with_object(Hash.new) do |key, hash|
      hash[key] = send(key)
    end
  end

  module ClassMethods
    # Inherits the attributes of the parent class
    def inherited(subclass)
      attributes.tap do |parent_attributes|
        subclass.class_eval do
          @attributes ||= {}.merge(parent_attributes)
        end
      end
    end

    # defines an attribute that is inherited
    def attribute(name, type = nil, **kwargs)
      @attributes[name] = { type: type }.merge(kwargs)
      attr_accessor name
    end

    def attributes
      @attributes
    end
  end
end
class Record
  include Attributes
  attribute :id, Integer
end
class Post < Record
  attribute :title, String
  attribute :votes, Integer
end
irb(main):101:0> Post.new(votes: 10, title: "Hello World").attributes
=> {:id=>nil, :title=>"Hello World", :votes=>10}

这将属性定义存储在 class instance variable 中,让您可以附加“元数据”,从而为您稍后需要的功能打开,例如类型转换、序列化和脏跟踪。