Ruby autoload throws error on first mention of constant
I'm trying to set up autoloading for my classes in a new project of mine using Module#autoload. It's almost working - the issue is that on first using a constant to be autoloaded, it errors with "uninitialized constant", but on second usage the constant works as expected.
Code illustrating problem:
init.rb:
# Load lib, other directories will be autoloaded
APPLICATION_ROOT=File.expand_path(File.dirname(__FILE__))
$:.unshift(APPLICATION_ROOT)
Dir.glob("#{APPLICATION_ROOT}/patches/*").each {|p| require p}
Dir.glob("#{APPLICATION_ROOT}/lib/*").each {|p| require p}
# Test autoloading
include Autoload
begin
puts Sprite.new.inspect
rescue
puts "Caught an error"
end
puts Sprite.new.inspect # will not error
patches/string.rb:
class String
def camelize
self.split("_").map{|word| word.cap开发者_Go百科italize}.join
end
end
lib/autoload.rb:
module Autoload
Dir.glob("#{APPLICATION_ROOT}/app/*/*").each do |path|
classname = File.basename(path).gsub(/.rb$/,'').camelize
autoload classname.to_sym, path
end
end
app/models/sprite.rb:
puts "Sprite Required!"
class Sprite
puts "Sprite Defining!"
def initialize
puts "Sprite Initialized!"
end
end
puts "Sprite Defined!"
The output:
Sprite Required!
Sprite Defining!
Sprite Defined!
Caught an error
Sprite Initialized!
#<Sprite:0x000000024ee920>
How can I obtain my desired behavior (no initial error)?
The problem is you are calling autoload in the scope of the module Autoload. In a case like that what ruby is expecting or creating is an autoload for the symbol Autoload::Sprite, when what you want is just Sprite.
Fortunately the fix is simple:
module Autoload
def self.included(mod)
# ...
# Call autoload on the scope of the includer
mod.autoload ...
end
end
Or you could also explicitly call autoload on Object, since that's where your "target scope" for the autoloaded classes most likely is:
Object.autoload ...
加载中,请稍侯......
精彩评论