【发布时间】:2011-03-11 09:11:02
【问题描述】:
Ruby on Rails 应用程序中load 和require 之间有什么主要区别吗?还是它们都具有相同的功能?
【问题讨论】:
标签: ruby-on-rails ruby
Ruby on Rails 应用程序中load 和require 之间有什么主要区别吗?还是它们都具有相同的功能?
【问题讨论】:
标签: ruby-on-rails ruby
require 在所有定义的搜索路径中搜索库并附加
.rb 或 .so 到您输入的文件名。它还确保只有一个库
包括一次。因此,如果您的应用程序需要库 A 和 B,而库 B 也需要库 A,则 A 只会加载一次。
使用load,您需要添加库的全名,并且每次您都会加载它
调用load - 即使它已经在内存中。
【讨论】:
require 跟踪已通过 $LOADED_FEATURES ($") 全局数组加载的内容,load 会忽略该数组。
Kernel#require 和Kernel#load 之间的另一个区别是Kernel#load 采用可选的第二个参数,允许您将加载的代码包装到一个匿名的空模块中。
不幸的是,它不是很有用。首先,loaded 代码很容易脱离模块,只需访问全局命名空间,即它们仍然可以猴子补丁类似class ::String; def foo; end end。其次,load 不会返回将代码包装到其中的模块,因此您基本上必须手动将其从ObjectSpace::each_object(Module) 中捞出。
【讨论】:
Kernel#load有另一个论点
我正在运行一个 Rails 应用程序,在 Gemfile 中,我使用“require: false”选项创建了一个特定的自定义 gem。现在,当我加载 rails 服务器或 rails 控制台时,我能够在初始化程序中要求 gem 并且加载 gem。但是,当我使用 rspec 和 capybara 运行规范功能测试时,出现加载错误。我完全困惑为什么在运行测试时没有在 $LOAD_PATH 中找到 Gem。
所以我回顾了加载、要求、rubygems 和捆绑程序交互的所有不同方式。这些是我发现的总结,帮助我找到了解决我的特定问题的方法:
加载
1) 您可以将绝对路径传递给 ruby 文件,它将执行该文件中的代码。
load('/Users/myuser/foo.rb')
2) 你可以传递一个相对路径来加载。如果您与文件在同一目录中,它将找到它:
> load('./foo.rb')
foo.rb loaded!
=> true
但是如果你尝试使用 load() 从不同的目录加载文件,它将找不到基于当前工作目录的相对路径(例如 ./):
> load('./foo.rb')
LoadError: cannot load such file -- foo.rb
3) 如上所示,load 始终返回 true(如果无法加载文件,则会引发 LoadError)。
4) 全局变量、类、常量和方法都是导入的,局部变量不导入。
5) 对同一个文件调用两次 load 将执行该文件中的代码两次。如果指定的文件定义了一个常量,它将定义该常量两次,这会产生一个警告。
6) $LOAD_PATH 是一个绝对路径数组。如果你只传递一个文件名,它将循环通过 $LOAD_PATH 并在每个目录中搜索文件。
> $LOAD_PATH.push("/Users/myuser")
> load('foo.rb')
foo.rb loaded!
=> true
要求
1) 对同一个文件调用两次 require 只会执行一次。如果您使用相对路径引用一次,使用绝对路径引用一次,则不会加载相同的文件两次,这也很聪明。
2) 如果文件被执行,require 返回 true,否则返回 false。
3) require 跟踪全局变量 $LOADED_FEATURES 中已经加载了哪些文件。
4) 您不需要包含文件扩展名:
require 'foo'
5) require 会查找 foo.rb,但也会查找动态库文件,例如 foo.so、foo.o 或 foo.dll。这就是您可以从 ruby 调用 C 代码的方式。
6) require 不检查当前目录,因为当前目录默认不在 $LOAD_PATH 中。
7) require_relative 采用相对于当前文件的路径,而不是进程的工作目录。
红宝石
1) Rubygems 是一个包管理器,旨在轻松管理名为 gems 的 Ruby 库的安装。
2) 它将其内容打包为一个 zip 文件,其中包含一堆 ruby 文件和/或可以由您的代码导入的动态库文件,以及一些元数据。
3) Rubygems 用自己的版本替换了默认的 require 方法。除了 $LOAD_PATH 中的目录之外,该版本还将查看已安装的 gem。如果 Rubygems 在您的 gems 中找到该文件,它将将该 gem 添加到您的 $LOAD_PATH。
4) gem install 命令找出gem 的所有依赖项并安装它们。实际上,它会在安装 gem 本身之前安装 gem 的所有依赖项。
捆绑器
1) Bundler 让您可以指定项目所需的所有 gem,以及这些 gem 的可选版本。然后 bundle 命令安装所有这些 gem 及其依赖项。
2) 您可以在名为 Gemfile 的文件中指定所需的 gem。
3) bundle 命令还会安装 Gemfile.lock 中列出的特定版本的所有 gem。
4) 将 bundle exec 放在命令之前,例如bundle exec rspec,确保 require 将加载 Gemfile.lock 中指定的 gem 版本。
Rails 和 Bundler
1) 在 config/boot.rb 中,运行 require 'bundler/setup'。 Bundler 确保 Ruby 可以找到 Gemfile 中的所有 gem(以及它们的所有依赖项)。 require 'bundler/setup' 将自动发现您的 Gemfile,并使您的 Gemfile 中的所有 gem 对 Ruby 可用(在技术术语中,它将 gem 放在“加载路径”上)。您可以将其视为添加一些额外的权力来要求“红宝石”。
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
2) 既然您的代码可用于 Ruby,您就可以要求所需的 gem。例如,您可以要求 'sinatra'。如果你有很多依赖项,你可能想说“需要我的 Gemfile 中的所有 gem”。为此,请将以下代码紧跟在 require 'bundler/setup' 之后:
Bundler.require(:default)
3) 默认情况下,调用 Bundler.require 将需要 Gemfile 中的每个 gem。如果 Gemfile 中的行显示 gem 'foo', :require => false 那么它将确保 foo 已安装,但不会调用 require。如果你想使用 gem,你必须调用 require('foo')。
因此,鉴于知识的广度,我回到了我的测试问题,并意识到我必须明确要求 rails_helper.rb 中的 gem,因为 Bundler.setup 将它添加到 $LOAD_PATH 但需要:false 排除了 Bundler.require from明确要求它。然后问题就解决了。
【讨论】: