ActiveSupport autoload源码分析

来源:互联网 发布:北平无战事知乎 编辑:程序博客网 时间:2024/05/29 18:36

在我们平常看一些gem或者开发中autoload用的还是很多的,闲的没事总结下这个方法的内部实现,分析一下源码。

在我们看源码的时候经常看到如下代码:

module ActiveRecord  extend ActiveSupport::Autoload  autoload :Attribute  autoload :Base  autoload :Callbacks  autoload :Core  autoload :ConnectionHandling  autoload :CounterCache  autoload :DynamicMatchers  autoload :Enum  autoload :InternalMetadata  autoload :Explain  autoload :Inheritance  autoload :Integration  autoload :Migration

那么在这里的autoload到底做了什么事??

看源码:

   module Autoload    def self.extended(base) # :nodoc:      base.class_eval do        @_autoloads = {}        @_under_path = nil        @_at_path = nil        @_eager_autoload = false      end    end    def autoload(const_name, path = @_at_path)      unless path        full = [name, @_under_path, const_name.to_s].compact.join("::")        path = Inflector.underscore(full)      end      if @_eager_autoload        @_autoloads[const_name] = path      end      super const_name, path    end

我们来分析下这个方法取上面的ActiveRecord来分析:

第一步:

extend ActiveSupport::Autoload

这里相当把ActiveSupport::Autoload的方法引入进来了,相当于类方法,具体细节看extend的用法,不多说,好,继续向下看。

第二步:

autoload :Attribute

这里相当于调用了

ActiveRecord.autoload(:Attribute)

再来细看这个方法,这个方法接入了两个参数通过上面分析我们知道:

const_name=:Attributepath = @_at_path = nil

这里的@_at_path是extend钩子方法extended调用初始化的值,继续向下看:

 unless path        full = [name, @_under_path, const_name.to_s].compact.join("::")        path = Inflector.underscore(full)      end

path=nil这个方法会执行,我们先来确认几个值。

name = self.name = ActiveRecord.name = "ActiveRecord"@_under_path = nilconst_name.to_s = :Attribute.to_s = "Attribute"

好了我么知道这几个值那就简单了,把这几个值带到full里面执行下

full =  ["ActiveRecord", nil, "Attribute"].compact.join("::") = "ActiveRecord::Attribute"

继续向下看:

path = Inflector.underscore(full)

其实是调用了Inflector里面的underscore方法只是把full传进来了,看源码:

    def underscore(camel_cased_word)      return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/      word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)      word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }      word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)      word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)      word.tr!("-".freeze, "_".freeze)      word.downcase!      word    end

这个方法很长,不具体分析了,其实这个方法具体就做了一件事,就是把我们的full转化为我们需要的路径。
上面的

full = "ActiveRecord::Attribute"

经过这个方法转化后

"active_record/attribute"

继续向下看:

  if @_eager_autoload        @_autoloads[const_name] = path      end      super const_name, path  end

@_eager_autoload = nil 所以最后只执行了:

super const_name, path

相当于:

super :Attribute, ""active_record/attribute""

其实分析到这里已经结束了,这里的super相当于调用了kernel下的autoload方法,这个是ruby的方法,执行这个方法成功返回nil,失败异常,简单提一下,其实就是懒加载,调用这个方法其实并没有require,只是把路径加到内存中了,当我们调用的时候回才去加载。

下面还需要提出一个小问题:为什么rails重写了ruby的这个方法而不是直接调用?

其实这个方法的目的就要还是为了Rails的约束,比如
A::B => rails 当然根据这个名字找到a/b文件,所以重写了这个方法
但是ruby不是这样的,没有这个规范,只要有路径就能找到,不符合rails的约束,人家当然要重写了。

原创粉丝点击