Task

The goal is to get the methods of an object, as names, values or both.

Some languages offer dynamic methods, which in general can only be inspected if a class' public API includes a way of listing them.

# Sample classes for reflection
class Super
  CLASSNAME = 'super'
 
  def initialize(name)
    @name = name
    def self.superOwn
      'super owned'
    end
  end
 
  def to_s
    "Super(#{@name})"
  end
 
  def doSup
    'did super stuff'
  end
 
  def self.superClassStuff
    'did super class stuff'
  end
 
  protected
  def protSup
    "Super's protected"
  end
 
  private
  def privSup
    "Super's private"
  end
end
 
module Other
  def otherStuff
    'did other stuff'
  end
end
 
class Sub < Super
  CLASSNAME = 'sub'
  attr_reader :dynamic
 
  include Other
 
  def initialize(name, *args)
    super(name)
    @rest = args;
    @dynamic = {}
    def self.subOwn
      'sub owned'
    end
  end
 
  def methods(regular=true)
    super + @dynamic.keys
  end
 
  def method_missing(name, *args, &block)
    return super unless @dynamic.member?(name)
    method = @dynamic[name]
    if method.arity > 0
      if method.parameters[0][1] == :self
        args.unshift(self)
      end
      if method.lambda?
        # procs (hence methods) set missing arguments to `nil`, lambdas don't, so extend args explicitly
        args += args + [nil] * [method.arity - args.length, 0].max
        # procs (hence methods) discard extra arguments, lambdas don't, so discard arguments explicitly (unless lambda is variadic)
        if method.parameters[-1][0] != :rest
          args = args[0,method.arity]
        end
      end
      method.call(*args)
    else
      method.call
    end
  end
 
  def public_methods(all=true)
    super + @dynamic.keys
  end
 
  def respond_to?(symbol, include_all=false)
    @dynamic.member?(symbol) || super
  end
 
  def to_s
    "Sub(#{@name})"
  end
 
  def doSub
    'did sub stuff'
  end
 
  def self.subClassStuff
    'did sub class stuff'
  end
 
  protected
  def protSub
    "Sub's protected"
  end
 
  private
  def privSub
    "Sub's private"
  end
end
 
sup = Super.new('sup')
sub = Sub.new('sub', 0, 'I', 'two')
sub.dynamic[:incr] = proc {|i| i+1}
 
p sub.public_methods(false)
#=> [:superOwn, :subOwn, :respond_to?, :method_missing, :to_s, :methods, :public_methods, :dynamic, :doSub, :incr]
 
p sub.methods - Object.methods
#=> [:superOwn, :subOwn, :method_missing, :dynamic, :doSub, :protSub, :otherStuff, :doSup, :protSup, :incr]
 
p sub.public_methods - Object.public_methods
#=> [:superOwn, :subOwn, :method_missing, :dynamic, :doSub, :otherStuff, :doSup, :incr]
 
p sub.methods - sup.methods
#=> [:subOwn, :method_missing, :dynamic, :doSub, :protSub, :otherStuff, :incr]
 
# singleton/eigenclass methods
p sub.methods(false)
#=> [:superOwn, :subOwn, :incr]
p sub.singleton_methods
#=> [:superOwn, :subOwn]