ActiveSupport源代码研究之 ActiveSupport::Concern

来源:互联网 发布:广州零点 数据分析 编辑:程序博客网 时间:2024/06/05 08:16
ActiveSupport::Concern 用于模块的引用,具体有两个应用:
  1.   简化 self.included 方法
  2.   用于模块之间方法的相互调用

1, 简化 self.included 方法
module M  def self.included(base)    base.class_eval do        def self.method_m         puts "method_m calling----------"      end     end   end end

使用concern的代码
require 'active_support/concern'module M  extend ActiveSupport::Concern  included do    class_eval do           def self.method_m        puts "method_m calling----------"      end    end  endend

2, 有两个module:A 和 B, A定义了一个方法: method_a,  B引用了method_a , 然后  class C 需要应用 B ,该如何做?
module A  def self.included(base)    base.extend ClassMethods  end   module ClassMethods    def method_a       puts "method_a calling -------------"    end   end endmodule B  include A  def self.included(base)    base.method_a  end endclass C  include Bend

如上代码是与错误的,提示如下:
concern2.rb:17:in `included': undefined method `method_a' for C:Class (NoMethodError)
原因是 base 是  class c , 而 C 根本没有定义 method_a , 修改 C 的代码
class C  include A  include Bend

在C中需要 先引入 A,才能引入 B,因为B依赖 A,显然这种方式不是很合适。不应该由调用对象C来关注这些因素。 引入 ActiveSupport::Concern的代码如下:

require 'active_support/concern'module A  extend ActiveSupport::Concern  module ClassMethods    def method_a       puts "method_a calling -------------"    end   end endmodule B  extend ActiveSupport::Concern  include A  included do      self.method_a  endendclass C  include Bend

运行正确!

接着应该分析 concern的源码。。。


module ActiveSupport  module Concern    def self.extended(base)      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.send(: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?        @_included_block = block      else        super      end    end  endend

用test_unit来测试一下吧

require 'test/unit'require 'active_support/concern'class ConcernTest < Test::Unit::TestCase  def test_dependencies    dependencies = Bar.instance_variable_get("@_dependencies")    assert dependencies assert dependencies.is_a?Arrayassert_equal 1, dependencies.sizeassert_equal Foo, dependencies.firstassert A.respond_to? :method_foo  endendmodule Foo  extend ActiveSupport::Concern  included do     def self.method_foo; puts "method_foo"; true;end  endendmodule Bar  extend ::ActiveSupport::Concern  include Foo  included do     self.method_foo  endendclass A  include Barend


原创粉丝点击