如何使普通的 ruby​​ 对象可分配为活动记录关联

Posted

技术标签:

【中文标题】如何使普通的 ruby​​ 对象可分配为活动记录关联【英文标题】:How to make a plain ruby object assignable as active record association 【发布时间】:2015-12-20 16:03:32 【问题描述】:

我有一个由 ActiveRecord 支持的 Audit 类。

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 设置为字符串“User”。

但是,我遇到了一个异常:

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

我将其追溯到ActiveRecord 源,它似乎被定义为here。不幸的是,它是 ActiveRecord 而不是 ActiveModel 的事实意味着我不能将该 mixin 包含到我的课程中。

我尝试定义自己的 _read_attribute 方法,但我不得不重新定义越来越多的 ActiveRecord 功能,例如 AttributeSet 等等。

我也意识到我可以通过分配Audit#user_typeAudit#user_id 来解决这个问题。然而,这并不令人满意,因为实际上,我必须派生一个 gem 并对其进行编辑才能做到这一点。

如何修改我的用户类,以便我可以干净地将其分配给审计。

附: Here's a sample app 所以你可以自己试试。

【问题讨论】:

【参考方案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

【讨论】:

以上是关于如何使普通的 ruby​​ 对象可分配为活动记录关联的主要内容,如果未能解决你的问题,请参考以下文章

Google Maps API v2:如何使标记可点击?

如何在普通的旧 Ruby 中模拟 SQL `timestamp`?

使用辅助方法和 Ruby 可枚举返回 html 格式的对象

如何使按钮看起来好像被按下?

ruby 活动记录有用的命令

ruby 使用ssl加密的活动记录