Ruby-on-rails 如何在 Rails 4 中使用关注点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14541823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use concerns in Rails 4
提问by yagooar
The default Rails 4 project generator now creates the directory "concerns" under controllers and models. I have found some explanations about how to use routing concerns, but nothing about controllers or models.
默认的 Rails 4 项目生成器现在在控制器和模型下创建目录“关注”。我找到了一些关于如何使用路由问题的解释,但没有找到关于控制器或模型的解释。
I am pretty sure it has to do with the current "DCI trend" in the community and would like to give it a try.
我很确定这与社区中当前的“DCI 趋势”有关,并想尝试一下。
The question is, how am I supposed to use this feature, is there a convention on how to define the naming / class hierarchy in order to make it work? How can I include a concern in a model or controller?
问题是,我应该如何使用此功能,是否有关于如何定义命名/类层次结构以使其工作的约定?如何在模型或控制器中包含关注点?
采纳答案by yagooar
So I found it out by myself. It is actually a pretty simple but powerful concept. It has to do with code reuse as in the example below. Basically, the idea is to extract common and / or context specific chunks of code in order to clean up the models and avoid them getting too fat and messy.
所以我自己发现了。它实际上是一个非常简单但功能强大的概念。它与代码重用有关,如下例所示。基本上,这个想法是提取常见的和/或上下文特定的代码块,以清理模型并避免它们变得过于庞大和混乱。
As an example, I'll put one well known pattern, the taggable pattern:
例如,我将放置一个众所周知的模式,可标记模式:
# app/models/product.rb
class Product
include Taggable
...
end
# app/models/concerns/taggable.rb
# notice that the file name has to match the module name
# (applying Rails conventions for autoloading)
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
class_attribute :tag_limit
end
def tags_string
tags.map(&:name).join(', ')
end
def tags_string=(tag_string)
tag_names = tag_string.to_s.split(', ')
tag_names.each do |tag_name|
tags.build(name: tag_name)
end
end
# methods defined here are going to extend the class, not the instance of it
module ClassMethods
def tag_limit(value)
self.tag_limit_value = value
end
end
end
So following the Product sample, you can add Taggable to any class you desire and share its functionality.
因此,按照 Product 示例,您可以将 Taggable 添加到您想要的任何类并共享其功能。
This is pretty well explained by DHH:
DHH很好地解释了这一点:
In Rails 4, we're going to invite programmers to use concerns with the default app/models/concerns and app/controllers/concerns directories that are automatically part of the load path. Together with the ActiveSupport::Concern wrapper, it's just enough support to make this light-weight factoring mechanism shine.
在 Rails 4 中,我们将邀请程序员使用默认 app/models/concerns 和 app/controllers/concerns 目录的关注点,这些目录自动成为加载路径的一部分。与 ActiveSupport::Concern 包装器一起,它的支持足以使这种轻量级的分解机制大放异彩。
回答by Aaditi Jain
I have been reading about using model concernsto skin-nize fat models as well as DRY up your model codes. Here is an explanation with examples:
我一直在阅读有关使用模型问题来简化脂肪模型以及干燥模型代码的文章。下面是一个例子的解释:
1) DRYing up model codes
1) 干燥模型代码
Consider a Article model, a Event model and a Comment model. An article or an event has many comments. A comment belongs to either Article or Event.
考虑一个文章模型、一个事件模型和一个评论模型。一篇文章或一个事件有很多评论。评论属于文章或事件。
Traditionally, the models may look like this:
传统上,模型可能如下所示:
Comment Model:
评论型号:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Article Model:
文章型号:
class Article < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#return the article with least number of comments
end
end
Event Model
事件模型
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#returns the event with least number of comments
end
end
As we can notice, there is a significant piece of code common to both Event and Article. Using concerns we can extract this common code in a separate module Commentable.
我们可以注意到,有一段重要的代码对于 Event 和 Article 是通用的。使用关注点,我们可以在一个单独的模块 Commentable 中提取这个公共代码。
For this create a commentable.rb file in app/models/concerns.
为此,在 app/models/concerns 中创建一个 commentable.rb 文件。
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
# for the given article/event returns the first comment
def find_first_comment
comments.first(created_at DESC)
end
module ClassMethods
def least_commented
#returns the article/event which has the least number of comments
end
end
end
And now your models look like this :
现在你的模型看起来像这样:
Comment Model:
评论型号:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Article Model:
文章型号:
class Article < ActiveRecord::Base
include Commentable
end
Event Model:
事件模型:
class Event < ActiveRecord::Base
include Commentable
end
2) Skin-nizing Fat Models.
2) 使皮肤光滑的脂肪模型。
Consider a Event model. A event has many attenders and comments.
考虑一个事件模型。一个活动有很多参与者和评论。
Typically, the event model might look like this
通常,事件模型可能如下所示
class Event < ActiveRecord::Base
has_many :comments
has_many :attenders
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
def self.least_commented
# finds the event which has the least number of comments
end
def self.most_attended
# returns the event with most number of attendes
end
def has_attendee(attendee_id)
# returns true if the event has the mentioned attendee
end
end
Models with many associations and otherwise have tendency to accumulate more and more code and become unmanageable. Concerns provide a way to skin-nize fat modules making them more modularized and easy to understand.
具有许多关联和其他方面的模型倾向于积累越来越多的代码并变得难以管理。关注点提供了一种皮肤化脂肪模块的方法,使它们更加模块化和易于理解。
The above model can be refactored using concerns as below:
Create a attendable.rband commentable.rbfile in app/models/concerns/event folder
可以使用如下关注点重构上述模型:在 app/models/concerns/event 文件夹中创建一个attendable.rb和commentable.rb文件
attendable.rb
出席人数.rb
module Attendable
extend ActiveSupport::Concern
included do
has_many :attenders
end
def has_attender(attender_id)
# returns true if the event has the mentioned attendee
end
module ClassMethods
def most_attended
# returns the event with most number of attendes
end
end
end
commentable.rb
可评论.rb
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments
end
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
module ClassMethods
def least_commented
# finds the event which has the least number of comments
end
end
end
And now using Concerns, your Event model reduces to
现在使用关注,你的事件模型减少到
class Event < ActiveRecord::Base
include Commentable
include Attendable
end
* While using concerns its advisable to go for 'domain' based grouping rather than 'technical' grouping. Domain Based grouping is like 'Commentable', 'Photoable', 'Attendable'. Technical grouping will mean 'ValidationMethods', 'FinderMethods' etc
* 在使用问题时,建议使用基于“域”的分组而不是“技术”分组。基于域的分组就像“可评论”、“可拍照”、“可参与”。技术分组意味着“ValidationMethods”、“FinderMethods”等
回答by Dr.Strangelove
It's worth to mention that using concerns is considered bad idea by many.
值得一提的是,使用关注点被许多人认为是个坏主意。
Some reasons:
一些原因:
- There is some dark magic happening behind the scenes - Concern is patching
includemethod, there is a whole dependency handling system - way too much complexity for something that's trivial good old Ruby mixin pattern. - Your classes are no less dry. If you stuff 50 public methods in various modules and include them, your class still has 50 public methods, it's just that you hide that code smell, sort of put your garbage in the drawers.
- Codebase is actually harder to navigate with all those concerns around.
- Are you sure all members of your team have same understanding what should really substitute concern?
- 幕后发生了一些黑暗的魔法 - 关注的是修补
include方法,有一个完整的依赖处理系统 - 对于一些微不足道的老式 Ruby 混合模式来说太复杂了。 - 你的课也同样枯燥。如果你在各种模块中填充 50 个公共方法并包含它们,你的类仍然有 50 个公共方法,只是你隐藏了代码气味,有点像把你的垃圾放在抽屉里。
- 代码库实际上更难处理所有这些问题。
- 你确定你团队的所有成员都对什么应该真正替代关注有相同的理解吗?
Concerns are easy way to shoot yourself in the leg, be careful with them.
顾虑很容易让自己射在腿上,小心它们。
回答by aminhotob
回答by Siva
I felt most of the examples here demonstrated the power of modulerather than how ActiveSupport::Concernadds value to module.
我觉得大部分的例子这里演示的力量module,而不是如何ActiveSupport::Concern增加价值module。
Example 1:More readable modules.
示例 1:更具可读性的模块。
So without concerns this how a typical modulewill be.
因此,无需担心这是典型的module情况。
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
def instance_method
...
end
module ClassMethods
...
end
end
After refactoring with ActiveSupport::Concern.
用ActiveSupport::Concern.
require 'active_support/concern'
module M
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
end
class_methods do
...
end
def instance_method
...
end
end
You see instance methods, class methods and included block are less messy. Concerns will inject them appropriately for you. That's one advantage of using ActiveSupport::Concern.
您会看到实例方法、类方法和包含的块不那么混乱。顾虑会为您适当地注入它们。这是使用ActiveSupport::Concern.
Example 2:Handle module dependencies gracefully.
示例 2:优雅地处理模块依赖项。
module Foo
def self.included(base)
base.class_eval do
def self.method_injected_by_foo_to_host_klass
...
end
end
end
end
module Bar
def self.included(base)
base.method_injected_by_foo_to_host_klass
end
end
class Host
include Foo # We need to include this dependency for Bar
include Bar # Bar is the module that Host really needs
end
In this example Baris the module that Hostreally needs. But since Barhas dependency with Foothe Hostclass have to include Foo(but wait why does Hostwant to know about Foo? Can it be avoided?).
在这个例子中Bar是Host真正需要的模块。但是,因为Bar有依赖Foo的Host类必须include Foo(但等待为什么Host想知道Foo?它能否避免?)。
So Baradds dependency everywhere it goes. And order of inclusion also matters here.This adds lot of complexity/dependency to huge code base.
所以Bar随处添加依赖。和包容的顺序也很重要在这里。这给庞大的代码库增加了很多复杂性/依赖性。
After refactoring with ActiveSupport::Concern
重构后 ActiveSupport::Concern
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
def self.method_injected_by_foo_to_host_klass
...
end
end
end
module Bar
extend ActiveSupport::Concern
include Foo
included do
self.method_injected_by_foo_to_host_klass
end
end
class Host
include Bar # It works, now Bar takes care of its dependencies
end
Now it looks simple.
现在看起来很简单。
If you are thinking why can't we add Foodependency in Barmodule itself? That won't work since method_injected_by_foo_to_host_klasshave to be injected in a class that's including Barnot on Barmodule itself.
如果您在想为什么我们不能Foo在Bar模块本身中添加依赖项?这是行不通的,因为method_injected_by_foo_to_host_klass必须注入一个Bar不包含Bar模块本身的类。
Source:Rails ActiveSupport::Concern
回答by Sajjad Murtaza
In concerns make file filename.rb
在关注 make 文件 filename.rb
For example I want in my application where attribute create_by exist update there value by 1, and 0 for updated_by
例如,我希望在我的应用程序中,属性 create_by 存在时将值更新为 1,将更新的值更新为 0
module TestConcern
extend ActiveSupport::Concern
def checkattributes
if self.has_attribute?(:created_by)
self.update_attributes(created_by: 1)
end
if self.has_attribute?(:updated_by)
self.update_attributes(updated_by: 0)
end
end
end
If you want to pass arguments in action
如果你想在行动中传递参数
included do
before_action only: [:create] do
blaablaa(options)
end
end
after that include in your model like this:
之后包含在您的模型中,如下所示:
class Role < ActiveRecord::Base
include TestConcern
end

