> From: Rasputin [mailto:rasputin / idoru.mine.nu]
> It seemed like you were more interested in a way to know how an API
> will use your object. Why not send in a probe?
>
> Pass a custom object in that has a method_missing? method that does
> a ludicrous amount of debugging of how its called. That would tell you
> how an API is actually using your object, what methods it calls and how.

Below is a quick hack to do that. Needs work. Needs redirection of IO. But
its kinda cute.. Has issues with side effects, of course, and printing
crapola all over the place.

# Usage:
# ins = ObjectProbe::Inspector.new( SomeObject.new )
# ins.probe
module ObjectProbe

  class Inspector
    def initialize( object )
      @object = object
    end

    def target_class
      @object.class
    end

    def target_methods
      @object.methods - Object.new.methods
    end

    def probe
      puts "Probing: #{target_class}"
      target_methods.each do | method |
        meta = MetaMethod.new( method.intern, @object )
        print "  Method: #{method}( "
        meta.args.each do | arg |
          print "[ #{arg.___requires.join(', ')} ], "
        end
        puts
      end
    end
  end

  class MetaMethod
    def initialize( method, object )
      @method = method
      @object = object
    end

    def symbol
      @method
    end

    def arity
      m = @object.method( @method )
      m.arity
    end

    def args
      result = []
      arity.times { result << DummyArgument.new }
      begin
        @object.send( @method, *result )
      rescue
        puts $!.to_s
      end
      result
    end
  end

  class DummyArgument
    attr_reader :___requires

    def initialize
      @___requires = []
    end

    def DummyArgument.clean_out_methods
      methods_list = Object.new.methods
      methods_list.each do | m |
        meth = m.intern
        # keep the important fns, and keep __send__ and __id__ to prevent
warnings.
        next if( meth == :method_missing || meth == :__send__ || meth ==
:__id__ )
        module_eval <<-"end_eval"
          alias_method :__#{meth.to_i}__, #{meth.inspect}
          def #{meth.id2name}(*args, &block)
            method_missing( :#{meth}, *args )
            __#{meth.to_i}__(*args, &block)
          end
        end_eval
      end
    end

    def method_missing( symbol, *args )
      @___requires << symbol
    end

    clean_out_methods
  end
end