开发者

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