Aleksi writes:
> In [ruby-talk:01697] at
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/~poffice/mail/rub
> y-talk/1697
> gotoken writes 
> 
>   Ruby doesn't have unbounded methods as first class data, 
> 
> which makes me wonder why - it's such a marvellous OO lang 
> with dedication for first-classness.
> 
> Why we have bounded methods, like blocks, but not 
> semibounded, like instance
> methods, nor unbounded, like classmethods?

and answers to himself:

Well, we *can* have objects for instance methods and class methods. The next
snippet contains one routine which should not belong there, but I bundled it
anyway. It's the Kernel#get_named. It takes the *rest argument container as
argument and returns the last contained hash if any. So one can effectively
use it with a definition and call like this

  def varying_number_of_arguments_and_named_parameter(*rest)
    named = get_named(rest)
    p rest
    named.each do |key, value| puts "#{key}=#{value}" end
  end

  varying_number_of_arguments_and_named_parameter(
      'param1', 'param2, 
      'test' => 'with', 'named' => 'arguments' )

Should print:

  ['param1, 'param2']
  test=with
  named=arguments


But now to the point, here's the code that enables one to have a reference
to instance's method and call it like this:

  instanceMethod = InstanceMethod.new(Test.new, :method )
  instanceMethod.call('argument')




class Method  # the calling interface
  def call
    raise "not implemented in abstract base class!"
  end
end

class Block #< Method
  def initialize( block=nil, &bk )
    @block = block if block
    @block = bk if bk
    raise "no block defined" unless defined? @block
  end
  def call(*args)      # associated block should get
    @block.call(*args) # passed, but apparently won't
  end
end

class InstanceMethod #< Method
  def initialize( instance, method )
    @instance = instance
    @method = method
  end
  def call(*args, &blk)
    @instance.send(@method, *args, &blk)
  end
end

# InstanceMethod works for ClassMethods too
# The instance passed to constructor should be 
# object ClassName (which is instance of class Class)
class ClassMethod < InstanceMethod
end

module Kernel
  # three helper methods, for convenience
  def inst_proc( instance, method )
    InstanceMethod.new instance, method
  end
  def class_proc( klass, method )
    ClassMethod.new klass, method
  end
  def block_proc( block=nil, &bk )
    return Block.new(block) if block
    return Block.new(bk) if bk
  end

  def get_named(rest)
    # can make errors
    (rest.size-1).downto(0) do |i|
      return rest.delete_at(i) if rest[i].kind_of? Hash
    end
    return Hash.new
  end
end

################################
################################

And here's the test case (and example):

require 'Method'

class Test
  def initialize(test)
    @test = test
  end
  def Test.test_class_method( arg, *rest, &bl)
    named = get_named(rest)
    puts "Class Method"
    puts arg
    p rest
    named.each do |key, value| puts "#{key}=#{value}" end
    bl.call
  end
  def test_instance_method( arg, opt="optional", &bl)
    puts "Instance method"
    puts @test
    puts arg
    puts opt
    bl.call
  end
  def test_block_method
    return proc { |arg, *rest|
      puts "Block_method"
      puts @test
      puts arg
      p rest
#     yield if iterator?
    }
  end
end

#classMethod = ClassMethod.new(Test, :test_class_method )
classMethod = class_proc(Test, :test_class_method )
classMethod.call('argument', 
                 'rest', 'test', 
                 'test' => 'with', 'named' => 'arguments' ) do
  puts "class method block test"
end
puts

test = Test.new("instance_data")
instanceMethod = InstanceMethod.new(test, :test_instance_method )
instanceMethod.call('argument') do
  puts "instance method block test"
end
puts
inst_proc(test, :test_instance_method).call('argument', 'optional set') do
  puts "instance method block test"
end
puts

test = Test.new("bound instance data")
procInstance = test.test_block_method
block = block_proc( procInstance  )
block.call('argument', 'rest', 'test') do
  puts "'block associated to block'-test would be here"
  puts "but it seems there's no such thing"
end
puts

puts "Block_method: plain associated block"
block = Block.new do puts "here I am" end
block.call




	- Aleksi