Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are require, require_dependency and constants reloading related in Rails?

How are require and require_dependency different?
How can require_dependency automatically reload classes in development but require can't ?

I digged into Rails' ActiveSupport::Dependencies and dispatcher.rb code. What I saw in require_dependency's code is it basically adds the constants to an autoloaded_constants array. But it gets cleared in clear_application inside dispatcher after each request.

Can someone give a clear explanation or point me to some resources which will help?

like image 721
wei Avatar asked Sep 21 '09 22:09

wei


People also ask

What is Autoloading in Rails?

Rails automatically reloads classes and modules if application files in the autoload paths change. More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants managed by the main autoloader just before the next request is processed.

What is autoload in Ruby?

Ruby has an in-built module autoload, which comes into action whenever a specific module or a class is accessed or called upon from the parent or calling class or module. Upon receiving a call, this module registers the corresponding file path to the called module.

How do I use require in Ruby?

The require method takes the name of the file to require, as a string, as a single argument. This can either be a path to the file, such as ./lib/some_library. rb or a shortened name, such as some_library. If the argument is a path and complete filename, the require method will look there for the file.

What is Zeitwerk?

Zeitwerk is an efficient and thread-safe code loader for Ruby. Given a conventional file structure, Zeitwerk is able to load your project's classes and modules on demand (autoloading), or upfront (eager loading).


2 Answers

require (and its cousin load) are core Ruby methods. require_dependency is a method that helps Rails handle the problem of dependency management. Long story short, it allows Rails to reload classes in development mode so that you don't have to restart the server each time you make a code change. The Rails framework will require_dependency your code so that it can track and reload it when changes are made. The standard Ruby require doesn't do that. As an app (or plugin/engine) developer you should not have to worry about require_dependency as this is purely internal to Rails.

The magic of the Rails class loading process is in the ActiveSupport::Dependencies module. This code extends the default Ruby behavior to allow code inside your Rails app to automatically load modules (including classes which inherit from Module) using Rails' path and file naming conventions. This eliminates the need for the programmer to litter their code with require calls like you would in a plain Ruby application.

To put it another way, this lets you define class Admin::User inside the file app/models/admin/user.rb and have Rails know what you are talking about when you call Admin::User.new from another part of the application like a controller. Without ActiveSupport::Dependencies involved you would have to manually require everything you needed.

If you are coming from a statically typed language like C#, Java, etc then this might be a surprise: Rails code is not loaded until it is needed. For example, a User model class isn't defined and user.rb isn't loaded until AFTER you try to call User.whatever_method_here. Rails prevents Ruby from complaining about that missing constant, loads code for User, and then allows Ruby to continue as normal.

While I can't speak for your specific need, I would be very surprised if you actually needed to use the require_dependency method from within a plugin or engine. If you follow Rails conventions you shouldn't have to tweak the $LOAD_PATH by hand, either. This is not "the Rails way".

In the world of Ruby and also Rails simplicity and clarity is key. If all you want to do is write a plugin or engine and you are already diving deep into internals then you may consider approaching your problem from a different angle. My gut tells me that you may be trying to do something that is needlessly complicated. But then again, I have no clue what you are doing exactly!! :)

like image 196
MDaubs Avatar answered Oct 06 '22 01:10

MDaubs


require_dependency is useful in an engine when you want to re-open a class which is not defined in your engine (for example in another engine or Rails app) and have it reloaded. In which case something like this works:

# app/controllers/my_engine/documents_controller.rb require_dependency MyEngine::Engine.root.join('app', 'controllers', 'my_engine', 'documents_controller').to_s  module MyEngine   class DocumentsController     def show       render :text => 'different'     end   end end 
like image 28
Kris Avatar answered Oct 06 '22 01:10

Kris