将目录添加到$ LOAD_PATH(Ruby)


96

我已经看到了两种将当前正在执行的文件目录添加到$ 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__)

有什么理由要互相反对吗?


2
一个少详细的版本的详细之一是:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
内森龙

“除非”子句怎么样?上面的两个如何相等?
inger 2014年

作为来这里尝试了解如何使用它的人,它是超级神秘的。我没有看到示例中的目录名称来自何处。如果有人可以说清楚,我将不胜感激。
SlySherZ 2015年

1
使用__dir__(从Ruby 2.0开始)可以使所有这些内容更加简洁。
内森·朗

Answers:


52

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


刚开始使用Ruby时,我显然认为$ LOAD_PATH更好。但是,一旦您从初学者状态毕业,如果我想使我的代码对初学者更易读,那么我只会使用$ LOAD_PATH。嗯,这是一个折衷。这取决于代码的“公开”程度,只要每个内存的使用情况都相同(我认为实际上是这样)即可。
boulder_ruby 2012年

9
取决于您为项目遵循的样式指南。流行的《Ruby样式指南》说:“避免使用Perl样式的特殊变量(例如$:,$;等)。它们非常隐秘,不建议将它们用于单行脚本以外的任何东西。”
bobmagoo

153

Ruby的加载路径通常被看成$:,但是仅仅因为它很短,并不能改善它。如果您更喜欢清晰而不是机灵,或者出于简洁目的让自己发痒,那么就不必因为其他所有人都那么做。打招呼 ...

$LOAD_PATH

...再见...

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

29
而且,对于Google来说,仅包含符号的字符串“ $:”要困难得多。
DSimon 2012年

23

我不太喜欢“快速而肮脏”的方式。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 2014年

@gaussblurinc库/应用程序“靠近顶部”的某个位置,但这确实取决于。如果您有一个bin始终与您有关的文件,code并且该bin文件仅由该文件运行过,请执行... bin中的引导程序。如果您有图书馆,则可以在库代码的顶部进行引导,例如in,lib/code.rb以访问下方的所有内容lib/code/。希望这次漫步能有所帮助!
路加·安东斯

1
RuboCop告诉我,__dir__可以用来获取当前文件目录的路径。
拉斐尔

8

如果您输入script/consoleRails项目并输入$:,您将获得一个数组,其中包含加载Ruby所需的所有目录。这项小练习的收获是,这$:是一个数组。这样,您可以在其上执行功能,例如使用unshift方法或<<运算符在其他目录之前添加。正如你在发言中暗示$:,并$LOAD_PATH是相同的。

如前所述,以快速而肮脏的方式执行此操作的缺点是:如果引导路径中已经有该目录,它将自动重复。

例:

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

/-供应商
  |
  | --- /插件
        |
        | --- /待办事项
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- /型号
                          | --- /控制器
              |
              | --- /导轨
                    |
                    | --- 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 << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar

8

最好我遇到了使用Rspec时通过相对路径添加目录的问题。我觉得它很详细,但仍然是一个不错的选择。

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


-2

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

我有另一位程序员几年来开发的几个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命令。

对我来说,这就是我一直在寻找的解决方案,我想我会将其添加到此答案中以传递信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.