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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:20:45  来源:igfitidea点击:

How to use concerns in Rails 4

ruby-on-railsruby-on-rails-4dci

提问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.rbcommentable.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.

值得一提的是,使用关注点被许多人认为是个坏主意。

  1. like this guy
  2. and this one
  1. 喜欢这个人
  2. 和这个

Some reasons:

一些原因:

  1. 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.
  2. 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.
  3. Codebase is actually harder to navigate with all those concerns around.
  4. Are you sure all members of your team have same understanding what should really substitute concern?
  1. 幕后发生了一些黑暗的魔法 - 关注的是修补include方法,有一个完整的依赖处理系统 - 对于一些微不足道的老式 Ruby 混合模式来说太复杂了。
  2. 你的课也同样枯燥。如果你在各种模块中填充 50 个公共方法并包含它们,你的类仍然有 50 个公共方法,只是你隐藏了代码气味,有点像把你的垃圾放在抽屉里。
  3. 代码库实际上更难处理所有这些问题。
  4. 你确定你团队的所有成员都对什么应该真正替代关注有相同的理解吗?

Concerns are easy way to shoot yourself in the leg, be careful with them.

顾虑很容易让自己射在腿上,小心它们。

回答by aminhotob

This posthelped me understand concerns.

这篇文章帮助我理解了担忧。

# app/models/trader.rb
class Trader
  include Shared::Schedule
end

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end

回答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?).

在这个例子中BarHost真正需要的模块。但是,因为Bar有依赖FooHost类必须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.

如果您在想为什么我们不能FooBar模块本身中添加依赖项?这是行不通的,因为method_injected_by_foo_to_host_klass必须注入一个Bar不包含Bar模块本身的类。

Source:Rails ActiveSupport::Concern

来源:Rails ActiveSupport::关注

回答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