在 rails 的很多模塊定義里經(jīng)呈睿看到這樣的代碼:
module Rails
extend ActiveSupport::Autoload
autoload :FooBar
看名稱就知道它與自動(dòng)加載有關(guān)纸颜。實(shí)際上 ruby 的Kernel
模塊里已經(jīng)定義了一個(gè)這樣的方法,autoload(module, filename)
這個(gè)方法的作用是當(dāng)?shù)谝淮斡龅街付K時(shí)因篇,自動(dòng)按照指定的路徑加載戳晌。
為什么 rails 還要再定義一個(gè)這樣的方法呢鲫尊?原因是為了方便。rails 有大量的模塊分散在不同的文件里沦偎,并且這些文件按一定的規(guī)則組織在一起疫向。ActiveSupport::Autoload
模塊中的autoload
能根據(jù)模塊名稱和約定的規(guī)則生成文件路徑加載模塊,不用每次都指定路徑豪嚎。除此之外搔驼,該模塊還定義了 eager_load! 方法,用于提前加載指定的一組模塊侈询。
其用法如下:
module MyLib
extend ActiveSupport::Autoload
autoload :Model
eager_autoload do
autoload :Cache
end
end
MyLib.eager_load!
ActiveSupport::Autoload
的源代碼文件在
~/.rvm/gems/ruby-2.4.0/gems activesupport-5.0.1/lib/active_support/dependencies/autoload.rb
這是我電腦上的路徑舌涨,根據(jù)不同的安裝環(huán)境可能會(huì)有不同。這個(gè)模塊的定義很簡(jiǎn)單扔字,只有70多行囊嘉,下面對(duì)其主要的方法進(jìn)行分析。
module Autoload
# 當(dāng)宿主模塊 extend ActiveSupport::Autoload 時(shí)革为,在宿主模塊的上下文中初始化幾個(gè)變量扭粱。
def self.extended(base) # :nodoc:
base.class_eval do
@_autoloads = {} # 保存 eager_load! 所需加載的模塊
@_under_path = nil # 保存中間路徑
@_at_path = nil # 保存準(zhǔn)確路徑
@_eager_autoload = false
end
end
# 該模塊的核心方法,主要作用是生成路徑震檩,并調(diào)用 ruby 自帶的 autoload琢蛤。
def autoload(const_name, path = @_at_path)
unless path
# 路徑生成規(guī)則,name 為宿主模塊的類方法抛虏,返回宿主模塊名稱博其。
# 例如在Rails模塊里 autoload :Foo,將返回路徑 Rails/Foo迂猴。
# @_under_path 默認(rèn)是 nil贺奠,可通過 autoload_under(path)更改。
full = [name, @_under_path, const_name.to_s].compact.join("::")
# 將形如 Rails::Foo::Bar 改為 Rails/Foo/Bar
path = Inflector.underscore(full)
end
# 為 true 時(shí)错忱,模塊保存到 @_autoloads儡率,從而可通過 eager_load! 提前加載
if @_eager_autoload
@_autoloads[const_name] = path
end
# 生成路徑后,調(diào)用 ruby 自帶的 autoload 方法
super const_name, path
end
# 增加中間路徑
def autoload_under(path)
@_under_path, old_path = path, @_under_path
yield
ensure
@_under_path = old_path
end
# 將模塊加入 @_autoloads以清,為 eager_load! 作準(zhǔn)備
def eager_autoload
old_eager, @_eager_autoload = @_eager_autoload, true
yield
ensure
@_eager_autoload = old_eager
end
# 依次加載 @_autoloads 中的模塊
def eager_load!
@_autoloads.each_value { |file| require file }
end
......
小結(jié)
ActiveSupport::Autoload
按照 Rails 的約定規(guī)則生成路徑自動(dòng)加載模塊儿普。改寫了 autoload
方法來自動(dòng)加載,提供eager_load!
方法來提前加載掷倔。生成路徑的規(guī)則為當(dāng)前模塊名/加載模塊名
眉孩,如有中間路徑,可通過autoload_under
插入勒葱。