从 Rails 3 中的 lib 文件夹加载模块/类的最佳方法?

Posted

技术标签:

【中文标题】从 Rails 3 中的 lib 文件夹加载模块/类的最佳方法?【英文标题】:Best way to load module/class from lib folder in Rails 3? 【发布时间】:2011-03-22 08:49:52 【问题描述】:

由于最新的 Rails 3 版本不再从 lib 自动加载模块和类, 加载它们的最佳方式是什么?

来自github:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);

【问题讨论】:

【参考方案1】:

As of Rails 2.3.9,config/application.rb 中有一个设置,您可以在其中指定包含要自动加载的文件的目录。

来自 application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#config.root/extras)

【讨论】:

如果您希望自动加载app/lib 的整个子树,请注意@thankful 的回答。【参考方案2】:
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#config.root/lib/**/"]

来源:Rails 3 Quicktip: Autoload lib directory including all subdirectories, avoid lazy loading

请注意,lib 文件夹中包含的文件仅在服务器启动时才会加载。如果您希望自动重新加载这些文件,请阅读:Rails 3 Quicktip: Auto reload lib folders in development mode。请注意,这不适用于生产环境,因为永久重新加载会减慢机器速度。

【讨论】:

链接失效【参考方案3】:

自动加载东西的魔力

我认为控制完成自动加载内容的文件夹的选项已在其他答案中充分涵盖。但是,如果其他人虽然根据需要修改了自动加载路径,但在加载内容时遇到问题,那么这个答案试图解释这个自动加载背后的魔力。

因此,在从子目录加载内容时,您应该注意一个问题或约定。有时 Ruby/Rails 的魔力(这次主要是 Rails)会让人很难理解为什么会发生某些事情。只有当模块名称对应于父目录名称时,才会加载自动加载路径中声明的任何模块。因此,如果您尝试输入 lib/my_stuff/bar.rb 之类的内容:

module Foo
  class Bar
  end
end

它不会自动加载。如果您将父目录重命名为foo,则再次将您的模块托管在路径:lib/foo/bar.rb。它会在那里为你服务。另一种选择是通过模块名称命名要自动加载的文件。显然,那个名称只能有一个文件。如果您需要将您的东西拆分成多个文件,您当然可以使用该文件来要求其他文件,但我不建议这样做,因为在开发模式下并且您修改这些其他文件时,Rails 无法自动执行为您重新加载它们。但是,如果您真的想要,您可以拥有一个模块名称的文件,然后指定使用该模块所需的实际文件。所以你可以有两个文件:lib/my_stuff/bar.rblib/my_stuff/foo.rb,前者与上面相同,后者包含一行:require "bar",这将是一样的。

附:我觉得有必要补充一件更重要的事情。最近,每当我想在 lib 目录中有一些需要自动加载的东西时,我倾向于开始思考,如果这是我实际上专门为这个项目开发的东西(通常是这样,可能有一天变成许多项目或 git 子模块等中使用的代码的“静态”sn-p。在这种情况下,它肯定应该在 lib 文件夹中)那么它的位置可能根本不在 lib 文件夹中。或许应该在app文件夹下的子文件夹里·我有一种感觉,这是新的rails做事方式。显然,无论您在自动加载路径中放置您的东西的任何地方,同样的魔法都在发挥作用,因此这对这些东西有好处。无论如何,这只是我对这个问题的想法。你可以不同意。 :)


更新:关于魔法的类型..

正如 severin 在他的评论中指出的那样,核心“自动加载模块机制”肯定是 Ruby 的一部分,但自动加载路径的东西不是。你不需要 Rails 来做autoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar")。当您第一次尝试引用模块 Foo 时,它将为您加载。然而,Rails 所做的是它为我们提供了一种尝试从注册文件夹自动加载内容的方法,并且它已经以一种需要假设命名约定的方式实现。如果它没有像那样实现,那么每次您引用当前未加载的内容时,它都必须检查所有自动加载文件夹中的所有文件,并检查其中是否包含您要引用的内容。这反过来又会破坏自动加载和自动重新加载的想法。但是,有了这些约定,它可以从模块/类中扣除您尝试加载可能定义的位置并加载它。

【讨论】:

为什么 Ruby 如此神奇? Ruby 仅提供 Module#autoload 函数,您可以使用它来命令在访问(未定义的)常量时加载文件(请参阅ruby-doc.org/core-1.9.3/Module.html#method-i-autoload)。在我看来,模块/类名称与目录/文件的匹配是在 Rails/ActiveSupport 中完成的(例如:github.com/rails/rails/blob/…)。我错了吗? 是的,我相信你是对的。当 Zabba 指出它的“缺陷”时,我太草率地“纠正”了我原来的答案。让我再更新一下我的答案以澄清这个问题。 我花了半个小时左右的时间胡思乱想。我需要(想要)自动加载 Sprockets::JSRender::Processor。可以通过进入 rails 控制台并执行“S​​prockets::JSRender::Processor”.underscore 并发现它是“sprockets/js_render/processor”(添加了 .rb)HTH 某人来找到该路径。 你刚刚拯救了我的理智。 ~深深地松了一口气~非常感谢你的分享:) 感谢您提供最有帮助的评论。在阅读您的评论之前,我不明白为什么某些模块的行为如此。祝福你!【参考方案4】:

