ruby-on-rails - rails知乎 - 创建rails项目




如何在Rails 4中使用关注点 (4)

这篇文章帮助我理解了关切。

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

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

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

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

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


值得一提的是,使用问题被许多人认为是不好的主意。

  1. 像这个人
  2. 和这个

一些原因:

  1. 幕后发生了一些黑暗魔法 - 关注点是修补include方法,还有一个完整的依赖关系处理系统 - 对于那些旧式的Ruby混合模式来说太复杂了。
  2. 你的班级不会干燥。 如果你在各个模块中填充50个公共方法并包含它们,你的班级仍然有50个公共方法,只是你隐藏了代码的味道,把垃圾放在抽屉里。
  3. Codebase实际上更难以驾驭所有这些问题。
  4. 你确定你的团队的所有成员都有相同的理解什么应该真正替代关注?

担心是简单的方法在腿上射击自己,小心他们。


我一直在阅读关于使用模型问题来皮肤化肥胖模型以及干起你的模型代码。 以下是一些例子的解释:

1)烘干型号代码

考虑文章模型,事件模型和评论模型。 文章或事件有很多评论。 评论属于文章或事件。

传统上,模型可能如下所示:

评论型号:

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都有一个共同的重要代码。 使用关注点,我们可以在一个单独的可评论模块中提取此通用代码。

为此,在app / models /关注点中创建一个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

现在你的模型看起来像这样:

评论型号:

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

有许多关联的模型,否则就会积累越来越多的代码并变得难以管理。 关注提供了一种方法来对脂肪模块进行皮肤润饰,使它们更模块化,易于理解。

上述模型可以使用以下关注点进行重构:在app / models / concerns / event文件夹中创建一个attendable.rbcommentable.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

*在使用问题时,建议采用基于“域”的分组而不是“技术”分组。 基于域的分组类似于“可评论的”,“可照片的”,“可参与的”。 技术分组将意味着'验证方法','FinderMethods'等


我觉得大多数示例都展示了module的强大功能,而不是ActiveSupport::Concern如何为module增加价值。

示例1:更多可读模块。

所以不用担心这个典型的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

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

你可以看到实例方法,类方法和包含的块都不那么杂乱。 担忧会为您适当注入。 这是使用ActiveSupport::Concern一个优势。

示例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

在这个例子中, BarHost真正需要的模块。 但是因为BarFoo有依赖性,所以Host类必须include Foo (但是等到Host想知道Foo原因是否可以避免?)。

所以Bar在任何地方都会增加依赖性。 包含的顺序在这里也很重要。 这给庞大的代码库增加了很多复杂性/依赖性。

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

现在看起来很简单。

如果你想为什么不能在Bar模块本身中添加Foo依赖项? 这不会工作,因为method_injected_by_foo_to_host_klass必须注入类包括Bar不在Bar模块本身。

来源: Rails ActiveSupport ::关注





dci