缺省的Rails 4项目生成器现在在控制器和模型下创建目录“关注点”。我找到了一些关于如何使用路由关注点的解释,但没有关于控制器或模型的解释。
我很确定这与当前社区中的“DCI趋势”有关,我想尝试一下。
问题是,我应该如何使用这个功能,为了使它工作,如何定义命名/类层次结构是否有一个约定?如何在模型或控制器中包含关注点?
缺省的Rails 4项目生成器现在在控制器和模型下创建目录“关注点”。我找到了一些关于如何使用路由关注点的解释,但没有关于控制器或模型的解释。
我很确定这与当前社区中的“DCI趋势”有关,我想尝试一下。
问题是,我应该如何使用这个功能,为了使它工作,如何定义命名/类层次结构是否有一个约定?如何在模型或控制器中包含关注点?
当前回答
我一直在阅读关于使用模型关注皮肤脂肪模型以及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”等
其他回答
在关注点中,创建文件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
我一直在阅读关于使用模型关注皮肤脂肪模型以及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目录 自动成为加载路径的一部分。连同 关注点包装器,它的支持足以做到这一点 轻量级保理机制大放异彩。
我觉得这里的大多数示例都展示了模块的强大功能,而不是ActiveSupport::Concern如何为模块增加价值。
例1:更多可读模块。
所以不用担心这是一个典型的模块。
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
在本例中,Bar是Host真正需要的模块。但是由于Bar与Foo有依赖关系,主机类必须包括Foo(但是等等为什么主机想知道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::Concern