Наследование определения класса от родительского класса

Я строю Grape Entities внутри моих моделей Rails, как описано здесь:

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

В настоящее время я создаю значения по умолчанию автоматически на основе хэша столбца самой модели.

Итак, у меня есть статический метод 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

Недостатком этого подхода является то, что мне всегда нужно копировать и вставлять объявление класса Entity в каждую модель, для которой я хочу Entity.

Кто-нибудь может помочь мне автоматически сгенерировать класс Entity для всех потомков ApplicationRecord? Тогда, если мне нужны переопределения, мне нужно будет иметь объявление Entity в классе, в противном случае, если объявление по умолчанию достаточно и может оставить его как есть.

НОТА:

Я не могу добавить определение класса Entity прямо в ApplicationRecord, потому что класс Entity должен вызывать get_entity, а get_entity зависит от column_hash of Books.

РЕШЕНИЕ:

в итоге сделал это благодаря мозговому мешку:

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

Спасибо!

1 ответ

Решение

Я не использовал Grape, поэтому здесь может быть какая-то дополнительная магия, о которой я не знаю, но в Ruby/Rails это легко сделать. Исходя из вашего вопроса "автоматическое создание класса Entity для всех дочерних элементов 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
Другие вопросы по тегам