Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

Ruby Techniques Revealed: Autoload

By Peter Cooper / March 19, 2009

monster-with-torch.jpgThere'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.. ;-)

Comments

  1. Matthew King says:

    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.

  2. Richie Vos says:

    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.

  3. Daniel Berger says:

    What if you want to autoload 'Foo::MyLibrary' vs 'Bar::MyLibrary'? It doesn't seem to like that.

  4. Peter Cooper says:

    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.

  5. Jan Wedekind says:

    @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

  6. Daniel Berger says:

    @Jan, thanks. I think autoload should be smarter about scoped classes, though.

  7. raggi says:

    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 } }

  8. ab5tract says:

    @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.

Other Posts to Enjoy

Twitter Mentions