【问题标题】:Attr_accessor for Subclass inside Parent Class父类中子类的 Attr_accessor
【发布时间】:2021-07-22 12:58:56
【问题描述】:

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

“记录”是我想要存储和编写所有代码的父类。 “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 实例变量

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

【问题讨论】:

  • 我有点困惑。您是否只想接受通过Hash 发送的任何内容并为此创建方法,例如Post.new(title: "New post", url: "some url", left_hand_monkey_wrench: true) 是一个有效的 Post 对象,您可以调用 post.left_hand_monkey_wrench #=> true 吗?如果这是您的目标,那么实现可以相当简单,如果不是,请更清楚地解释您的愿望。
  • @engineersmnky 是的,这正是我想要实现的目标!
  • @engineersmnky 能否请您分享一个提示或帮助我?真的很感激
  • 为什么要将所有代码都放入 Record 中?像instance_variable_set(:@votes, params["votes"] || 0) if instance_of?(Post) 这样从父类引用子类只是各种错误。如果Post 有一个特定的属性,它应该定义它而不是把所有东西都放到一个上帝类中。
  • 我知道你在说什么,但它更像是一种“风格练习”。我是 ruby​​ 和编程的新手,实际上我正在尝试学习新的东西,所以在这个特定的案例中,这是一个针对这个问题的抽象练习

标签: ruby metaprogramming


【解决方案1】:

你正在倒退。父类不必了解或实现其子类的特定逻辑。

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 中,让您可以附加“元数据”,为您以后需要的功能(例如类型转换、序列化和脏跟踪)打开。

【讨论】:

  • 鉴于所需实现的简单性(阅读天真),看起来def initialize(**params); @attributes = params; end; 后跟method_missing 查找或简单的@attributes.each {|a| define_method(a) { @attributes[a]}; define_method("#{a}=") {|val| @attributes[a] = val}} 可以解决问题
猜你喜欢
  • 1970-01-01
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-09
相关资源
最近更新 更多