gpt4 book ai didi

ruby-on-rails - Rails 协会的最佳代码结构

转载 作者:数据小太阳 更新时间:2023-10-29 07:29:55 24 4
gpt4 key购买 nike

舞台

让我们谈谈我们遇到的最常见的关联类型。

我有一个用户 :has_many 帖子

class User < ActiveRecord::Base
has_many :posts
end

class Post < ActiveRecord::Base
belongs_to :user
end

问题陈述

我想对用户的所有帖子进行一些(非常轻松和快速的)处理。我正在寻找构建我的代码以实现它的最佳方法。以下是几种方法以及它们有效或无效的原因。

方法一

User 类本身中执行。

class User < ActiveRecord::Base
has_many :posts

def process_posts
posts.each do |post|
# code of whatever 'process' does to posts of this user
end
end
end

课后保持不变:

class Post < ActiveRecord::Base
belongs_to :user
end

该方法被称为:

User.find(1).process_posts

为什么这看起来不是最好的方法

处理用户帖子的逻辑应该真正属于 Post 类。在现实世界的场景中,用户也可能与许多其他类有 :has_many 关系,例如订单评论 child

如果我们开始向 User 类添加类似的 process_ordersprocess_commentsprocess_children (yikes) 方法,它会产生一个巨大的包含大量代码的文件,其中大部分可以(并且应该)分发到它所属的位置,即目标关联。

方法二

代理关联和范围

这两种构造都需要向 User 类添加方法/代码,这再次使其变得臃肿。我宁愿将所有 实现转移到目标类。

方法三

目标类的类方法

在目标类中创建类方法并在 User 对象上调用这些方法。

class User < ActiveRecord::Base
has_many :comments
# all target specific code in target classes
end

class Post < ActiveRecord::Base
belongs_to :user

# Class method
def self.process
Post.all.each do |post| # see Note 2 below
# code of whatever 'process' does to posts of this user
end
end
end

该方法被称为:

User.find(1).posts.process   # See Note 1 below

现在,这看起来和感觉上都比方法 1 和 2 好,因为:

  • 用户模型保持整洁。
  • 流程函数称为process 而不是process_posts。现在我们也可以为其他类创建一个 process 并将它们调用为:User.find(1).orders.process 等而不是 User.find (1).process_orders(方法一).

注1:

是的,您可以在关联上调用这样的类方法。 Read why here . TL;DR 是 User.find(1).posts 返回一个 CollectionProxy 对象,该对象可以访问目标 (Post) 类的类方法。它还方便地传递一个 scope_attributes,它存储调用 posts.process 的用户的 user_id。这很方便。请参阅下面的注释 2。

注2:

当我们在类方法中执行 Post.all.each 时,对于不确定发生了什么的人,它返回调用此方法的用户的所有帖子 针对数据库中的所有帖子。

所以当调用 User.find(99).posts.process 时,Post.all 执行:

SELECT "notes".* FROM "posts" WHERE "posts"."user_id" = $1  [["user_id", 99]]

这是用户 ID:99 的所有帖子。

根据@Jesuspc 下面的评论,Post.all.each 可以简洁地写成 all.each。它更加地道,不会让人觉得我们正在查询数据库中的所有帖子。

我正在寻找的答案

  • 解释处理此类关联的最佳方式。人们通常是怎么做的?方法三是否存在明显的设计缺陷。

最佳答案

还有第四种选择。将此逻辑完全移出模型:

class PostProcessor
def initialize(posts)
@posts = posts
end

def process
@posts.each do |post|
# ...
end
end
end

PostProcessor.new(User.find(1).posts).process

这有时称为服务对象模式。这种方法的一个非常好的好处是它使得为这个逻辑编写测试变得非常简单。这是一篇关于重构“胖”模型的方法和其他方法的精彩博客文章:http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

关于ruby-on-rails - Rails 协会的最佳代码结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34640341/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com