缺省的Rails 4项目生成器现在在控制器和模型下创建目录“关注点”。我找到了一些关于如何使用路由关注点的解释,但没有关于控制器或模型的解释。

我很确定这与当前社区中的“DCI趋势”有关,我想尝试一下。

问题是,我应该如何使用这个功能,为了使它工作,如何定义命名/类层次结构是否有一个约定?如何在模型或控制器中包含关注点?


当前回答

所以我自己发现了。这实际上是一个非常简单但强大的概念。它与代码重用有关,如下面的例子所示。基本上,这个想法是提取公共和/或特定于上下文的代码块,以清理模型,避免它们变得过于臃肿和混乱。

作为一个例子,我将给出一个众所周知的模式,即可标记模式:

# 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

因此,按照Product示例,您可以将Taggable添加到您想要的任何类中,并共享其功能。

DHH很好地解释了这一点:

在Rails 4中,我们将邀请程序员使用关注点 默认的app/models/concerns和app/controllers/concerns目录 自动成为加载路径的一部分。连同 关注点包装器,它的支持足以做到这一点 轻量级保理机制大放异彩。

其他回答

在关注点中,创建文件filename.rb

例如,我想在我的应用程序中,属性create_by存在更新值为1,updated_by为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

如果你想实际传递参数

included do
   before_action only: [:create] do
     blaablaa(options)
   end
end

之后,在你的模型中包括如下内容:

class Role < ActiveRecord::Base
  include TestConcern
end

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

就像这个人 还有这个

一些原因:

There is some dark magic happening behind the scenes - Concern is patching include method, 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?

忧虑很容易让你伤到自己的腿,小心对待它们。

我一直在阅读关于使用模型关注皮肤脂肪模型以及DRY你的模型代码。下面是一个例子的解释:

1)耗尽模型代码

考虑文章模型、事件模型和评论模型。一篇文章或一件事有很多评论。评论属于Article或Event。

传统上,模型可能是这样的:

评价模型:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

文章模型:

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

事件模型

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

我们可以注意到,Event和Article都有一段重要的代码。使用关注点,我们可以在一个单独的模块Commentable中提取这个公共代码。

为此,创建一个可注释对象。app/models/concerns中的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

现在你的模型是这样的:

评价模型:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

文章模型:

class Article < ActiveRecord::Base
  include Commentable
end

事件模型:

class Event < ActiveRecord::Base
  include Commentable
end

2)长皮肤的胖模特。

考虑一个事件模型。一个活动有许多参与者和评论。

通常,事件模型看起来像这样

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

具有许多关联的模型往往会积累越来越多的代码,从而变得难以管理。关注点提供了一种简化脂肪模块的方法,使它们更加模块化且易于理解。

上面的模型可以使用下面的关注点进行重构: 创建一个可出席者。Rb和可注释的。app/models/concerns/event文件夹下的Rb文件

attendable.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

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

现在使用关注点,您的事件模型简化为

class Event < ActiveRecord::Base
  include Commentable
  include Attendable
end

*在使用关注点时,建议采用“基于领域”的分组,而不是“技术”分组。基于域的分组就像“可评论的”,“可拍照的”,“可出席的”。技术分组将意味着“ValidationMethods”,“FinderMethods”等

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

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

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

所以我自己发现了。这实际上是一个非常简单但强大的概念。它与代码重用有关,如下面的例子所示。基本上,这个想法是提取公共和/或特定于上下文的代码块,以清理模型,避免它们变得过于臃肿和混乱。

作为一个例子,我将给出一个众所周知的模式,即可标记模式:

# 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

因此,按照Product示例,您可以将Taggable添加到您想要的任何类中,并共享其功能。

DHH很好地解释了这一点:

在Rails 4中,我们将邀请程序员使用关注点 默认的app/models/concerns和app/controllers/concerns目录 自动成为加载路径的一部分。连同 关注点包装器,它的支持足以做到这一点 轻量级保理机制大放异彩。