【问题标题】:Adding a directory to $LOAD_PATH (Ruby)将目录添加到 $LOAD_PATH (Ruby)
【发布时间】:2009-05-07 21:26:51
【问题描述】:

我见过两种常用的技术,用于将当前正在执行的文件的目录添加到 $LOAD_PATH(或 $:)。如果您不使用 gem,我会看到这样做的好处。显然,一个似乎比另一个更冗长,但是有理由选择一个而不是另一个吗?

第一种,冗长的方法(可能有点矫枉过正):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

还有更直接、快速和肮脏的方法:

$:.unshift File.dirname(__FILE__)

有什么理由选择一个而不是另一个?

【问题讨论】:

  • 一个 稍微 不那么冗长的版本是:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
  • “除非”子句怎么样?以上两者怎么能等价呢?
  • 作为一个来这里尝试了解如何使用它的人,它非常神秘。我看不到示例中目录名称的来源。如果有人能说清楚,我将不胜感激。
  • 使用__dir__(从 Ruby 2.0 开始)可以使这些更简洁。

标签: ruby rubygems load-path


【解决方案1】:

Ruby 加载路径通常写成 $: ,但仅仅因为它很短,并不能使它变得更好。如果你更喜欢清晰而不是聪明,或者如果简洁本身就让你发痒,那么你不必仅仅因为其他人都这样做。 打个招呼……

$LOAD_PATH

...和...说再见

# I don't quite understand what this is doing...
$:

【讨论】:

  • 另外,对于像“$:”这样只包含符号的字符串,谷歌搜索起来要困难得多。
【解决方案2】:

我会说使用$:.unshift File.dirname(__FILE__) 而不是另一个,只是因为我看到它在代码中的使用比$LOAD_PATH 多得多,而且它也更短!

【讨论】:

  • 当我第一次开始使用 Ruby 时,我显然认为 $LOAD_PATH 更好。但是一旦你从初学者状态毕业,如果我试图让我的代码对初学者来说更具可读性,我只会使用 $LOAD_PATH 。嗯,这是一个权衡。这取决于代码的“公共”程度,只要每个代码的内存使用量相同,我认为它本质上是相同的。
  • 取决于您为项目遵循的样式指南。流行的Ruby Style Guide 说“避免使用 Perl 风格的特殊变量(如 $:、$; 等)。它们非常神秘,不鼓励在单行脚本之外的任何地方使用它们。”
【解决方案3】:

我不太喜欢“快速而肮脏”的方式。 任何刚接触 Ruby 的人都会思考$:. 是什么。

我觉得这更明显。

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

或者如果我关心拥有完整路径...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

更新 2009/09/10

最近我一直在做以下事情:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

我在浏览 GitHub 时在一大堆不同的 ruby​​ 项目中看到了它。

似乎是惯例?

【讨论】:

  • @LukeAntins,这真的很棒,但我应该在哪里“引导”load_path 在应用程序中?
  • @gaussblurinc 在你的库/应用程序“靠近顶部”的某个地方,但这真的取决于。如果您有一个始终与您的 code 相关的 bin 文件,并且它仅由 bin 文件运行...在 bin 中引导。如果您有一个库,则在您的库代码顶部引导程序,如lib/code.rb 以访问lib/code/ 下的所有内容。希望这篇游记有所帮助!
  • RuboCop 告诉我__dir__ 可用于获取当前文件目录的路径。
【解决方案4】:

如果您在 Rails 项目中键入 script/console 并输入 $:,您将获得一个包含加载 Ruby 所需的所有目录的数组。这个小练习的要点是$: 是一个数组。既然如此,您可以在其上执行功能,例如使用 unshift 方法或 << 运算符添加其他目录。正如您在声明中暗示的那样,$:$LOAD_PATH 是相同的。

您提到的快速而肮脏的方式的缺点是:如果您的引导路径中已经有该目录,它会重复自己。

例子:

我创建了一个名为 todo 的插件。我的目录结构如下:

/ - -小贩 | |---/插件 | |---/待办事项 | |---/库 | |---/应用 | |---/型号 |---/控制器 | |---/导轨 | |---init.rb

在 init.rb 文件中我输入了以下代码:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

