在 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 << 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_paths
或eager_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 文件夹加载模块/类的最佳方法?