Ruby require、load 加载机制

LongLonghaoran · 2021年06月01日 · 229 次阅读

require 会按照 $LOAD_PATH 中的路径来加载,但是 gems 路径并不在 $LOAD_PATH 中,为什么也可以加载的到呢?

load 方法:

  • load 方法用于根据相对路径、绝对路径、LOAD_PATH 来加载 ruby 文件,ruby 文件中的方法、类、常量都会导入,但是局部变量不会导入

  • 特点:多次 load 同一个文件,会加载多次,也就是会多次执行被 load 的文件内的代码

$LOAD_PATH:

  • $LOAD_PATH 中存储了一组目录的绝对路径,对于 load 和 require 方法来说,如果方法参数只是一个不带路径的文件名的话,这时候就会去 $LOAD_PATH 中定义的路径中去查找

默认的 require:

  • 与 load 最大的不同是不会加载同一个文件两次,多次 require 同一个文件只会加载一次,所有被加载过的文件,都会记录在一个全局变量 $LOADED_FEATURES 中,不论是 load 还是 require,都无法在不加路径(相对路径,绝对路径)的情况下直接加载文件,因为当前路径不在 $LOAD_PATH 中

rubygems 中的 require, gems 是如何加载的呢?

  • rubygems 是安装 ruby 是会默认一同安装的库,并且在启动 ruby 时会默认加载,比如启动 irb 之后,通过查看 $LOADED_FEATURES 就会发现,已经有很多.rb 文件被加载进来了,ruby 默认的 Kernel#require 这个方法已经被 rubygems 重写了

  • 打开该文件会发现,该文件正是重写了默认的 Kernel#require 方法:

  • 这个重写的版本最大的特点,就是可以用来加载已经安装好的 gem,加载的流程大致是这样的:

    • 当 require(‘xx’) 的时候,先从 $LOAD_PATH 中查找,是否能从 $LOAD_PATH 中定义的路径中直接找到同名文件,如果找到的话,那么就按照默认情况直接加载
    • 如果没有找到,那么将会安装 gems 的目录查找,找到之后,activating(激活),将该 gem 所在目录 (比如 rails-6.0.3.6) 添加到 $LOAD_PATH,这个 gem 中的 gemspec 还会声明该 gem 需要 require 的其他路径,按照惯例,一般都只会添加 lib 这个目录
    • 在 $LOAD_PATH 被添加好之后,再次执行来查找 xx 这个文件,这次由于 gem 所在目录的 lib 也被添加到 $LOAD_PATH,所以这次能够加载到了
  • 一般来说 xx.rb,这个与 gem 包同名的 rb 文件中还会继续 require 该 gem 所需依赖的其他文件,随便查看一个 gem 看下,比如 tzinfo 这个 gem:

  • 这些 require 的文件全部都在该 gem 的 lib 目录下,所以都能够正常加载,以这个 tzinfo 为例,再 require(‘tzinfo’) 之后,$LOADED_FEATURES 自然就多了一大堆已加载的文件列表:

Kernel#gem 方法:

  • 如果同时安装了一个 gem 的多个版本,比如 rails-6.0.3.7 和 rails-6.0.3.6,那么通过 gem 方法,可以将指定的 gem 的路径存入 $LOAD_PATH,这样子之后在 require 该 gem 的时候,也就能按照指定的版本来加载了,上面的 Kernel#require 方法实际也是使用了 gem 方法,来将 gem 的路径放入 $LOAD_PATH

rails 中加载 gems:

  • 在 rails 的初始化加载/启动加载文件中 (比如 config/boot.rb),会加载 bundler,我这里看到的是加载了 bundler/setup 这个文件,bundler/setup 的目的就是用来加载 Gemfile 中列出的 gem,执行流程大致是:
    • 1.读取 Gemfile.lock 文件,根据 Gemfile.lock 文件中 spec 下的描述,获取该项目所需加载的 gem 的名称以及版本号
    • 2.调用 gem 方法,来激活该 gem,也就是将 gem 的相关路径加入到 $LOAD_PATH
    • 3.最后在 application.rb 中,调用 Bundler.require,这个时候由于 $LOAD_PATH 中已经存入了定义好的目录,所以 require 可以顺利的加载
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号