Ruby Techniques Revealed: Autoload
There's plenty of stuff in Ruby that I've either not noticed before, noticed but forgotten about, or otherwise failed to realize the utility of. Add to that all the awesome Ruby tricks and techniques I'm seeing in people's code over on Github lately and.. we need a new series here: Ruby Techniques Revealed!
Disclaimer: I'm not promising you won't already know about anything I "reveal." A lot of you are far better than me at knowing all of Ruby's dark corners. I'm just going to shine a spotlight in the direction of things I don't see used very often that I think are cool.
Autoload - Load stuff only when it's needed
I was going through the source for Thin and noticed that instead of using require
, Marc-Andre Cournoyer was using a method called autoload
to load thin's constituent parts. Specifically:
autoload :Command, 'thin/command' autoload :Connection, 'thin/connection' autoload :Daemonizable, 'thin/daemonizing' autoload :Logging, 'thin/logging'
This intrigued me so I hit Google and came across a little demo of autoload by Mike Subelsky. autoload
works in a similar way to require
, but it only loads the file specified when a constant of your choosing is accessed/used for the first time. This constant would usually be the name of the class provided by the other source file (but it doesn't have to be, as Mike demonstrates).
Let's say you have a file called mylibrary.rb
containing the following code:
puts "I was loaded!" class MyLibrary end
Now, from irb
, let's use require
to load the "library":
irb(main):001:0> require 'mylibrary' I was loaded! => true
Because require
works straight away, the puts
method gets executed immediately. So let's restart irb
and see what happens if we use autoload
instead:
irb(main):001:0> autoload :MyLibrary, 'mylibrary' => nil irb(main):002:0> MyLibrary.new I was loaded! => #<MyLibrary:0x0b1jef>
The puts
method doesn't get executed until we try and use MyLibrary
. autoload
has prevented the loading of mylibrary.rb
until we really needed it.
Detecting Autoloadable Classes
If you want to detect whether a certain class is awaiting autoloading, you can check, like so:
autoload :X, 'x' puts "X is awaiting loading" if autoload?(:X)
Just beware ye threads..
Further Googling led me to some interesting discussions about autoload
. Back in December 2008, a whole group of Ruby luminaries got into a discussion about autoload's issues with threads. Turns out that even though several projects have been adopting autoload in anger recently, there are some concerns surrounding how thread safe autoload is. The proposed solution is to have a mutex between all autoloads, but I haven't yet established how far work on this (if any) has gone.
There was a similar discussion back in early 2008, started by Yehuda Katz (of Merb fame) that I'd advise you read if threading is going to be a big deal to you. He seems to imply that big issues only crop up under heavy concurrency situations, but the problem is there regardless. There seem to be similar issues around require
too though so I'll be taking the "cross that bridge when I get to it" approach.. ;-)
March 19, 2009 at 3:23 pm
The AutoCode project, created by Dan Yoder, attempts to provide a more flexible suite of autoload-like methods.
auto_load :Command, :directories => 'thin'
auto_load true, :directories => [ 'thin' ]
auto_load /[a-zA-Z]+Controller/, :directories => [ 'controllers' ]
auto_create_class true, :Base # creates any missing constant as a child of ThisNamespace::Base
Most of the development of AutoCode is currently happening at http://github.com/dyoder/autocode/tree/master
I recently nudged AutoCode a few steps closer to thread safety. Comments and criticisms welcome.
March 20, 2009 at 2:24 am
I wonder how this code relates to Rails on the fly const_missing auto requiring. For instance, looking at Matthew King's example, I wonder if things like auto_load /.*/, :directories => 'models' could be the implementation of loading the models dynamically.
March 20, 2009 at 3:48 am
What if you want to autoload 'Foo::MyLibrary' vs 'Bar::MyLibrary'? It doesn't seem to like that.
March 20, 2009 at 4:30 am
Riche: I haven't looked myself, but one of the comments in the links above says that Rails has been starting to use autoload a lot recently.
March 20, 2009 at 10:57 am
@Daniel Berger:
One can autoload 'Foo::MyLibrary' and 'Bar::MyLibrary' as follows.
# foo.rb
module Foo
class MyLibrary
def initialize
print "autoload"
end
end
end
# bar.rb:
module Bar
class MyLibrary
def initialize
puts " works"
end
end
end
# main program
module Foo
autoload :MyLibrary, 'foo'
end
module Bar
autoload :MyLibrary, 'bar'
end
Foo::MyLibrary.new
Bar::MyLibrary.new
March 21, 2009 at 3:10 am
@Jan, thanks. I think autoload should be smarter about scoped classes, though.
March 24, 2009 at 8:11 am
If you're *really* paranoid about thread safety....
Here's a brutal but effective solution...
ObjectSpace.each_object(Module) { |m| m.constants.each { |c| m.const_get c } }
March 25, 2009 at 4:50 pm
@2
Autocode was developed to provide Waves with the ability to dynamically load default models (which can be user defined) based on the tables present in a database. What you are saying is not necessarily the same as that, but with Autocode would probably look something like that and should be relatively easy to implement.