作用:解决了rails中module之间的混入问题,参考如下代码示例
#代码示例
module SecondLevelModule
def self.included(base); base.extend ClassMethods; end
def second_level_instance_method; 'ok'; end
module ClassMethods
def second_level_class_method; 'ok'; end
end
end
module FirstLevelModule
def self.included(base); base.extend ClassMethods; end
def first_level_instance_method; 'ok'; end
module ClassMethods
def first_level_class_method; 'ok'; end
end
include SecondLevelModule
end
class BaseClass
include FirstLevelModule
end
上面的代码中,示例方法调用没有问题,FirstLevelModule中的类方法调用也没有问题,SecondLevelModule中的类方法不能被调用,这就是module的混入问题。
p BaseClass.new.first_level_instance_method #ok
p BaseClass.new.second_level_instance_method #ok
p BaseClass.first_level_class_method #ok
p BaseClass.second_level_class_method #error
Rails2中的解决方式
#只是修改included中方法
module FirstLevelModule
def self.included(base)
base.extend ClassMethods
base.send :include, SecondLevelModule
end
end
ActiveSupport::Concern的使用
require 'active_support'
module SecondLevelModule
extend ActiveSupport::Concern #add
def second_level_instance_method; 'ok'; end
module ClassMethods
def second_level_class_method; 'ok'; end
end
end
module FirstLevelModule
extend ActiveSupport::Concern #add
def first_level_instance_method; 'ok'; end
module ClassMethods
def first_level_class_method; 'ok'; end
end
include SecondLevelModule
end
class BaseClass
include FirstLevelModule
end
append_features的作用原理:是include的hook方法
#先于included方法执行,included方法默认是空的,append_features会检查模块是否包含在祖先链中,如果没有,添加进去
module M
def self.append_features(base)
return false
end
end
class Demo
include M
end
#改了append_features方法,是它不在祖先链中
p Demo.ancestors #[Demo, Object, PP::ObjectMixin, Kernel, BasicObject]
#如果要改写的同时要添加到祖先链中,需要在append_features中添加super方法
module M
def self.append_features(base)
super
end
end
#查看self和base
module M
def self.append_features(base)
p "self is #{self}"
p "base is #{base}"
end
end
class Demo
include M
end
#"self is M"
#"base is Demo"
ActiveSupport::Concern的源代码
module ActiveSupport
module Concern
class MultipleIncludedBlocks < StandardError #:nodoc:
def initialize
super "Cannot define multiple 'included' blocks for a Concern"
end
end
def self.extended(base) #:nodoc:
base.instance_variable_set(:@_dependencies, [])
end
def append_features(base)
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
return false
else
return false if base < self
@_dependencies.each { |dep| base.include(dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
def included(base = nil, &block)
if base.nil?
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
@_included_block = block
else
super
end
end
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
mod.module_eval(&class_methods_module_definition)
end
end
end
单单只是extend ActiveSupport::Concern
#only extend ActiveSupport::Concern
module SecondLevelModule
extend ActiveSupport::Concern #add
def second_level_instance_method; 'ok'; end
module ClassMethods
def second_level_class_method; 'ok'; end
end
end
#只执行下面 的代码,其中base是SecondLevelModule,这个模块设置实例变量
def self.extended(base) #:nodoc:
base.instance_variable_set(:@_dependencies, [])
end
#result, SecondLevelModule中包含实例变量@_dependencies,变量值为[]
<#SecondLevelModule, @_dependencies: []>
extend ActiveSupport::Concern的同时include SecondLevelModule
#extend ActiveSupport::Concern and include SecondLevelModule
module FirstLevelModule
extend ActiveSupport::Concern #add
def first_level_instance_method; 'ok'; end
module ClassMethods
def first_level_class_method; 'ok'; end
end
include SecondLevelModule
end
#执行下面的代码
def self.extended(base)
base.instance_variable_set(:@_dependencies, [])
end #result,FirstLevelModule中添加实例变量@_dependencies,将其设值为[]
def append_features(base) #这里的base还是FirstLevelModule
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self #这里是self是SecondLevelModule
return false
else
return false if base < self
@_dependencies.each { |dep| base.include(dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
#result
1.FirstLevelModule中设置实例变量,而且这个实例变量包含SecondLevelModule
<#FirstLevelModule, @_dependencies: [SecondLevelModule]>
2.return false表示不把SecondLevelModule添加到ancestors中
只是include FirstLevelModule
#only inclue no active_support::concern
class BaseClass
include FirstLevelModule
end
#只执行下面
def append_features(base) #这里的base是BaseClass
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self #这里是self是SecondLevelModule
return false
else
#base < self是用来判断FirstLevelModule是否在BaseClass的祖先链中
return false if base < self #这里的self是FirstLevelModule
#@_dependencies是self即FirstLevelModule的实例变量
@_dependencies.each { |dep| base.include(dep) }
super #调用super方法,实现默认的append_features方法
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
总结:一般在插件中使用ActiveSupport::Concern,使用规则一般如下:
module SecondLevelModule
extend ActiveSupport::Concern #需要extend
included do #这里放实例方法
def second_level_instance_method; 'ok'; end
end
module ClassMethods #这里面放类方法
def second_level_class_method; 'ok'; end
end
end
class Demo
#进行include使用
include SecondLevelModule
end