From: Mark Hubbart Date: 2005-02-19T10:04:37+09:00 Subject: Re: adding a dynamic method handler? (long post) On Fri, 18 Feb 2005 17:39:47 +0900, Robert Klemme wrote: > > "Mark Hubbart" schrieb im Newsbeitrag > news:de63abca050217095240cde47c@mail.gmail.com... > > On Thu, 17 Feb 2005 17:19:54 +0900, Robert Klemme > wrote: > > > > > > "Mark Hubbart" schrieb im Newsbeitrag > > > news:de63abca050216125273686a45@mail.gmail.com... > > > > On Wed, 16 Feb 2005 18:19:50 +0900, Robert Klemme > > > wrote: > > > > > > > > > > "Mark Hubbart" schrieb im Newsbeitrag > > > > > news:de63abca0502152303763354f7@mail.gmail.com... > > > > > > Hi, > > > > > > > > > > > > I've been using method_missing overly much in my code lately, > and > > > it's > > > > > > prompted me to think a lot about it's limitations. > > > > > > > > > > > > > > > > > > > > > So, any thoughts? > > > > > > > > > > I'm wondering in which situation you need this. Although I > understand > > > the > > > > > benefits of your approach I don't see the use case for this. > > > > > > > > Basically this is for any time that you want the code re-use and > ease > > > > of implementation afforded by method_missing, but the benefits of > > > > still having the methods behave mostly as if they were actually > > > > defined, rather than handled dynamically. This is useful for quickly > > > > defining wrapper objects, or objects that delegate to multiple other > > > > objects. > > > > > > > > The idea is that this would be a way of dynamically creating methods > > > > for an object, without resorting to relatively permanent methods > like > > > > "class << self; define_method(:foo){...}; end". > > > > > > I'm sorry if I am being stubborn (or dump), but this is still pretty > much > > > abstract. I'd like to know which concrete use case made this behavior > > > necessary. > > > > Maybe stubborn, but that's not always a bad thing. There are many > > things I'm very glad Matz is stubborn about :) > > > > I don't think I ever implied that this behavior was *necessary*. You > > can implement similar functionality with what is currently available. > > It's just a pain in the neck to do it; You either have to break down > > and def a bunch of methods, or override respond_to? and method in your > > class. And sometimes neither of those is the *best* solution. > > > > I did give a specific use case where it would be very useful, though. > > When wrapping a class, or doing runtime refactoring (like what > > pathname does, which is, for the most part, a refactored wrapper for > > File and Dir), this could be very handy. Like method_missing, it would > > allow you to handle large amounts of similar methods at once, letting > > you condense code; while still getting almost all the benefits of > > actually defining each individual method. > > > > class DirectoryItem > > def initialize(path) > > @path = path > > end > > [...] > > def dynamic_method(name) > > @@file_methods = (File.methods - Object.methods).map{|s|s.to_sym} > > @@dir_methods = (Dir.methods - Object.methods).map{|s|s.to_sym} > > if @@file_methods.include? name > > if File.method(name).arity == 1 > > lambda{ File.send(name, @path) } > > elsif name == :truncate > > lambda{|len| File.send(name, @path, len) } > > elsif File.method(name).arity == 2 > > lambda{|path| File.send(name, @path, path) } > > end > > elsif @@dir_methods.include? name > > # handle Dir methods here > > end > > end > > end > > > > The equivalent portion of code using 'def' would be much, much longer, > > and very repetitive. The equivalent code using method_missing would be > > about the same length, but if anyone tried to check it's capabilities, > > it would seem to almost be an empty object. > > Although I agree to that - wouldn't it be more efficient to define all > forwarding methods in DirectoryItem class once and for all? That way you > get these benefits: > > - faster as methods can be invoked directly and no lambdas have to be > created > - reduced mem usage as not every instance has its own copy of the lambdas > > Drawback is of course that methods added to File and Dir later don't get > invoked - but it's unlikely in this case I'd say. > > Also, a suggestion for improvement: wrap dynamic_method with a method > similar to this, which will reduce the number of created lambdas: > > # untested > def dyn_create(name) > m = dynamic_method(name) > class< define_method(name.to_sym,*a,&m) > end > end > > Then invoke this method via respond_to?, method_missing and method. Then > the method is defined on first access. What do you think? That's an interesting way of doing it. I suppose if at a later time the method needs to be modified, you could always undef it and let the dyn_create method catch it the next time through. On the other hand, the more I think about this, the more complicated it seems to get. I'm not really as gung ho on the idea as when I first thought of it. Maybe if it was worked into the syntax somehow, it would be worthwhile; but I wouldn't know where to start. Perhaps a better way to go would be to make it easier to wrap respond_to?() and methods(). Which is coming in 2.0 anyway :) cheers, Mark