在我们平常看一些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=:Attribute
path = @_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 = nil
const_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的约束,人家当然要重写了。