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

У меня есть 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, Однако это неудовлетворительно, потому что на самом деле мне пришлось бы раскошелиться на камень и отредактировать его, чтобы сделать это.

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

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

1 ответ

Решение

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

ActiveRecord-бестабличные

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

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
Другие вопросы по тегам