Как сделать простой объект 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
, Ваше единственное ограничение - у вас нет стола. Для этого есть жемчужина:
Этот класс работает с вашим примером приложения:
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