请注意我如何告诉代码块执行块内的操作到字符串“models”、“controllers”和“models”,在这里我重复“models”。 (仅供参考,%w{ ... } 只是告诉 Ruby 保存字符串数组的另一种方式)。当我运行script/console 时,我输入以下内容:

>> puts $:

我输入这个是为了更容易阅读字符串中的内容。我得到的输出是:

... ... ./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models ./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers ./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

如您所见,虽然这是我在使用我目前正在从事的项目时可以创建的简单示例,但如果您不小心,这种快速而肮脏的方式会导致重复路径。较长的方法将检查重复的路径并确保它们不会发生。

如果您是一位经验丰富的 Rails 程序员,您可能对自己正在做的事情有一个很好的了解,并且可能不会犯重复路径的错误。如果您是新手,我会走更长的路,直到您真正了解自己在做什么。

【讨论】:

  • 您的回复很有帮助,也很好解释。建议编辑:方法 load_pathsload_once_paths.delete 已被弃用。将有助于更新引用它们的行:ActiveSupport::Dependencies.autoload_paths &lt;&lt; pathActiveSupport::Dependencies.autoload_once_paths.delete(path)
【解决方案5】:

在使用 Rspec 时,我遇到过通过相对路径添加目录的最佳方法。我觉得它足够冗长,但仍然是一个不错的衬里。

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

【讨论】:

    【解决方案6】:

    有一个 gem 可以让你用更好更简洁的代码设置你的加载路径。看看这个:https://github.com/nayyara-samuel/load-path

    它也有很好的文档

    【讨论】:

    • 显然不存在了
    【解决方案7】:

    我知道距离第一次提出这个问题已经有很长时间了,但我还有一个额外的答案想要分享。

    我有几个 Ruby 应用程序是由另一位程序员在几年内开发的,它们在不同的应用程序中重复使用相同的类,尽管它们可能访问同一个数据库。由于这违反了 DRY 规则,我决定创建一个类库以供所有 Ruby 应用程序共享。我本可以将它放在主 Ruby 库中,但这会将自定义代码隐藏在我不想做的公共代码库中。

    我遇到了一个问题,我在已定义的名称“profile.rb”和我正在使用的类之间存在名称冲突。在我尝试创建公共代码库之前,这种冲突不是问题。通常,Ruby 首先搜索应用程序位置,然后转到 $LOAD_PATH 位置。

    application_controller.rb 找不到我创建的类,并在原始定义上抛出错误,因为它不是类。由于我从应用程序的 app/models 部分中删除了类定义,Ruby 无法在其中找到它并在 Ruby 路径中查找它。

    所以,我修改了 $LOAD_PATH 变量以包含我正在使用的库目录的路径。这可以在初始化时在 environment.rb 文件中完成。

    即使将新目录添加到搜索路径中,Ruby 还是会抛出错误,因为它优先获取系统定义的文件。 $LOAD_PATH 变量中的搜索路径优先搜索 Ruby 路径。

    因此,我需要更改搜索顺序,以便 Ruby 在搜索内置库之前在我的公共库中找到该类。

    这段代码是在 environment.rb 文件中完成的:

    Rails::Initializer.run do |config|
    
    * * * * *
    
    path = []
    path.concat($LOAD_PATH)
    $LOAD_PATH.clear
    $LOAD_PATH << 'C:\web\common\lib'
    $LOAD_PATH << 'C:\web\common'
    $LOAD_PATH.concat(path)
    
    * * * * *
    
    end
    

    我认为您不能在此级别使用之前给出的任何高级编码结构,但如果您想在应用程序的初始化时设置一些东西,它就可以正常工作。在将原始 $LOAD_PATH 变量添加回新变量时,您必须保持其原始顺序,否则一些主要的 Ruby 类会丢失。

    在 application_controller.rb 文件中,我简单地使用了一个

    require 'profile'
    require 'etc' #etc
    

    这会加载整个应用程序的自定义库文件,也就是说,我不必在每个控制器中都使用 require 命令。

    对我来说,这是我正在寻找的解决方案,我想我会将它添加到这个答案中以传递信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-07
      • 1970-01-01
      • 2011-01-20
      • 2013-04-13
      相关资源
      最近更新 更多