Как сделать простой рубиновый объект, который можно присвоить как активная запись ассоциации


У меня есть класс Audit, который поддерживается ActiveRecord.

class Audit < ActiveRecord::Base
  belongs_to :user, polymorphic: true
end

У меня есть класс User, который представляет собой простой объект ruby с некоторой функциональностью ActiveModel. Это не модель базы данных, потому что мои пользователи фактически хранятся в другой базе данных и обслуживаются через API.

class User
  include ActiveModel::Conversion
  include ActiveModel::Validations
  extend  ActiveModel::Naming

  def self.primary_key
    'id'
  end

  def id
    42
  end

  def persisted?
    false
  end
end

Я пытаюсь назначить пользователя для такого аудита:

audit = Audit.new
audit.user = User.new
audit.save!

С точки зрения данных, это должно работать нормально. Чтобы определить полиморфную ассоциацию, нам нужно разделить значения на два столбцы в таблице аудита. Мы можем установить audits.user_id в значение 42 и audits.user_type в строку "пользователь".

Однако я попал в исключение:

undefined method `_read_attribute' for #<User:0x007f85faa49520 @attributes={"id"=>42}> (NoMethodError)
active_record/associations/belongs_to_polymorphic_association.rb:13:in `replace_keys'

Я проследил это до источника ActiveRecord, и он, кажется, определен здесь. К сожалению, тот факт, что это ActiveRecord, а не ActiveModel, означает, что я не могу включить этот микс в свой класс.

Я попытался определить свой собственный метод _read_attribute, но я спускаюсь в кроличью нору необходимости переопределять все больше и больше функций ActiveRecord например, AttributeSet и так далее.

Я также понимаю, что могу обойти эту проблему, назначив Audit#user_type и Audit#user_id. Однако это неудовлетворительно, потому что на самом деле мне пришлось бы раскошелиться на драгоценный камень и отредактировать его, чтобы сделать это.

Как я могу изменить свой класс пользователя, чтобы я мог чисто назначить его аудиту.

P.S.Вот пример приложения , так что вы можете попробовать это самостоятельно.

1 2

1 ответ:

Вместо того, чтобы взламывать все глубже и глубже для репликации функциональности ActiveRecord, Вы можете рассмотреть возможность фактического наследования от ActiveRecord::Base вместо включения ActiveModel. Единственное ограничение-у вас нет таблицы. Для этого есть драгоценный камень:

Activerecord-tableless

Этот класс работает с вашим примером приложения:

require 'active_record'
require 'activerecord-tableless'

class User < ActiveRecord::Base
  has_no_table

  # required so ActiveRecord doesn't try to create a new associated
  # User record with the audit
  def new_record?
    false
  end

  def id
    42
  end
end