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 ...
精彩评论