在Rails 4中自动加载lib文件


229

/lib在开发过程中,我在初始化器中使用以下行来自动加载目录中的代码:

config / initializers / custom.rb:

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

(来自Rails 3 Quicktip:在开发模式下自动重新加载lib文件夹

它工作得很好,但是在生产中使用效率太低-我只想在启动时加载它们,而不是在每个请求上加载库。同一博客还有另一篇文章描述了如何执行此操作:

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文件夹?
Jwan622 '16

Answers:


548

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

  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
  2. 如果您确实想在文件lib / extensions.rb之类的文件中做一些猴子补丁,则可以手动要求它:

    config / initializers / require.rb中

    require "#{Rails.root}/lib/extensions" 

聚苯乙烯


1
@ifyouseewendy-您说得很对-因为extensions.rb没有遵循Rails的命名约定,所以Rails不会在加载过程中包含它。我是通过手动要求它来工作的。
Yarin 2013年

@ifyouseewendy如何在加载模型之前包含文件?添加自动加载的路径很酷,但是如何控制包含顺序?thx
Matrix

@Matrix“在加载模型之前包括文件”,您可以手动要求文件而不使用自动加载功能。
ifyouseewendy 2014年

@ifyouseewendy,如果我在初始化程序中需要它,但是文件在autoload_path中,它将被重新加载(加载2次)吗?theire是一个类似PHP的“ require_once”吗?
Matrix

5
这似乎在生产中的Rails 5 API中不起作用(但在开发中)。相信您需要使用config.eager_load_paths << Rails.root.join('lib')。但是,这样做的主要缺点是也可以eager_load_paths加载任务中的所有内容。我认为卢拉拉拉的解决方案更好。这是一个具有更多详细信息的博客文章:blog.arkency.com/2014/11/…–
hirowatari

33

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

为了避免所有的autoload_pathseager_load_paths麻烦,创建一个“LIB”或“应用程序”目录下的“杂项”目录。像通常在其中那样放置代码,Rails将像加载(和重新加载)模型文件一样加载文件。


3
我在Rails 4.2中。它不会自动加载文件下app,我需要做手工......还是需要把它放在autload路径..
奥雅纳Rakshit

6
你错了,奥雅纳,应用程序目录的所有子目录都自动在Rails的4.2.See的autoload_paths阵列中edgeguides.rubyonrails.org/...
Dr.Strangelove

除了app/views没有添加的目录;或者更确切地说,被明确删除。
詹姆斯·伯恩

1
好答案。只有在Rails 5 / api上对我有用的东西。
jstafford '16

6
只需记住,lib它的意思是可以应用于多个项目的代码,并且有可能被提取到gem中。如果没有,请在应用程序搜索下创建一个更合适的文件夹,services/或者presenters/甚至将其作为子目录。
PhilT '16

6

这可能会帮助像我这样的人在寻找有关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

可以,但是如果我没有将类放在模块中,则不会加载。


1
在ruby中,“适当匹配”是指文件位于文件系统中LOAD_PATH/module/class.rb(加有下划线),LOAD_PATH位于Ruby应用程序使用的加载路径中(在Rails中为autoload_paths)。 lib已从由Rails自动加载更改为不自动加载,并且在最新版本(> = Rails 3.x)中未自动加载。不建议使用任何魔术手段为您工作。也许是旧的Railscast?
Peter H. Boling

0

使用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"

结束

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.