Rails 关注点如何工作以及如何使用它们
注:我已更新本文,添加了更多详细信息。您可以访问我的博客“ Rails 中的疑虑:你需要知道的一切”阅读。
许多 Rails 应用程序(包括框架本身)都大量使用了关注点。但是,如果您刚接触 Rails,关注点可能会让您感到困惑。为什么需要它们?它们如何工作?以及何时使用(或不使用)它们?几个月前,当我开始学习 Rails 时,关注点是我花了很长时间才理解的主题之一。
Rails API 文档也帮不上什么忙。它一开始就假设读者已经熟悉要解决的问题,然后给出解决方案,结果只会让读者更加困惑。
因此我做了一些研究,发现网络上散布着许多有趣的关注点智慧,例如DHH 的这篇博客文章展示了关注点的用法,或者来自核心 Rails 成员的A May of WTFs 线程中的这些 答案,用写作的比喻解释了关注点。
这篇博文试图总结这些分散的信息,以描绘出一幅连贯的画面,向您展示关注点如何帮助您编写更好的代码。
问题关注解决
使用关注点可以将不同类中的通用逻辑提取到可重用的模块中。
考虑以下两个模型:Post和Comment。除了它们各自的代码之外,这两个模型还包含处理其可见性的代码,即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
什么是关注?
很多关注点只是简单的 Ruby 模块,并没有添加任何额外的内容。少数情况下,例如当你的关注点同时包含类方法和实例方法,或者你想在 mixin 中调用类方法时,可以使用 extends ActiveSupport::Concern。 - DHH
Rails关注点是扩展模块的模块ActiveSupport::Concern
。关注点允许我们将包含方法(实例和类)和常量的模块包含到类中,以便包含类可以使用它们。
关注点提供了两个块:
included
- 被包含的块内的代码将在包含类的上下文中执行。例如,如果
Post
包含一个关注点,included
则块内的任何内容都将像在 中编写一样进行执行Post
。 - 您可以在此处编写类宏(验证、关联、范围等),任何方法都成为包含类的实例方法。
- 被包含的块内的代码将在包含类的上下文中执行。例如,如果
class_methods
- 该块内添加的方法将成为包含类的类方法。
- 除了
class_methods
块之外,您还可以创建一个名为的嵌套模块ClassMethods
。
这是一个名为Taggable的关注点。
module Taggable
extend ActiveSupport::Concern
included do
end
class_methods do
end
end
关注点是指提取一个模块,以便将类或模块的实现拆分成连贯的块,而不是拥有一个庞大的类主体。API 接口是相同的,它们只是帮助组织代码。关注点是一个普通的 Ruby mixin,没有其他含义,它不是一个新概念。它是纯粹的 Ruby。—— Xavier Noria
如何使用关注?
让我们创建一个关注点,从和模型Visible
中提取与可见性相关的代码。通常,该关注点处理实体的可见性,即它是否可见。Post
Comment
Visible
# 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
要使用此关注点,请像往常一样包含模块。例如,如果Post
模型需要该visibility
功能,它就会包含该Visible
关注点。
class Post
include Visible
end
包括这个关注点会将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
我们为什么需要关注?
你可能会想:这么巧妙的设计到底有什么意义?如果我们想把一些常见的行为抽象到一个中心位置,难道不能简单地创建一个新类,把查询封装在一个独立的对象里吗?没错。
您可以创建一个包含共享代码的类,实例化它并使用它。
visibility_manager = Visibilty.new
visibility_manager.is_visible(post)
然而,关注点通常只是适度的抽象,从而产生更友好的 API。所有需要的方法都在主对象上,无需像visibility_manager
访问共享代码那样引入中间对象。
以下是 DHH 解决这一问题的方法(无双关语);)
所以换句话说:这是一种写作风格。就像用小标题在更广泛的语境中解释次要的观点一样。你或许可以把所有这些小标题都提取出来,然后把它们变成一篇独立的小文章。但这通常不是合适的提取层次。正如你所见,Accessor 角色最初实际上只是两个方法!它不值得被变成一个独立的类。它没有独立的重心。——五月的 WTFs
这就是所关注的:一种将相关方法组合在一起的方法,同时仍然属于一个类。
这是结构化的写作。
文章来源:https://dev.to/software_writer/how-rails-concerns-work-and-how-to-use-them-gi6