Ruby 如何单独使用 active_support 中的 autoload

zeekerpro · 2019年05月10日 · 最后由 kayakjiang 回复于 2019年05月12日 · 3983 次阅读

查看了相关 active_support 关于 autoload 的相关解释和源码,然后自己试着用一下这个方法:

文件目录结构如下

demo/
  | - main/
  |      | - base.rb
  |      | - scoping.rb
  | - main.rb
  | - run.rb

代码如下:

#main.rb
require 'active_support/all'

ActiveSupport::Dependencies.autoload_paths.push('.', 'main')

module Main
  extend ActiveSupport::Autoload

  autoload :Base
  autoload :Scoping

end
#main/base.rb
module Main
  class Base
    include Scoping
  end
end
# main/scoping.rb
module Main
  module Scoping
    def hello
      "hello world"
    end
  end
end
# run.rb
require 'main'

b = Main::Base.new
p b.hello

当我在运行 ruby run.rb 时报错,提示信息为:

Traceback (most recent call last):
    7: from run.rb:1:in `<main>'
    6: from run.rb:1:in `require_relative'
    5: from  main.rb:19:in `<top (required)>'
    4: from /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require'
    3: from /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency'
    2: from /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `block in require'
    1: from /usr/local/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/usr/local/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- main/base (LoadError)

我的理解是在 main.rb 中使用 ActiveSupport::Dependencies.autoload_paths.push('.', 'main') 将目录添加至 autoload_path 中,则使用 autoload 方法后,会在需要时自动加载 base.rb 和 scoping.rb。但是实际却加载失败了,请问是我理解错误,还是代码表达的时候写错了。该如何修正?

试试@kayakjiang,解惑答疑时间

zeekerpro 回复

我上传了一个 demo: https://github.com/baya/autoload_demo,可以运行,在 run.rb 顶部加了一行代码:$:.unshift File.dirname(__FILE__)

楼主对 autoload 的理解是对的

autoload :Base 

会在引用 Base 常量,按需执行

require('main/base')

但 require 查找文件是根据$LOAD_PATH来找的,参考 wiki https://ruby-china.org/topics/28453

楼上 $:就是$LOAD_PATH简写

4 楼 已删除

我补充一点:ActiveSupport::Dependencies.autoload_paths.push('.', 'main') 这段代码其实可以不要的, autoload 会将模块名自动 map 到对应的路径,比如 Main::Base 会 map 到 main/base,但是要想找到 main/base 就必须把 demo 的当前目录放到 $LOAD_PATH 里

kayakjiang 回复

@kayakjiang 感谢你的回答,设置了$LOAD_PATH 运行成功了,现在还有一点疑惑的地方就是原先我以为 ActiveSupport::Dependencies.autoload_paths 会自动维护$LOAD_PATH,目前来看我的理解还是有偏差,所以 autoload_paths 和 $LOAD_PATH 之间是什么关系,各自负责的内容是什么,按照上面的解决方式,其实 autoload_paths 这个数组好像没有什么作用。

zhuoerri 回复

感谢你的传送门,学了一遍历史,对 bundle 的认识更丰满了,哈哈

zeekerpro 回复

如果模块的命名无法 map 到对应的 path, 就需要设置 autoload_paths, 比如在 rails 的 lib 目录下写了几个 class: A, B 如果要 autoload A 和 B, 就需要:

config.autoload_paths << "#{Rails.root}/lib"
需要 登录 后方可回复, 如果你还没有账号请 注册新账号