Rails 关注点如何工作以及如何使用它们

2025-06-07

Rails 关注点如何工作以及如何使用它们

注:我已更新本文,添加了更多详细信息。您可以访问我的博客“ Rails 中的疑虑:你需要知道的一切”阅读。

许多 Rails 应用程序(包括框架本身)都大量使用了关注点。但是,如果您刚接触 Rails,关注点可能会让您感到困惑。为什么需要它们?它们如何工作?以及何时使用(或不使用)它们?几个月前,当我开始学习 Rails 时,关注点是我花了很长时间才理解的主题之一。

Rails API 文档也帮不上什么忙。它一开始就假设读者已经熟悉要解决的问题,然后给出解决方案,结果只会让读者更加困惑。

因此我做了一些研究,发现网络上散布着许多有趣的关注点智慧,例如DHH 的这篇博客文章展示了关注点的用法,或者来自核心 Rails 成员的A May of WTFs 线程中的这些 答案,用写作的比喻解释了关注点

这篇博文试图总结这些分散的信息,以描绘出一幅连贯的画面,向您展示关注点如何帮助您编写更好的代码。

问题关注解决

使用关注点可以将不同类中的通用逻辑提取到可重用的模块中。

考虑以下两个模型:PostComment。除了它们各自的代码之外,这两个模型还包含处理其可见性的代码,即visible_to属性、is_visible实例方法和count_all_visible类方法。可能还有很多其他模型需要以类似的方式控制其可见性。如果能有一种方法可以抽象这些代码就好了。

# post.rb

class Post < ApplicationRecord
  belongs_to :author
  has_many :comments, dependent: :delete_all
  has_rich_text :content

  validates :title, presence: true, length: { minimum: 2 }
  validate :has_valid_content

  attr_accessor :visible_to

  def is_visible?
    visible_to.present?
  end

  def has_valid_content
    # some code
  end

  def self.count_all_visible
    all.select { |item| item.is_visible? }
  end
end

# comment.rb

class Comment < ApplicationRecord
  belongs_to :post
  validates :commenter, :body, presence: true

  attr_accessor :visible_to

  def is_visible?
    visible_to.present?
  end

  def self.count_all_visible
    all.select { |item| item.is_visible? }
  end
end
Enter fullscreen mode Exit fullscreen mode

什么是关注?

很多关注点只是简单的 Ruby 模块,并没有添加任何额外的内容。少数情况下,例如当你的关注点同时包含类方法和实例方法,或者你想在 mixin 中调用类方法时,可以使用 extends ActiveSupport::Concern。 - DHH

Rails关注点是扩展模块的模块ActiveSupport::Concern。关注点允许我们将包含方法(实例和类)和常量的模块包含到类中,以便包含类可以使用它们。

关注点提供了两个块:

  1. included
    • 被包含的块内的代码将在包含类的上下文中执行。例如,如果Post包含一个关注点,included则块内的任何内容都将像在 中编写一样进行执行Post
    • 您可以在此处编写类宏(验证、关联、范围等),任何方法都成为包含类的实例方法。
  2. class_methods
    • 该块内添加的方法将成为包含类的类方法。
    • 除了class_methods块之外,您还可以创建一个名为的嵌套模块ClassMethods

这是一个名为Taggable的关注点。

module Taggable
  extend ActiveSupport::Concern

  included do

  end

  class_methods do

  end
end
Enter fullscreen mode Exit fullscreen mode

关注点指提取一个模块,以便将类或模块的实现拆分成连贯的块,而不是拥有一个庞大的类主体。API 接口是相同的,它们只是帮助组织代码。关注点是一个普通的 Ruby mixin,没有其他含义,它不是一个新概念。它是纯粹的 Ruby。—— Xavier Noria

如何使用关注?

让我们创建一个关注点,从和模型Visible中提取与可见性相关的代码。通常,该关注点处理实体的可见性,即它是否可见。PostCommentVisible

# visible.rb

module Visible
  extend ActiveSupport::Concern

  # The code inside the included block is evaluated
  # in the context of the class that includes the Visible concern.
  # You can write class macros here, and
  # any methods become instance methods of the including class.
  included do
    attr_accessor :visible_to

    def is_visible?
      visible_to.present?
    end
  end

  # The methods added inside the class_methods block (or, ClassMethods module)
  # become the class methods on the including class.
  class_methods do
    def count_all_visible
      all.select { |item| item.is_visible? }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

要使用此关注点,请像往常一样包含模块。例如,如果Post模型需要该visibility功能,它就会包含该Visible关注点。

class Post
  include Visible

end
Enter fullscreen mode Exit fullscreen mode

包括这个关注点会将visible_to属性、is_visible实例方法和count_all_visible类方法添加到Post类中。

以下测试说明了这一点。

require "test_helper"

class PostTest < ActiveSupport::TestCase

  test "Post can include the visible concern" do
    post = Post.new
    assert_not post.is_visible?

    post.visible_to = "reader"
    assert_equal "reader", post.visible_to
    assert post.is_visible?

    assert_equal [], Post.count_all_visible
  end
end
Enter fullscreen mode Exit fullscreen mode

我们为什么需要关注?

你可能会想:这么巧妙的设计到底有什么意义?如果我们想把一些常见的行为抽象到一个中心位置,难道不能简单地创建一个新类,把查询封装在一个独立的对象里吗?没错。

您可以创建一个包含共享代码的类,实例化它并使用它。

visibility_manager = Visibilty.new

visibility_manager.is_visible(post)
Enter fullscreen mode Exit fullscreen mode

然而,关注点通常只是适度的抽象,从而产生更友好的 API。所有需要的方法都在主对象上,无需像visibility_manager访问共享代码那样引入中间对象。

以下是 DHH 解决这一问题的方法(无双关语);)

所以换句话说:这是一种写作风格。就像用小标题在更广泛的语境中解释次要的观点一样。你或许可以把所有这些小标题都提取出来,然后把它们变成一篇独立的小文章。但这通常不是合适的提取层次。正如你所见,Accessor 角色最初实际上只是两个方法!它不值得被变成一个独立的类。它没有独立的重心。——五月的 WTFs

这就是所关注的:一种将相关方法组合在一起的方法,同时仍然属于一个类。

这是结构化的写作。

文章来源:https://dev.to/software_writer/how-rails-concerns-work-and-how-to-use-them-gi6
PREV
系统设计面试的 10 个微服务最佳实践
NEXT
开发人员倦怠——迹象、影响和预防