From: Robert Klemme Date: 2005-06-07T23:35:27+09:00 Subject: Re: making a duck Eric Mahurin wrote: > --- Robert Klemme wrote: > >> Eric Mahurin wrote: >>> --- Robert Klemme 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 <> 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_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<