This article targets Rails 3. The information contained in this page might not apply to different versions.
This is article is part of my series Understanding Ruby and Rails. Please see the table of contents for the series to view the list of all posts.
A small-but-interesting feature introduced in Rails 3 is the built-in support for lazy loading.
Lazy loading is a very common design pattern. The concept is to defer initialization of an object until the point at which it is needed. This design pattern decreases the time required by an application to boot by distributing the computation cost during the execution. Also, if a specific feature is never used, the computation won’t be executed at all.
With Rails 3 you can now register specific hooks to be lazy-executed when the corresponding library is loaded.
class ApplicationController < ActionController::Base
initializer "active_record.include_plugins" do
ActiveSupport.on_load(:active_record) do
include MyApp::ActivePlugins
end
end
end
In this case we register the block to be executed when the ActiveRecord library is loaded. If you read the ActiveRecord::Base source code, the very last line is a call to
ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
This line of code executes all the hooks previously registered for ActiveRecord.
lazy-load hooks in the wild
Perhaps one of the most frequent usage of the lazy-load hooks is in Rails plugins.
For example, if your plugin needs to register some helpers, you can write a hook to include the helpers in ActionView only when ActionView is loaded. If the environment is loaded from a rake task (which doesn’t necessary need ActionView), then your plugin hook won’t be executed and the Rails application will boot faster.
Here’s an example from the will_paginate gem.
require 'will_paginate'
require 'will_paginate/collection'
module WillPaginate
class Railtie < Rails::Railtie
initializer "will_paginate.active_record" do |app|
ActiveSupport.on_load :active_record do
require 'will_paginate/finders/active_record'
WillPaginate::Finders::ActiveRecord.enable!
end
end
initializer "will_paginate.action_dispatch" do |app|
ActiveSupport.on_load :action_controller do
ActionDispatch::ShowExceptions.rescue_responses['WillPaginate::InvalidPage'] = :not_found
end
end
initializer "will_paginate.action_view" do |app|
ActiveSupport.on_load :action_view do
require 'will_paginate/view_helpers/action_view'
include WillPaginate::ViewHelpers::ActionView
end
end
end
end
Using lazy load in your libraries
So far, we only discussed about using lazy-loading to hook Rails core library. Because lazy-loading is an ActiveSupport feature, you can use it in your Rails applications but also in your own Ruby classes.
First, make sure to add a call to ActiveSupport.run_load_hooks at the end of your Ruby class.
class HttpClient # ... ActiveSupport.run_load_hooks(:http_client, self) end
Now you can register on_load hooks everywhere passing the name of the library used in run_load_hooks.
class Request
# ...
ActiveSupport.on_load :http_client do
# do something
end
end
class Response
# ...
ActiveSupport.on_load :http_client do
# do something
end
end
Hook context
The run_load_hooks method takes a second parameter representing the context within the hook will be executed. It’s a common pattern to pass the class/instance the hooks refer to.
For instance, the ActiveRecord library mentioned at the beginning of the article passes self. In that context, self references the ActiveRecord::Base class.
This is useful because, if you want to include some custom methods into ActiveRecord, you can use the following block
ActiveSupport.on_load :active_record do include MyPlugin::Extensions end
instead of
ActiveSupport.on_load :active_record do ActiveRecord::Base.send :include, MyPlugin::Extensions end
There’s also an other interesting use case for this feature. If you want to perform some kind of lazy-initialization when an instance of a class is created, just pass the instance itself.
class Color
def initialize(name)
@name = name
ActiveSupport.run_load_hooks(:instance_of_color, self)
end
end
ActiveSupport.on_load :instance_of_color do
puts "The color is #{@name}"
end
Color.new("yellow")
# => "The color is yellow"
Source Code
The source code of the lazy-loading feature is available in the lazy_load_hooks.rb file.


[...] which allows you to lazy load code in initializers. Check out Simone Carletti’s tutorial on Lazy Load Hooks for details on how this works. In short, when action_controller is loaded, our code can be placed [...]
Great info! I have been using lazy loading with Rails Engine & Railtie initializers, but I wasn’t aware that you could register your own hooks with run_load_hooks. Thanks for sharing all the details.
Hi John,
I’m glad you found it useful! :)
It should be:
instead of:
You’re right. Fixed.
Thanks!
Great post, thanks.
By your request, I want to make a correction to your English, which is very understandable.
You have written two lines:
“The run_load_hooks method takes a second parameter representing the context within the hook will be executed. It’s a common pattern to pass the class/instance the hooks refer to.”
Both sentences have issues issues with participles that may be fixed by the same word.
“The run_load_hooks method takes a second parameter representing the context within which the hook will be executed. It’s a common pattern to pass the class/instance to which the hooks refer.”
While grammatically correct now, the sentence structure is a bit “snobby”; it might be better to re-organize them.
You could change just the second sentence to convey the “common pattern” idea: “Passing the hook-referenced class/instance as a second parameter is a common pattern.”, but it is still a choppy construct.
Or, in this context with better flow:
“The run_load_hooks method makes use of a common pattern of taking a second parameter which is used to pass in the hook-referenced class/instance context.”
Or, perhaps this single sentence structure:
“A commonly used pattern of taking a second parameter allows the run_load_hooks method access to the the hook-referenced class/instance context.”
Or, this one:
“We use the common pattern of taking a second parameter to provide the run_load_hooks method access to the the hook-referenced class/instance context.”
Or, perhaps the “common pattern” idea is just too awkward:
“The run_load_hooks method takes a second parameter in order to pass in the hook-referenced class/instance context.”
Or, perhaps:
“The run_load_hooks method takes a second parameter which is used to pass in a class/instance context for the hooks.”
Or, perhaps if we need the “common pattern” idea:
“We use a common pattern to pass in a class/instance context for the hooks; the run_load_hooks method takes a second parameter.”
Or, perhaps better:
“Using a common pattern, the run_load_hooks method takes a second parameter to pass in a class/instance context for the hooks.”
Or, perhaps even more succinct as two sentences:
“The run_load_hooks method takes a second parameter to pass in a class/instance context for the hooks. Passing class/instance context in this manner is a common pattern.”
English constructs are nuanced and can be just nasty, but your command of the language for conveying ideas is to be applauded. Thanks again.
Hi Steven,
thank you very much for taking the time to write this long comment.
I’ll read it carefully and try to assimilate all the information.
Thanks!
Was just wondering, in the last block, should `puts “The color is #{@color}”` be `puts “The color is #{@name}”`?
Cheers
You’re right. Fixed, thank you.
Great explanation, helped me figure out how to hook into another gem that I wanted to do some setup on, from inside my Rails engine initializer blocks :)