Ruby 中的 require 和 require_relative 方法

yufei       5 年, 8 月 前       2364

周六的下雨天,实在无聊,那么我们就来聊聊 Ruby 是如何加载 ( require ) 一个文件或模块吧。本章节,我们将涉及到以下内容

  1. 绝对路径和相对路径的不同点。
  2. Kernel#require 方法
  3. Kernel#require_relative 方法

绝对路径与相对路径

绝对路径是指向同一位置的完整路径,与当前目录的位置无关

$> tree
.
├── file1.txt
├── directory/
│   └── file2.txt
├── another_directory/
│   └── file3.txt
2 directories, 3 files
$> pwd
/Users/yufei/path

顺着上面的示例,让我们尝试使用绝对路径输出 file3.txt 文件的内容

$> cat /Users/yufei/path/another_directory/file3.txt
I'm the file #3
$> cd directory && pwd
/Users/mehdi/path/directory
$> cat /Users/yufei/path/another_directory/file3.txt
I'm the file #3

上面的代码中

  1. 首先,我们使用 cat 命名输出 file3.txt 的内容使用的就是绝对路径
  2. 接下来,我们移动到 directory 目录
  3. 然后我们可以看到,使用绝对路径调用 cat 仍然可以显示文件的内容,而不管当前目录的位置

相反,相对路径从当前工作目录开始

$> cat another_directory/file3.txt
I'm the file #3
$> cd directory && pwd
/Users/mehdi/path/directory
$> cat ../another_directory/file3.txt
I'm the file #3

上面的代码中

  1. 首先,我们的 cat 命令使用相对路径输出 file3.txt 文件的内容
  2. 接着,我们移动到 directory 目录
  3. 如果我们想要用相对路径在此输出 file3.txt 文件的内容,因为 file3.txt 是在当前目录的同级目录 another_directory 下,也就是当前目录的父目录下的 another_directory 目录下,这个目录相对与当前目录来说,就是 ../another_directory

一个 ../ 表示一个父级目录,那么两个 ../../ 则表示当前目录的父目录的父目录。

当前目录也可以用 ./ 来表示

因此,上面的 ../another_directory/file3.txt 文件也可以表示为 ./../another_directory/file3.txt

require 方法

Kernel#require 方法用于加载文件或某个模块。参数即是要加载的文件路径或模块路径。

该方法期望传递的文件路径或模块路径为一个 绝对路径,否则,该方法会尝试把 $LOAD_PATH 全局变量中包含的每个路径作为前缀添加到传递的参数前面,转换为一个绝对路径,然后再查找文件或库

当然了,如果传递的文件或模块路径中以 ./ 开始,就表示参数路径基于当前的工作目录,那么就会基于当前的工作目录将传递的参数转换为绝对路径。

一旦文件或模块加载成功,那么文件或模块的绝对路径就会被添加到 $LOADED_FEATURES 全局数组中,以防止重复的加载。

$LOADED_FEATURES 是用来防止重复加载的,如果检查到要加载的文件或模块已经加载过,那么 require 方法则不会再次加载,而是直接返回 false

文字和机制都很简单,我们用一个小范例来理解下这个 require 机制

假设当前目录下存在一个文件 hello.rb

$> tree
.
├── hello.rb
0 directories, 1 file

然后呢,我们在当前目录下打开 shell 并输入 irb 命令启动 Ruby 交互式命令行工具

然后在 irb 中尝试加载当前 hello.rb 文件

irb> require './hello.rb'
hello!
 => true
irb> $LOADED_FEATURES.grep /hello.rb/
 => ["/Users/yufei/require/hello.rb"]
irb> require './hello.rb'
 => false

上面的代码中,我们给 require 方法传递的参数是 ./hello.rb。因为该参数以 ./ 开头,所以模块文件是基于当前目录的。那么 require 就会基于当前工作目录创建一个绝对路径 /Users/yufei/require/hello.rb

当加载成功后,require 就会把这个绝对路径添加到 $LOADED_FEATURES 数组中。

最后,因为计算得出 ./hello.rb 文件已经加载了,也就是判断绝对路径 /Users/yufei/require/hello.rb 已经在 $LOADED_FEATURES 数组中存在,所以再次加载 ./hello.rb 返回的结果就是 false

示例 2 :require 'rake'

我们已经熟悉了如何加载当前目录下的文件或模块,现在我们来看看 require 是如何加载其它目录下的模块的,比如 rake

irb> require 'rake'
 => true
irb> $LOADED_FEATURES.grep /rake/
 => [
     ...,
     "/Users/yufei/.rvm/2.5.0/gems/2.5.0/gems/rake-12.3/lib/rake.rb"
    ]
irb> require 'rake'
 => false

上面的代码中,我们将 rake 模块名作为参数传递给 require 方法,因为参数既不是绝对路径,也不是以 ./ 开始的相对路径,因此 require 会遍历全局变量 $LOAD_PATH 中的每个绝对路径,看看要加载的模块名是不是这些路径名下的子目录。

如果检测到某个绝对路径下存在 rake 目录,就会尝试加载这个模块,加载成功后就会把模块的绝对路径添加到 $LOADED_FEATURES 全局数组中。

当再次加载 rank 时,因为判断出绝对路径已经存在,所以就会直接返回 false 而不会再次加载

require_relative 方法

require 方法一样,require_relative 方法也用于加载一个文件或模块。

只不过 require_relative 并不是采用绝对路径加载,而采用的是相对于当前 require_relative 调用所在的文件路径来加载

这和 require 传递相对路径是不同的:

  • 当给 require 传递相对路径时,要么相对的是 $LOAD_PATH 里的每个绝对路径,那么相对的是当前的工作目录,也就是程序运行的目录

  • 而给 require_relative 传递相对路径时,相对的是 require_relative 所在的文件目录。

同样的,require_relative 也会在内部将相对路径转换为绝对路径,并在加载成功后把绝对路径添加到 $LOADED_FEATURES 全局数组中。

我们使用一个示例,演示如何使用 Kernel#require_relative 方法来获取 hello.rb 文件

irb> require_relative 'hello.rb'
hello
 => true
irb> $LOADED_FEATURES.grep /hello.rb/
 => ["/Users/mehdi/require/hello.rb"]
irb> require_relative 'hello.rb'
 => false

在 irb 中使用 require_relative 方法,因为 irb 中所有的全局代码都运行在 main 对象中,而 main 对象是基于当前工作目录的,所以,require_relative 方法就是基于当前工作目录加载 hello.rb

其它代码就和 require 示例中的一样,我们就不做过多介绍了

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.