Eric Mahurin wrote:
> --- Robert Klemme <bob.news / gmx.net> wrote:
>
>> Eric Mahurin wrote:
>>> --- Robert Klemme <bob.news / gmx.net> wrote:
>>>
>>>> Eric Mahurin wrote:
>>>>> Regarding duck-typing... Is there an easy way make a
>>>> "duck"?
>>>>> i.e. an object that responds to enough methods to be an
>>>>> acceptable argument to a certain methods.  For example, if I
>>>>> have a method that takes aString and uses the #size and
>>>> #[i]
>>>>> methods, I could pass it something that looked just enough like
>>>>> a String to work:
>>>>>
>>>>> alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
>>>>
>>>> Why aren't you satisfied with
>>>>
>>>> class Alpha
>>>>   def [](i) ?a+i end
>>>>   def size() 26 end
>>>> end
>>>> alphabet = Alpha.new
>>>>
>>>> or
>>>>
>>>> alphabet = Object.new
>>>> def alphabet.[](i) ?a+i end
>>>> def alphabet.size() 26 end
>>>>
>>>> You can even squeeze that on one line if you feel the need
>>>> for it.  I
>>>> mean, you don't generate those methods dynamically or get
>>>> them from
>>>> somewhere else so why not just use the std approach?
>>>
>>> For the example I gave above, I think you are correct.  The
>>> examples I gave in response to David Black are probably
>> better
>>> ones.  With those, a simple "def" won't cut it.  You need
>>> define_method.  But, using define_method is cumbersome from
>> an
>>> object because you first need to make it have a singleton
>>> class, then use "send" to access it from that class because
>> it
>>> it a private method.  Another solution to the problem of
>>> "making a duck" would be to have a
>>> Object#define_singleton_method:
>>>
>>> class Object
>>>     def define_singleton_method(name,&block)
>>>         klass = (class << self;self;end)
>>>         klass.send(:define_method,name,&block)
>>>         self
>>>     end
>>> end
>>
>> Btw, you can also use class eval:
>>
>> class Object
>>     def define_singleton_method(name,&block)
>>         klass = (class << self;self;end).class_eval do
>>           define_method(name,&block)
>>         end
>>         self
>>     end
>> end
>>
>> and also
>>
>> o=Object.new
>> class <<o;self;end.class_eval do
>>   def bax() "bar" end
>> end
>> o.bax
>>
>>> Then, for example, you could do this to make a set be
>> useful
>>> for a method that uses == for comparison:
>>>
>>> seteq = Object.new.
>>>     define_singleton_method(:==,&set.method(:include?))
>>
>> This does not work.  You cannot transfer a method from one
>> class to
>> another:
>>
>> 09:14:01 [ruby]: cat define.rb
>>
>> class Foo
>>   def test() bar() end
>>   def bar() "FOO::BAR" end
>> end
>>
>> class Bar
>>   def bar() "BAR::BAR" end
>> end
>>
>> bar = Bar.new
>> bar_kl = (class<<bar;self;end)
>> bar_kl.send(:define_method, :xxx, Foo.new.method(:test))
>> bar_kl.send(:public, :xxx)
>>
>> bar.xxx()
>> 09:15:33 [ruby]: ruby define.rb
>> define.rb:16:in `xxx': bind argument must be an instance of
>> Foo
>> (TypeError)
>>         from define.rb:16
>
> If you put an & in front of your "Foo.new.method(:test)" (like
> what I did), or .to_proc it will work fine.

Darn, though I had the & in there.  However, there's another problem which
I originally wanted to demonstrate with this setup: methods might not
behave as one would expect:

16:31:16 [ruby]: cat define.rb

class Foo
  def test() bar() end
  def bar() "FOO::BAR" end
end

class Bar
  def bar() "BAR::BAR" end
end

bar = Bar.new
bar_kl = (class<<bar;self;end)
bar_kl.send(:define_method, :xxx, &Foo.new.method(:test))
bar_kl.send(:public, :xxx)

p bar.xxx()
16:31:16 [ruby]: ruby define.rb
"FOO::BAR"

Foo::test is still bound to Foo::bar and you probably would rather see
Bar::bar being called.

Kind regards

    robert