ActiveSupport::Concern的作用和实现原理

作用:解决了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

results matching ""

    No results matching ""