# 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
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
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
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
and this one 一些原因: 1.在幕后有一些黑暗的魔法发生--关注的是修补include方法,有一个完整的依赖处理系统--对于一些微不足道的老Ruby混合模式来说,太复杂了。 1.你的课也没那么枯燥。如果你把50个公共方法塞到不同的模块中并包含它们,你的类仍然有50个公共方法,只是你隐藏了代码的味道,有点像把垃圾放在抽屉里。 1.代码库实际上很难在所有这些关注的情况下导航。 1.你确定你的团队所有成员都有相同的理解,什么是真正应该替代的关注? 担忧是射在自己腿上的简单方法,小心它们。
# app/models/trader.rb
class Trader
include Shared::Schedule
end
# app/models/concerns/shared/schedule.rb
module Shared::Schedule
extend ActiveSupport::Concern
...
end
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
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
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
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
7条答案
按热度按时间kyxcudwk1#
所以我自己发现的。这实际上是一个非常简单但强大的概念。它与代码重用有关,如下面的示例所示。基本上,这个想法是提取公共的和/或特定于上下文的代码块,以清理模型,避免它们变得过于臃肿和混乱。
作为一个例子,我将把一个众所周知的模式,taggable模式:
因此,按照Product示例,您可以将Taggable添加到您想要的任何类并共享其功能。
DHH解释得很好:
在Rails 4中,我们将邀请程序员使用默认的app/models/concerns和app/controllers/concerns目录中的concerns,它们自动成为加载路径的一部分。再加上ActiveSupport::Concern Package 器,它提供的支持足以让这种轻量级的分解机制大放异彩。
bfnvny8b2#
我一直在阅读关于使用模型关注点来皮肤化脂肪模型以及干燥你的模型代码。下面是一个解释与例子:
1)DRing up model code
考虑一个Article模型、一个Event模型和一个Comment模型。一篇文章或一个事件有许多评论。评论属于文章或事件。
传统上,模型可能看起来像这样:
评论型号:
产品型号:
事件模型
正如我们所注意到的,Event和Article都有一段重要的代码。使用关注点,我们可以在一个单独的模块Commentable中提取这个公共代码。
为此,在app/models/concerns中创建一个可注解的.rb文件。
现在你的模型看起来像这样:
评论型号:
产品型号:
事件模型:
2)减脂模型。
考虑一个事件模型。一个事件有许多参与者和评论。
通常,事件模型可能如下所示
具有许多关联和其他特性的模型往往会积累越来越多的代码并变得难以管理。关注点提供了一种对脂肪模块进行皮肤化的方法,使它们更加模块化和易于理解。
上述模型可以使用以下关注点进行重构:在app/models/concerns/event文件夹中创建
attendable.rb
和commentable.rb
文件attendable.rb
commentable.rb
现在使用关注点,您的事件模型简化为
mum43rcc3#
值得一提的是,许多人认为使用关注点是个坏主意。
一些原因:
1.在幕后有一些黑暗的魔法发生--关注的是修补
include
方法,有一个完整的依赖处理系统--对于一些微不足道的老Ruby混合模式来说,太复杂了。1.你的课也没那么枯燥。如果你把50个公共方法塞到不同的模块中并包含它们,你的类仍然有50个公共方法,只是你隐藏了代码的味道,有点像把垃圾放在抽屉里。
1.代码库实际上很难在所有这些关注的情况下导航。
1.你确定你的团队所有成员都有相同的理解,什么是真正应该替代的关注?
担忧是射在自己腿上的简单方法,小心它们。
xfb7svmp4#
This post帮助我理解了问题。
k75qkfdt5#
我觉得这里的大多数例子都展示了
module
的强大功能,而不是ActiveSupport::Concern
如何为module
增加价值。**示例1:**更多可读模块。
所以不用担心这是一个典型的
module
。使用
ActiveSupport::Concern
重构后。你可以看到示例方法,类方法和包含块不那么混乱。关注会为你注入适当的。这是使用
ActiveSupport::Concern
的一个优点。**示例2:**优雅地处理模块依赖。
在这个例子中,
Bar
是Host
真正需要的模块。但是,由于Bar
与Foo
有依赖关系,Host
类必须include Foo
(但是等等,为什么Host
想知道Foo
?它能避免吗?).所以
Bar
在任何地方都增加了依赖性。而且**包含的顺序在这里也很重要。**这给巨大的代码库增加了很多复杂性/依赖性。使用
ActiveSupport::Concern
重构后现在看起来很简单。
如果你在想为什么我们不能在
Bar
模块本身中添加Foo
依赖?这是行不通的,因为method_injected_by_foo_to_host_klass
必须注入到包含Bar
的类中,而不是Bar
模块本身。来源:Rails ActiveSupport::Concern
yjghlzjz6#
在关注使文件文件名.rb
例如,我想在我的应用程序中,属性create_by存在时,将其值更新为1,将updated_by值更新为0
如果你想在实际中传递参数
然后在你的模型中加入这样的内容:
gpfsuwkq7#
在Rails 4中,关注点用于组织跨模型或控制器的共享代码。它们有助于保持代码的组织性并防止重复。以下是如何使用关注点:
创建关注点:
导航到app/models/concerns(对于模型)或app/controllers/concerns(对于控制器)。创建一个以_concern.rb结尾的新Ruby文件(例如,timestampable_concern.rb)。将关注点定义为一个模块并使用extend ActiveSupport::Concern。在模块内部,定义要共享的方法或行为。
使用关注点:
在你想要使用关注点的模型/控制器中,使用include语句包含它。包含的方法在模型/控制器中可用。您可以通过链接include语句来包含多个关注点。
注意事项:
为清晰起见,在关注点的名称中加上关注点后缀。将共享的关注点放在适当的关注点目录中。避免对单个模型/控制器的过度关注。Rails 4自动加载关注点,因此不需要显式的require。
从这里详细阅读Rails 4关注点:-Rails Concern 4