Auto-loading lib files in Rails 4

2018-12-31 21:26发布

问题:

I use the following line in an initializer to autoload code in my /lib directory during development:

config/initializers/custom.rb:

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

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

It works great, but it\'s too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:

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/**/\"]

However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.

Example of one of my lib files:

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

Calling Time.milli_stamp will throw NoMethodError

I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn\'t to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What\'s the right way to do this?

回答1:

I think this may solve your problem:

  1. in config/application.rb:

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

    and keep the right naming convention in lib.

    in lib/foo.rb:

    class Foo
    end
    

    in lib/foo/bar.rb:

    class Foo::Bar
    end
    
  2. if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:

    in config/initializers/require.rb:

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

P.S.

  • Rails 3 Autoload Modules/Classes by Bill Harding.

  • And to understand what does Rails exactly do about auto-loading?
    read Rails autoloading — how it works, and when it doesn\'t by Simon Coffey.



回答2:

Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.

To avoid all the autoload_paths or eager_load_paths hassle, create a \"lib\" or a \"misc\" directory under \"app\" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.



回答3:

This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:

In file lib/development_mail_interceptor.rb (Yes, I\'m using code from a 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

works, but it doesn\'t load if I hadn\'t put the class inside a module.



回答4:

Use config.to_prepare to load you monkey patches/extensions for every request in development mode.

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\"

end