How it works with activerecord is that the relation is a wrapper around the array, delegating any undefined method to this internal array (called target
). So what you need is to start with a BasicObject instead of Object:
class Foo < BasicObject
then you need to create internal variable, to which you will delegate all the methods:
def method_missing(*args, &block)
reload! unless loaded?
@target.send(*args, &block)
end
def reload!
# your logic to populate target, e.g:
@target = @counter
@loaded = true
end
def loaded?
!!@loaded
end
To chain methods, your methods need to return new instance of your class, e.g:
def initialize(counter=0)
@counter = counter
end
def bar
_class.new(@counter + 1)
end
private
# BasicObject does not define class method. If you want to wrap your target
# completely (like ActiveRecord does before rails 4), you want to delegate it
# to @target as well. Still you need to access the instance class to create
# new instances. That's the way (if there are any suggestion how to improve it,
# please comment!)
def _class
(class << self; self end).superclass
end
Now you can check it in action:
p Foo.new.bar.bar.bar #=> 3
(f = Foo.new) && nil # '&& nil' added to prevent execution of inspect
# object in the console , as it will force @target
# to be loaded
f.loaded? #=> false
puts f #=> 0
f.loaded? #=> true