首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Active Support 源码研究 - Concern

2012-12-20 
Active Support 源码研究 -- Concern前言?最近都没怎么更新博客,一来没什么时间学习新知识,二来平时积累的

Active Support 源码研究 -- Concern
前言

?

最近都没怎么更新博客,一来没什么时间学习新知识,二来平时积累的感觉还没到质变的程度。既然没时间学一些新东西,就研究一下基础吧。之所以选择ActiveSupport,是因为它是做基础支持工作的,很多都是对Ruby原生对象的hack,对外部的gem依赖较少。我打算挑几个自己感兴趣的模块来分析分析。第一个就是这个Concern模块。

?

虽然Concern只有不到50行代码,也没依赖其他的模块,但还是花了我半天时间才搞清楚它怎么运作的,惭愧……

?

Concern模块是用来解决module和module之间的依赖问题。这里只说大概用法,想进一步了解,请移步 这篇文章。

?

?

ActiveSupport::Concern的作用

?

一般来说,要定义一个模块,为了更好的组织类方法和实例方法,以下这种写法几乎成为一种标准了:

?

?

module M3  def self.append_features(base)    puts "call M3 append_features"    super  end  def m3_instance_method; endendmodule M4  def self.append_features(base)    puts "call M4 append_features"  end  def m4_instance_method; endendclass C  include M3, M4end# 这时会打印# call M4 append_features# call M3 append_featuresc = C.newc.m3_instance_method  # 这个方法成功的混入到C中c.m4_instance_method  # 对象c没有这个方法
?

ActiveSupport::Concern中的append_feature并不是在Concern模块被include进其他模块时调用的(这个模块只会被extend,而且这个append_features并不是类方法),而是对于extend了ActiveSupport::Concern的模块而言(如module M1),当它被混入类C时,会触发append_features方法。

这个方法的作用是,如果base类(或模块)有@_dependencies列表时,将自己记入base的@_dependencies中,然后直接return(就是不混入base)。如果base类没有@_dependencies列表(这种情况可以肯定base就是最终要混入的class),就循环自己的@_dependencies列表,依次把每个依赖的module混入base。

?

第三个方法 included。很简单,如果有block。就把block存进 @_included_block 变量。然后在append_featuers中传给base.class_eval。没有block。就和普通的included回调方法一样。

?

方法都说完了,但还是有点绕。下面说下整体流程,以上面的module M1,module M2,class C为例子:

?

    module M2和module M1都extend了ActiveSupport::Concern。ActiveSupport::Concern的extended方法被调用,为module M2和module M1类加入实例变量@_dependencies。同时修改了它们的append_features和included两个回调方法。module M2被混入module M1,module M2的append_features被调用,base参数为module M1。因为module M1有@_dependencies,module M2并没有真的被混入,只是被加入到module M1的@_dependencies列表中;module M1被混入class C,因为class C没有@_dependencies列表,所以M1遍历自己的@_dependencies列表,将module M2混入class C;此时module M2的append_features会再次被调用,但base变成了class C。所以module M2会先把自己的@_dependencies列表中的module加入到base(其实没有可加的),然后把调用append_features的默认行为,然后执行included中的block。调用base的方法。最后混入ClassMethods和InstanceMethods两个模块(如果有)回到module M1,它处理完@_dependencies后,会调用super方法,执行append_features的默认行为,将自己混入到class C中,然后执行included的block,最后混入ClassMethods和InstanceMethods两个模块。

总结
基本上,ActiveSupport::Concern的思路就是使用append_features回调,去修改module的被include时的默认行为,延迟module被实际include的时机。这个模块中使用了相当一部分元编程方法。有些细节因为比较直观所以没讲,比如用instance_variable_defined?判断@_dependencies变量定义了没有,用instance_variable_get和instance_variable_set来获取和设置实例变量,等等。这段源码中,还有一部分我没讲,就是源码第12行的 if base < self 。这个判断是说,当base是self的子类时,返回true,否则返回nil。我没有想到什么情况会有base是self的子类的,除非自定义一个类继承自module……有兴趣的可以自己推演下。

热点排行