【问题标题】:Inheriting class definition from parent class从父类继承类定义
【发布时间】:2017-11-18 21:26:17
【问题描述】:

我正在我的 Rails 模型中构建葡萄实体,如下所述:

https://github.com/ruby-grape/grape-entity#entity-organization

目前我正在根据模型本身的列哈希自动创建默认值。

所以我有一个静态的 get_entity 方法,可以公开所有模型的列:

class ApplicationRecord < ActiveRecord::Base

  def self.get_entity(target)
    self.columns_hash.each do |name, column|
      target.expose name, documentation: { desc: "Col #{name} of #{self.to_s}" }
    end
  end

end

然后我在这里有一个示例 Book 模型,在声明的 Entity 子类中使用它(注释还显示了我如何覆盖模型列之一的文档):

class Book < ActiveRecord::Base

  class Entity < Grape::Entity
    Book::get_entity(self)
    # expose :some_column, documentation: {desc: "this is an override"}
  end

end

这种方法的缺点是我总是需要在我想要实体的每个模型中复制并粘贴类实体声明。

谁能帮我自动为 ApplicationRecord 的所有子级生成实体类?然后,如果我需要覆盖,我将需要在类中有实体声明,否则如果默认声明足够并且可以保持原样。

注意:

我不能直接在 ApplicationRecord 中添加实体类定义,因为实体类应该调用 get_entity 并且 get_entity 取决于 Books 的 column_hash。

解决方案:

感谢brainbag,最终做到了:

def self.inherited(subclass)
  super
  # definition of Entity
  entity = Class.new(Grape::Entity)
  entity.class_eval do
    subclass.get_entity(entity)
  end
  subclass.const_set "Entity", entity

  # definition of EntityList
  entity_list = Class.new(Grape::Entity)
  entity_list.class_eval do
    expose :items, with: subclass::Entity
    expose :meta, with: V1::Entities::Meta
  end
  subclass.const_set "EntityList", entity_list
end

def self.get_entity(entity)
  model = self
  model.columns_hash.each do |name, column|
    entity.expose name, documentation: { type: "#{V1::Base::get_grape_type(column.type)}", desc: "The column #{name} of the #{model.to_s.underscore.humanize.downcase}" }
  end
end

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby grape-api grape-entity


    【解决方案1】:

    我没有使用过 Grape,所以这里可能有一些你需要的额外魔法,但我不知道,但这在 Ruby/Rails 中很容易做到。根据您的问题“自动为 ApplicationRecord 的所有子项生成类实体”,您可以这样做:

    class ApplicationRecord < ActiveRecord::Base
      self.abstract_class = true
    
      class Entity < Grape::Entity
        # whatever shared stuff you want
      end
    end
    

    然后图书将可以访问父级Entity

    > Book::Entity
    => ApplicationRecord::Entity
    

    如果您只想向Book::Entity 添加额外的代码,您可以在Book 中对其进行子类化,如下所示:

    class Book < ApplicationRecord
      class Entity < Entity # subclasses the parent Entity, don't forget this
        # whatever Book-specific stuff you want
      end
    end
    

    那么Book::Entity 将是它自己的类。

    > Book::Entity
    => Book::Entity
    

    要将这与您需要在继承的类上调用 get_entity 结合起来,您可以使用 #inherited 方法在任何时候自动调用 get_entity ApplicationRecord 是子类:

    class ApplicationRecord < ActiveRecord::Base
      self.abstract_class = true
    
      def self.get_entity(target)
        target.columns_hash.each do |name, column|
          target.expose name, documentation: { desc: "Col #{name} of #{self.to_s}" }
        end
      end
    
      def self.inherited(subclass)
        super
        get_entity(subclass)
      end
    
      class Entity < Grape::Entity
        # whatever shared stuff you want
      end
    end
    

    【讨论】:

    • 问题是 get_entity 结果取决于上面示例中的实际 Book 模型。我怎么能指向被调用者模型?如果我使用 self,它将指向 ApplicationRecord 而不是 Book。
    • 您可以使用parent::get_entity(self) 而不是Book::get_entity(self)。如果您对此有任何疑问,请告诉我,我会更详细地更新答案。
    • 是的,即使是父级,我也肯定有问题:ApplicationRecord(abstract):Class 的未定义方法 `get_entity'
    • 流程必须是: 1. Book 被初始化 2. Book 从 ApplicationRecord 继承 Entity 3. Entity 类有一个由 Grape::Entity 继承的 'expose' 函数,所以如果 'self' 没有' t 指向实体,你需要明确地写出来 4. 需要找到一种方法来调用 get_entity,以便 columns_hash 指向 Book 模型(或 2.Book 的任何其他模型子模型从 ApplicationRecord 继承)
    • def self.inherited(subclass) 绝对是答案
    猜你喜欢
    • 2020-06-06
    • 2017-03-18
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    相关资源
    最近更新 更多