警告:如果你想从你的'lib'文件夹中加载'monkey patch'或'open class',不要使用'autoload' approach!!!

"config.autoload_paths" 方法:仅在加载仅在一个位置定义的类时才有效。如果某个类已经在其他地方定义了,那么你不能通过这种方法再次加载它。

"config/initializer/load_rb_file.rb" 方法:总是有效!无论目标课程是新课程还是现有课程的“开放课程”或“猴子补丁”,它总是有效的!

更多详情请见:https://***.com/a/6797707/445908

【讨论】:

这是一个需要理解的关键区别。谢谢你。【参考方案5】:

非常相似,但我认为这更优雅一点:

config.autoload_paths += Dir["#config.root/lib", "#config.root/lib/**/"]

【讨论】:

【参考方案6】:

在我的例子中,我试图直接在 lib 目录下加载一个文件。

在 application.rb...

require '/lib/this_file.rb' 

没有工作,即使在控制台中,然后当我尝试时

require './lib/this_file.rb' 

并且 rails 可以完美地加载文件。

我还是个菜鸟,我不确定为什么会这样,但它确实有效。如果有人想向我解释它,我会很感激 :D 我希望这对任何人都有帮助。

【讨论】:

这是因为 ./lib/this_file.rb 在当前目录中查找(在 Rails 控制台中,这将是您的 Rails 根目录),而 /lib/this_file.rb 将其作为绝对路径查找。示例:./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb【参考方案7】:

我遇到了同样的问题。这是我解决它的方法。该解决方案加载 lib 目录和所有子目录(不仅是直接目录)。当然,您可以将其用于所有目录。

# application.rb
config.autoload_paths += %W(#config.root/lib)
config.autoload_paths += Dir["#config.root/lib/**/"]

【讨论】:

这具有完全破坏 Rails 命名空间约定的令人讨厌的副作用。如果在自动加载查找中定义 Bar::Foo 的 lib/bar/foo.rb 出现在定义 Foo 的 lib/foo.rb 之前,那么如果您尝试通过引用加载 lib/foo.rb到 Foo 常量。【参考方案8】:

config.autoload_paths 对我不起作用。我用其他方法解决了

Ruby on rails 3 不会从 /lib 文件夹自动重新加载(自动加载)代码。我通过输入ApplicationController来解决它

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 

【讨论】:

【参考方案9】:

截至Rails 5,建议将lib文件夹放在app目录下,或者为该文件夹创建其他有意义的命名空间servicespresentersfeatures等,并将其放在app目录下通过导轨自动加载。

请同时查看GitHub Discussion Link。

【讨论】:

感谢您发布 GH 问题链接。许多其他项目在将lib 移至app\lib 时对其进行了引用。【参考方案10】:

如果只有某些文件需要访问 lib 中的模块,只需在需要它的文件中添加 require 语句即可。例如,如果一个模型需要访问一个模块,则添加:

require 'mymodule'

在 model.rb 文件的顶部。

【讨论】:

您不应该在 Rails 应用程序中使用 require,因为它会阻止 ActiveSupport::Dependencies 正确 [un] 加载该代码。相反,您应该像上面的答案一样使用config.autoload_paths,然后根据需要包含/扩展。 谢谢@Mike,我正要照你做的,很高兴看到为什么不好的解释,谢谢你没有删除答案。 如果你只想加载一个模块,那么 include 'mymodule' 怎么样? @ben_h 你不应该在 Rails 应用程序的任何地方require 吗?在一个 rake 任务中,我目前正在 require-ing 和 include-ing 一个位于 lib/ 中的模块。我不应该这样做吗? @ben_h 我的搜索显示require 您的lib/ 代码很常见(例如this blog post、this SO answer)。我仍然不确定整个事情。您能否提供更多关于未使用 require 的说法的证据?【参考方案11】:

正确拼写文件名。

说真的。我和一个班级争吵了一个小时,因为班级是 Governance::ArchitectureBoard 并且文件在 lib/governance/architecture_baord.rb 中(在“board”中转置了 O 和 A)

回想起来似乎很明显,但这是魔鬼在追踪它。如果类没有定义在 Rails 期望它基于修改类名的文件中,它根本不会找到它。

【讨论】:

【参考方案12】:

从 lib 加载时出现问题的原因有多种 - 请参阅此处了解详细信息 - http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/

修复自动加载路径 线程安全相关 命名相关 ...

【讨论】:

以上是关于从 Rails 3 中的 lib 文件夹加载模块/类的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

(Rails)重新加载“lib”文件而无需重新启动服务器......? [复制]

Ruby on Rails 3 - 为每个请求重新加载 lib 目录

Rails 3库直到需要才加载

在 Rails 4 中自动加载 lib 文件

Rails 3.2 - 无法加载初始化文件的模块

Rails 3.1.3 - 从供应商/资产/样式表中的子文件夹加载资产