既可以在 model 中使用,也可以在 controller 中使用。
使用方式:
include ConcernName
一、用于把特定功能代码集合封装成一个 concern,提高代码可读性和方便代码重构;
二、把多个 model 或者 多个 controller 重复的功能集合代码封装成一个 concern。
#定义一
module MyConcern
extend ActiveSupport::Concern
...
end
#定义二
concern MyConcern do
...
end
以上两种定义 concern 的方式完全等同
ActiveSupport::Concern 提供了一种约定配置:
约定 ClassMethods 用来给类添加类方法(可选)
module ClassMethods
end
定义 included 作为当当前 module(也就是子 concern) 被 include 的回调方法(可选)
def self.included
end
module FirstConcern
#把ActiveSupport::Concern中的方法 加入到 当前module模块方法(与class类方法原理相同)中
extend ActiveSupport::Concern
def self.included(base)
base.instance_eval do
scope :jief, ->{...}
has_many :table
end
# 或者
#base.class_eval do
# ...
#end
...
end
#included 也可以这样
#included do
# 这里作用域是base
#
# ...
#end
module ClassMethods
#定义类方法
def my_method
end
...
end
#或者
# class_methods do
# ...
# end
end
module Concern
# 负责当定义多个 included 抛出异常
class MultipleIncludedBlocks < StandardError #:nodoc:
def initialize
super "Cannot define multiple 'included' blocks for a Concern"
end
end
# extended 是回调方法,当 当前module(这里指的是ActiveSupport::Concern) 被 extend 的时候执行
# 参数--base是extend当前模块的module或者class
# 用途,在base的作用域初始化@dependencies为空数组 []
#@_dependencies是base的类实例变量(类实例变量只有类可以访问,他的实例无法访问),
#用于记录base中include哪些module,顺序---从上到下,深度--1
def self.extended(base) #:nodoc:
base.instance_variable_set(:@_dependencies, [])
end
#这里的 append_features 是 对 Module.append_features 的扩展
# Module#include 方法最终是 通过 Module.append_features 实现的(参数顺序是相反的)
# append_features 只有在多个concern,include嵌套(即一个concern include 若干个其他的concern)时
#使用此时扩展后的append_features。
# 当子concern被include时 调用append_features(可理解成 include) 和 included,
#append_features 在 included 之前调用
# 参数--base是include当前模块的module或者class
def append_features(base)
if base.instance_variable_defined?(:@_dependencies)
# instance_variable_defined? 用于判断实例变量是否存在。
#如果 base是module,并且 extend ActiveSupport::Concern,
# 也就是当 base是concern(这里称其为父concern)时,
# @_dependencies 就会在上面extended回调方法中初始化
#否则 这里不会执行。
#把当前concern 加入到 base(父concern)的 类实例变量 @_dependencies中
base.instance_variable_get(:@_dependencies) << self
return false
else
# 当base不是concern(base 是 class 或者 是普通 module),才会执行
#如果base 已经继承了self(子concern) 就不再执行,
#这也就是 多次include 同一个module时,实际上只执行了第一次的原因
return false if base < self
#遍历子concern 类实例变量 @_dependencies,base include 每一个子concern include的module
@_dependencies.each { |dep| base.include(dep) }
super #执行祖先链后面按序逐个查找并执行 append_features 方法
#如果子concern 定义了 module ClassMethods,
# base 会 extend 该module-ClassMethods
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
#在base作用域中执行 子concern中 included 方法的内容(Proc实例)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
#这里扩展了 Module#included 方法
def included(base = nil, &block)
if base.nil?
#这里的处理 实现了 included do ... end
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
# 通过实例变量@_included_block,实现了 included 传入的块(这里变成了Proc对象) 共享,
# 即append_features能够访问到
@_included_block = block
else
super
end
end
#这里实现了 class_methods do ... end
def class_methods(&class_methods_module_definition)
# mod 判断子concern中是否定义了 module ClassMethods,没有就用Module.new 初始化
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
# mod 把 传入的块(这里变成了Proc对象) 添加到 mod 中
mod.module_eval(&class_methods_module_definition)
end
end