在 Rails 4 中自动加载 lib 文件

Posted

技术标签:

【中文标题】在 Rails 4 中自动加载 lib 文件【英文标题】:Auto-loading lib files in Rails 4 【发布时间】:2013-10-06 13:53:09 【问题描述】:

我在初始化程序中使用以下行在开发期间自动加载我的/lib 目录中的代码:

config/initializers/custom.rb:

RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?

(来自Rails 3 Quicktip: Auto reload lib folders in development mode)

效果很好,但在生产中使用效率太低 - 我不想在每个请求上加载库,我只想在启动时加载它们。同一个博客有 another article 描述如何做到这一点:

config/application.rb:

# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#config.root/lib)
config.autoload_paths += Dir["#config.root/lib/**/"]

但是,当我切换到那个时,即使在开发中,我在尝试使用 lib 函数时也会收到 NoMethodErrors。

我的一个 lib 文件的示例:

lib/extensions.rb:

Time.class_eval do
  def self.milli_stamp
    Time.now.strftime('%Y%m%d%H%M%S%L').to_i
  end
end

调用Time.milli_stamp会抛出NoMethodError

我意识到其他人已经回答了关于 SO 的类似问题,但他们似乎都在处理命名约定和其他我以前不必担心的问题 - 我的 lib 类已经工作 -请求加载,我只想将其更改为按启动加载。这样做的正确方法是什么?

【问题讨论】:

Rails 应用启动时是否会自动加载 config/initializers 文件夹? 【参考方案1】:

我认为这可能会解决您的问题:

    config/application.rb 中:

    config.autoload_paths << Rails.root.join('lib')
    

    并在 lib 中保持正确的命名约定。

    lib/foo.rb 中:

    class Foo
    end
    

    lib/foo/bar.rb

    class Foo::Bar
    end
    

    如果你真的想在 lib/extensions.rb 之类的文件中做一些猴子补丁,你可以手动要求它:

    config/initializers/require.rb

    require "#Rails.root/lib/extensions" 
    

附言

Rails 3 Autoload Modules/Classes by Bill Harding。

要了解 Rails 对自动加载到底做了什么? 阅读 Simon Coffey 的 Rails autoloading — how it works, and when it doesn't。

【讨论】:

@ifyouseewendy- 你说得对——因为 extensions.rb 没有遵循 Rails 命名约定,Rails 不会在加载过程中包含它。我通过手动要求它使其工作。 @ifyouseewendy 如何在加载模型之前包含文件?添加自动加载的路径很酷,但是如何控制包含的顺序?谢谢 @Matrix "在加载模型之前包含文件",您可以手动要求您的文件而不使用自动加载功能。 @ifyouseewendy 如果我在初始化程序中需要它但文件在 autoload_path 中,是否会重新加载(加载 2 次)?他们是像 php 中的“require_once”? 这似乎在 Rails 5 API 中不起作用(但在开发中)。我相信你需要使用config.eager_load_paths &lt;&lt; Rails.root.join('lib')。但是,这有一个主要缺点,因为eager_load_paths 也会在任务中加载所有内容。我认为 lulalala 的解决方案更好。这是一篇包含更多详细信息的博客文章:blog.arkency.com/2014/11/…【参考方案2】:

这可能会帮助像我这样的人在搜索 Rails 如何处理类加载的解决方案时找到这个答案......我发现我必须定义一个 module,其名称与我的文件名适当匹配,而不仅仅是定义一个类:

在文件 lib/development_mail_interceptor.rb 中(是的,我使用的是 Railscast 中的代码 :))

module DevelopmentMailInterceptor
  class DevelopmentMailInterceptor
    def self.delivering_email(message)
      message.subject = "intercepted for: #message.to #message.subject"
      message.to = "myemail@mydomain.org"
    end
  end
end

有效,但如果我没有将类放在模块中,它不会加载。

【讨论】:

在 ruby​​ 中“适当匹配”意味着文件位于文件系统中 LOAD_PATH/module/class.rb(下划线),其中 LOAD_PATH 位于 Ruby 应用程序使用的加载路径中(本例中为 autoload_paths轨道)。 lib 已经从被 Rails 自动加载到不自动加载,并且在最近的版本 (>= Rails 3.x) 中它没有自动加载。不推荐任何使这项工作为您工作的魔法。也许它是一个旧的 Railscast?【参考方案3】:

虽然这并不能直接回答这个问题,但我认为完全避免这个问题是一个很好的选择。

为避免所有autoload_pathseager_load_paths 的麻烦,请在“app”目录下创建“lib”或“misc”目录。像往常一样在其中放置代码,Rails 会像加载(和重新加载)模型文件一样加载文件。

【讨论】:

我在 Rails 4.2 中。并且它不会自动加载app下的文件,我需要手动执行......或者需要将它放在自动加载路径中.. 你错了,Arup,在 Rails 4.2 中 app 目录的任何子目录都自动在 autoload_paths 数组中。见edgeguides.rubyonrails.org/… 除了app/views目录没有被添加;或者更确切地说被明确删除。 很好的答案。只有在 rails 5/api 上对我有用的东西。 请记住,lib 用于可应用于多个项目并可能被提取到 gem 中的代码。如果没有在应用搜索下创建一个更合适的文件夹,如 services/presenters/ 甚至是这些文件夹的子目录。【参考方案4】:

使用 config.to_prepare 为开发模式下的每个请求加载猴子补丁/扩展。

config.to_prepare do |action_dispatcher|
 # More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
 Rails.logger.info "\n--- Loading extensions for #self.class "
 Dir.glob("#Rails.root/lib/extensions/**/*.rb").sort.each do |entry|
   Rails.logger.info "Loading extension(s): #entry"
   require_dependency "#entry"
 end
 Rails.logger.info "--- Loaded extensions for #self.class\n"

结束

【讨论】:

以上是关于在 Rails 4 中自动加载 lib 文件的主要内容,如果未能解决你的问题,请参考以下文章

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

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

无法在 Rails 4 中自动加载常量 API 控制器

重新加载 rails 初始化器

自动加载常量时检测到循环依赖(Rails 4、Ruby 2)

Websocket 控制器未在 Rails 开发环境中自动加载