ActiveRecord::AssociatedObject 使用指南
1. 项目介绍
ActiveRecord::AssociatedObject
是一个新的领域概念,旨在帮助你从 Active Record 模型中提取协作对象。这些对象本质上是无状态的 Ruby 对象(POROs),你可以将它们与 Active Record 模型关联,以获得更简洁的代码和自动化的应用/模型组织。
在 Rails 应用中,模型往往会变得非常庞大,Ruby 社区的常见解决方案是使用服务对象(Service Objects)。然而,服务对象有时会变成另一个杂乱的抽屉,无法帮助你构建和形成领域模型的概念。ActiveRecord::AssociatedObject
正是为了解决这个问题而设计的。
2. 项目快速启动
安装
首先,通过 Bundler 将 gem 添加到你的 Gemfile 中:
$ bundle add active_record-associated_object
如果你不使用 Bundler,可以通过以下命令安装 gem:
$ gem install active_record-associated_object
使用示例
假设你有一个 Post
模型,它封装了一个博客文章的内容管理系统:
# app/models/post.rb
class Post < ApplicationRecord
end
你已经确定在发布文章时需要执行多个操作,但这些行为应该放在哪里呢?如果将它们放在 Post
模型中,可能会变得混乱。如果使用经典的服务对象,你只能访问 def call
方法,这可能不够灵活。
相反,你可以识别一个 Publisher
协作对象,这是一个处理发布的 Ruby 类。你可以将其放置在 Post::
命名空间中,以自动帮助表示该对象属于并协作于 Post
:
# app/models/post/publisher.rb
class Post::Publisher < ActiveRecord::AssociatedObject
end
然后在 Post
模型中声明它:
# app/models/post.rb
class Post < ApplicationRecord
has_object :publisher
end
现在,你可以通过 publisher
方法访问 Post
的 Publisher
对象:
post = Post.new
post.publisher # 返回 Post::Publisher 实例
3. 应用案例和最佳实践
测试关联对象
在测试中,遵循 app/models/post.rb
和 app/models/post/publisher.rb
的命名结构,并添加 test/models/post/publisher_test.rb
。然后像测试其他对象一样测试它:
# test/models/post/publisher_test.rb
class Post::PublisherTest < ActiveSupport::TestCase
setup do
@publisher = posts(:one).publisher
end
test "publish updates the post" do
@publisher.publish
assert @publisher.post.reload.published?
end
end
扩展 Active Record
你可以在关联对象中使用 extension
块来扩展 Active Record 模型:
class Post::Publisher < ActiveRecord::AssociatedObject
extension do
has_many :contracts, dependent: :destroy do
def signed
all(&:signed?)
end
end
after_create_commit :publish_later, if: -> { contracts.signed? }
private
def publish_later
publisher.publish_later
end
end
end
4. 典型生态项目
ActiveJob::Performs
如果你还捆绑了 active_job-performs
,你可以使用 performs
宏来简化 Active Job 的样板代码:
gem "active_job-performs"
gem "active_record-associated_object"
class Post::Publisher < ActiveRecord::AssociatedObject
performs queue_as: :important
performs :publish
performs :retract
def publish
# 发布逻辑
end
def retract(reason:)
# 撤回逻辑
end
end
Kredis 集成
ActiveRecord::AssociatedObject
还支持 Kredis 集成,因此你可以在关联对象中使用任何 kredis_*
类型:
class Post::Publisher < ActiveRecord::AssociatedObject
kredis_datetime :publish_at
end
结语
ActiveRecord::AssociatedObject
通过聚焦于命名空间协作对象的概念,帮助你更好地组织和编写应用。通过使用这个 gem,你可以看到在构建新功能时如何改变你的应用结构和编写方式。