ActiveRecord:添加 'fake' 模型 class 的最佳方式?
ActiveRecord: Best way to add a 'fake' model class?
在我们的 Rails 应用程序中,Post
资源可以由 User
或 Admin
创建。
因此,我们有一个名为 Post
的 ActiveRecord 模型 class,带有 belongs_to :author, polymorphic: true
.
但是,在某些情况下,系统本身应该能够创建帖子。
因此,我正在寻找一种方法来添加例如System
作为 author
.
显然,永远只有一个 System
,因此它不会存储在数据库中。
天真地尝试将 class System; end
的实例(例如单例实例)添加为作者 returns 错误,例如 NoMethodError: undefined method `primary_key' for System:Class
.
解决这个问题最干净的方法是什么?
有没有一种方法可以编写 'fake' 实际上不属于数据库的 ActiveRecord 模型?
我认为有两种方法最有意义:
选项 A:向数据库添加 'system' Author
记录
这不是一个可怕的想法,它只是将负担转移到您身上,以确保某些记录存在于每个环境中。但是,如果您想确保始终创建这些记录,您始终可以在种子文件中创建这些记录。
与选项 B 相比的好处是您可以只使用标准 ActiveRecord
查询来查找系统的所有 Post
。
选项 B:保留关联 nil 并为 :created_by_system
添加新标志
这是我会选择的。如果 Post
是系统创建的,只需将作者引用留空并设置一个特殊标志以指示该模型是内部创建的。
您仍然可以通过创建一个范围来快速获得所有这些列表的方法:
scope :from_system, -> { where(created_by_system: :true) }
我认为您选择哪一个取决于您是否希望能够查询 Post.author
并获取有关系统的信息。在这种情况下,您需要选择选项 A。否则,我会使用选项 B。我相信还有一些其他方法可以做到这一点,但我认为这是最有意义的。
最后我创建了以下 'fake' 模型 class,它不需要对数据库架构进行任何更改。
它利用了一些元编程:
# For the cases in which the System itself needs to be given an identity.
# (such as when it does an action normally performed by a User or Admin, etc.)
class System
include ActiveModel::Model
class << self
# The most beautiful kind of meta-singleton
def class
self
end
def instance
self
end
# Calling`System.new` is a programmer mistake;
# they should use plain `System` instead.
private :new
def primary_key
:id
end
def id
1
end
def readonly?
true
end
def persisted?
true
end
def _read_attribute(attr)
return self.id if attr == :id
nil
end
def polymorphic_name
self.name
end
def destroyed?
false
end
def new_record?
false
end
end
end
这里要注意的是 System
既是它自己的 class 又是它自己的实例。
这有以下优点:
- 我们可以只传递
Post.new(creator: System)
而不是 System.new
或 System.instance
- 任何时候都只有一个系统。
- 我们可以在
System
本身而不是 Class
. 上定义 ActiveRecord 需要 (polymorphic_name
) 的 class 方法
当然,你是喜欢这种元编程还是觉得它太复杂是很主观的。
不太主观的是覆盖 ActiveRecord 的 _read_attribute
并不好;我们依赖于 ActiveRecord 的实现细节。不幸的是,据我所知,没有公开的 public API 可用于更干净地执行此操作。 (在我们的项目中,我们有一些规范可以在 ActiveRecord 可能更改时立即通知我们。)
在我们的 Rails 应用程序中,Post
资源可以由 User
或 Admin
创建。
因此,我们有一个名为 Post
的 ActiveRecord 模型 class,带有 belongs_to :author, polymorphic: true
.
但是,在某些情况下,系统本身应该能够创建帖子。
因此,我正在寻找一种方法来添加例如System
作为 author
.
显然,永远只有一个 System
,因此它不会存储在数据库中。
天真地尝试将 class System; end
的实例(例如单例实例)添加为作者 returns 错误,例如 NoMethodError: undefined method `primary_key' for System:Class
.
解决这个问题最干净的方法是什么?
有没有一种方法可以编写 'fake' 实际上不属于数据库的 ActiveRecord 模型?
我认为有两种方法最有意义:
选项 A:向数据库添加 'system' Author
记录
这不是一个可怕的想法,它只是将负担转移到您身上,以确保某些记录存在于每个环境中。但是,如果您想确保始终创建这些记录,您始终可以在种子文件中创建这些记录。
与选项 B 相比的好处是您可以只使用标准 ActiveRecord
查询来查找系统的所有 Post
。
选项 B:保留关联 nil 并为 :created_by_system
添加新标志
这是我会选择的。如果 Post
是系统创建的,只需将作者引用留空并设置一个特殊标志以指示该模型是内部创建的。
您仍然可以通过创建一个范围来快速获得所有这些列表的方法:
scope :from_system, -> { where(created_by_system: :true) }
我认为您选择哪一个取决于您是否希望能够查询 Post.author
并获取有关系统的信息。在这种情况下,您需要选择选项 A。否则,我会使用选项 B。我相信还有一些其他方法可以做到这一点,但我认为这是最有意义的。
最后我创建了以下 'fake' 模型 class,它不需要对数据库架构进行任何更改。
它利用了一些元编程:
# For the cases in which the System itself needs to be given an identity.
# (such as when it does an action normally performed by a User or Admin, etc.)
class System
include ActiveModel::Model
class << self
# The most beautiful kind of meta-singleton
def class
self
end
def instance
self
end
# Calling`System.new` is a programmer mistake;
# they should use plain `System` instead.
private :new
def primary_key
:id
end
def id
1
end
def readonly?
true
end
def persisted?
true
end
def _read_attribute(attr)
return self.id if attr == :id
nil
end
def polymorphic_name
self.name
end
def destroyed?
false
end
def new_record?
false
end
end
end
这里要注意的是 System
既是它自己的 class 又是它自己的实例。
这有以下优点:
- 我们可以只传递
Post.new(creator: System)
而不是System.new
或System.instance
- 任何时候都只有一个系统。
- 我们可以在
System
本身而不是Class
. 上定义 ActiveRecord 需要 (
polymorphic_name
) 的 class 方法
当然,你是喜欢这种元编程还是觉得它太复杂是很主观的。
不太主观的是覆盖 ActiveRecord 的 _read_attribute
并不好;我们依赖于 ActiveRecord 的实现细节。不幸的是,据我所知,没有公开的 public API 可用于更干净地执行此操作。 (在我们的项目中,我们有一些规范可以在 ActiveRecord 可能更改时立即通知我们。)