$LOADED_FEATURES and require, load, require_dependency

Neeraj Singh

By Neeraj Singh

on May 12, 2010

Rails developers know that in development mode classes are loaded on demand. In production mode all the classes are loaded as part of bootstrapping the system. Also in development mode classes are reloaded every single time page is refreshed.

In order to reload the class, Rails first has to unload . That unloading is done something like this.

1# unload User class
2Objet.send(:remove_const, :User)

However a class might have other constants and they need to be unloaded too. Before you unload those constants you need to know all the constants that are defined in the class that is being loaded. Long story short rails keep track of every single constant that is loaded when it loads User or UserController.

Dependency mechanism is not perfect

Sometimes dependency mechanism by rails lets a few things fall through the crack. Try following case.

1require 'open-uri'
2class UsersController < ApplicationController
3  def index
4    open("http://www.ruby-lang.org/") {|f| }
5    render :text => 'hello'
6  end
7end

Start the server in development mode and visit http://localhost:3000/users . First time every thing will come up fine. Now refresh the page. This time you should get an exception uninitialized constant OpenURI .

So what's going on.

After the page is served the very first time then at the end of response rails will unload all the constants that were autoloaded including UsersController. However while unloading UsersContorller rails will also unload OpenURI.

When the page is refreshed then UsersController will be loaded and require 'open-uri' will be called. However that require will return false.

Why require returns false

Try the following test case in irb.

step 1

1irb(main):002:0> require 'ostruct'
2=> true

step 2

1irb(main):005:0* Object.send(:remove_const, :OpenStruct)
2=> OpenStruct

step 3 : ensure that OpenStruct is truly removed

1irb(main):006:0> Object.send(:remove_const, :OpenStruct)
2NameError: constant Object::OpenStruct not defined
3        from (irb):6:in `remove_const'
4        from (irb):6:in `send'
5        from (irb):6

step 4

1irb(main):007:0> require 'ostruct'
2=> false

step 5

1irb(main):009:0> OpenStruct.new
2NameError: uninitialized constant OpenStruct
3        from (irb):9

Notice that in the above case in step 4 require returns false. 'require' checks against $LOADED_FEATURES. When OpenStruct was removed then it was not removed from $LOADED_FEATURES and hence ruby thought ostruct is already loaded.

How to get around to this issue.

require loads only once. However load loads every single time. In stead of 'require', 'load' could be used in this case.

1irb(main):001:0> load 'ostruct.rb'
2=> true
3
4irb(main):002:0> OpenStruct.new
5=> #<OpenStruct>

Back to the original problem

In our rails application refresh of the page is failing. To get around to that issue use require_dependency instead of require. require_dependency is a rails thing. Under the hood rails does the same trick we did in the previous step. Rails calls kernel.load to load the constants that would fail if require were used.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.