How do I lazy evaluate yet-to-be-declared classes in Ruby?
Or, rather, how do I do it perfectly?
This is what I came up with so far:
# A double that stands in for a yet-to-be-defined class. Otherwise # known as "lazy evaluation." # # Idea lifted from: # http://github.com/soveran/ohm/ class Double < BasicObject def initialize(name) @name = name end def to_s @name.to_s end alias_method :inspect, :to_s def method_missing(mth, *args, &block) @unwrapped ? super : @unwrapped = true ::Kernel.const_get(@name).send(mth, *args, &block) ensure @unwrapped = false end; private :method_missing end
foo = Double(:Foo) # Now we can safely pass around Foo without # having initialised it. foo.class # Uninitialised constant # That's expected because Foo doesn't exist yet! class Foo; end # So there, we shoo it into existence. foo.class # Foo # foo indeed is Foo. The sleight of hand of works.
This is what I can't get to work:
inst = Foo.new inst.is_a? Foo # true, of course inst.is_a? foo # TypeError: class or module required
Why won't the double stand in for Foo in the last line?
There's nothing wrong with your code - that's expected behavior. The #is_a? method expects a class or module. Try it with build-in classes and you get the same error:
str = "a string" str.is_a? String => true other_str = "another string" str.is_a? other_str => TypeError: class or module required
If you want to change that you'll have to override is_a? (wouldn't recommend that). More likely, you want to do something like this:
str.is_a? other_str.class => true
If you want foo to be the class Foo:
foo = Foo inst = Foo.new inst.is_a? foo #=> true inst2 = foo.new inst2.is_a? Foo #=> true
What if you defined the behaviors you want in the class in a module instead?
Or perhaps wrapping the class in a module?