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为例子:
?