hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Stick it in Your ~/.irbrc: MethodFinder #

by why in inspect

Ooh, nice ripoff from Smalltalk. A class called MethodFinder for looking up methods based on what they should return.

You use MethodFinder.show( "hello", 5 ) and it’ll suggest the length and size methods. Maybe we could hack it to be a bit more terse?

 >> "hello".what?  5
 "hello".length  5
 “hello”.size == 5
 => [“length”, “size”]

 >> “foo”.what?(“bar”)  "foobar" 
 "foo".<<("bar")  “foobar” 
 “foo”.+(“bar”)  "foobar" 
 "foo".concat("bar")  “foobar” 
 => [“<<”, ”+”, “concat”]

Just add to Andrew’s code:

 class MethodFinder
   def initialize( obj, *args )
     @obj = obj
     @args = args
   end
   def ==( val )
     MethodFinder.show( @obj, val, *@args )
   end
 end

 class Object
   def what?(*a)
     MethodFinder.new(self, *a)
   end
 end

In any case, use the syntax easiest to remember. Lest we need a whatwhat?

said on 16 May 2006 at 16:20

Pretty neat rick. This comes in handy in irb.

said on 16 May 2006 at 16:26

That’s very cool. Like it a lot!

said on 16 May 2006 at 16:43

Medallions go to you, Andrew. I would also encourage you to suppress the warnings by temporarily redirecting $stderr

  require 'stringio'
  # Pretty-prints the results of the previous method
  def MethodFinder.show( anObject, expectedResult, *args )
    $old_stderr = $stderr; $stderr = StringIO.new
    methods =
      find( anObject, expectedResult, *args ).each { |name|
        print "#{anObject.inspect}.#{name}" 
        print "(" + args.map { |o| o.inspect }.join(", ") + ")" unless args.empty?
        puts " == #{expectedResult.inspect}" 
      }
    $stderr = $old_stderr
    methods
  end
said on 16 May 2006 at 16:50
said on 16 May 2006 at 20:01
Very nice. Sort of like my commonly used:
class Object
  def method_like(regex)
    methods.select {|m| m =~ regex}
  end
end
(Although I actually abbreviate it to #mlike for faster typing)
said on 16 May 2006 at 21:00

Very handy. I start liking ruby :)

But what if you don’t know the order of the parameters? I used the permutation class to make it work for each order of the parameters. here

said on 16 May 2006 at 21:09
Usage:
irb(main):005:0> how?(", ", [1,2,3]) >> "1, 2, 3" 
[1, 2, 3].*(", ")
=> nil
said on 17 May 2006 at 00:54

Kian: class Object def methods_like(regexp) methods.grep(regexp) end end

said on 17 May 2006 at 00:55

Kian:

class Object
  def methods_like(regexp)
    methods.grep(regexp)
  end
end

said on 17 May 2006 at 01:28

ruby, work smarter not harder?

said on 17 May 2006 at 02:52
To save some time: Windows lacks a fork routine, which gets me a:
NotImplementedError: the fork() function is unimplemented on this machine

for anything I try

said on 17 May 2006 at 03:52

call me a thicky – but what do i do with this code ?

said on 17 May 2006 at 04:06

Hrm.


mathie@laphroaig:mathie$ irb
irb(main):001:0> "foo".what?("bar") == "foobar" 
LoadError: no such file to load -- bar
        from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__'
        from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require'
        from /Users/mathie/.irbrc:43:in `find'
        from /Users/mathie/.irbrc:42:in `find'
        from /Users/mathie/.irbrc:51:in `show'
        from /Users/mathie/.irbrc:37:in `=='
        from (irb):1

Looks like it’s an interaction with the require 'rubygems' at the top of my ~/.irbrc.

said on 17 May 2006 at 07:09

So this is all fine and dandy in your typical irb session, but when I try it in the Rails console I get:

  >> "hello".which? == 5
  $

i.e., irb immediately aborts, all error messages going down with the process.

Any ideas before I start debugging?

said on 17 May 2006 at 07:10

So this is all fine and dandy in your typical irb session, but when I try it in the Rails console I get:

  >> "hello".which? == 5
  $

i.e., irb immediately aborts, all error messages going down with the process.

Any ideas before I start debugging?

said on 17 May 2006 at 07:50

I am seeing have the same trouble as reported by Jim and by mathie.

I am on Windows though not sure that matters.

said on 17 May 2006 at 08:51

Jim, mathie, Brian: That’s really odd. Looks like mathie’s on OSX , though, so it’s not platform-specific. I just tested on Windows and it worked fine like double donkeys. I think we need to catch exceptions and put together a whitelist of desctructive methods (ignoring bangs.)

said on 17 May 2006 at 09:17

My problem turned out to be the Rails “daemonize” method—my irb session wasn’t dying, just slipping into the background.

Adding a @@black_list and a select on not @@black_list.include? name in MethodFinder#find fixed it.

said on 17 May 2006 at 11:48

J`ey: Thanks. Can’t believe I haven’t done it that way all this time… I always forget to look at the Enumerable API when dealing with arrays. Really wish they’d just include those methods on the Array API page!

said on 17 May 2006 at 14:15

Wow! I found this just after I spent a few hours (or so it felt) searching for the #camelize method (that turns “dragon_controller” to “DragonController”). #what? would have saved those hours—or so I thought.

It turns out that

  "dragon_controller".what? == "DragonController" 

does not return #camelize since #camelize has an optional first argument (capiltalize_first = true). So it has an arity of -1. So here’s my suggestion:

  def self.find( anObject, expectedResult, *args )
    $old_stderr = $stderr; $stderr = StringIO.new
    targetArity = [args.size, - args.size - 1]
    methods = anObject.methods.select do |name|
      targetArity.include?(anObject.method(name).arity) &&
      ! @@blacklist.include?(name) &&
      begin anObject.megaClone.method( name ).call(*args) == expectedResult; rescue; end
    end
    $stderr = $old_stderr
    methods
  end

  @@blacklist = %w(daemonize what? display)

Note that ‘what?’ must be in the @@blacklist or we will get eternal recursion. Oh, and I moved the STDERR stuff to the find method since that is where the errors are printed.

I’ll try to work the permutations in there too. But again: wow!

said on 17 May 2006 at 14:23

That’s a good Vrensk!

said on 17 May 2006 at 15:24

I streamlined the code, and changed it to use my preferred coding style (change it back if you want to). I hope it’s ok with Andrew that I post it here; I couldn’t comment on his own entry.

class Object
  def mega_clone
    self.clone
  rescue
    self
  end
end
class MethodFinder
  # Find all methods on obj which, when called with args
  # return expected_result
  def self.find(obj, expected_result, args)
    obj.methods.select do |name|
      obj.method(name).arity == args.size and
      obj.mega_clone.send(name, args) == expected_result rescue nil
    end
  end
end
  1. Pretty-prints the results of the previous method def self.show(obj, expected_result, args) find(obj, expected_result, args).each do |name| print ”#{obj.inspect}.#{name}” print “(#{args.map{|arg| arg.inspect }.join(’, ‘)})” unless args.empty? puts ” == #{expected_result.inspect}” end end

Just add the #what? method yourself.

By the way, is there a better name for #mega_clone ? It sounds kind of odd…

said on 17 May 2006 at 15:27

Hmmm, it seems your preview function ain’t working—trying again:

I streamlined the code, and changed it to use my preferred coding style (change it back if you want to). I hope it’s ok with Andrew that I post it here; I couldn’t comment on his own entry.


  class Object
    def mega_clone
      self.clone
    rescue
      self
    end
  end

  class MethodFinder
    # Find all methods on +obj+ which, when called with +args+
    # return +expected_result+
    def self.find(obj, expected_result, *args)
      obj.methods.select do |name|
        obj.method(name).arity == args.size and
        obj.mega_clone.send(name, *args) == expected_result rescue nil
      end
    end

    # Pretty-prints the results of the previous method
    def self.show(obj, expected_result, *args)
      find(obj, expected_result, *args).each do |name|
        print "#{obj.inspect}.#{name}" 
        print "(#{args.map{|arg| arg.inspect }.join(', ')})" unless args.empty?
        puts " == #{expected_result.inspect}" 
      end
    end
  end

Just add the #what? method yourself.

By the way, is there a better name for #mega_clone ? It sounds kind of odd…

said on 17 May 2006 at 18:51

Daniel: Maybe just #clone? Given that you can clone frozen objects, being unable to clone numbers seems odd to me. Even if you actually get a new object, you still can’t change it so it might as well be the old object. Maybe “#clone_if_mutable”? “#clone_or_self”?

OTOH , I just realized that the code as stands could be rather dangerous if the clonee was (a) mutable but (b) raised an exception in #clone. Time to start checking for TypeError in particular?

said on 17 May 2006 at 19:26

Forgive me for not being precise, am a nuby. On Windows, I had originally tried under script/console, which is where I spend more time, and where I get the fork error. On IRB it works just fine, warning about Object#type & Object#id being deprecated.

Brian, did you make the same mistake?

Non nubies: what could I do to make it work in the script/console mode?

said on 18 May 2006 at 07:29

llasram: yeah, I added the TypeError restriction to the rescue clause after posting here.

class Object
  alias_method :__clone__, :clone
  def clone
    __clone__
  rescue TypeError
    self
  end
end
said on 18 May 2006 at 07:46

Okay, I’ve boiled it down even further. I’ve removed the output method (I don’t really need it) and I’ve used a block to specify the output requirements. Remember to use the #clone version from my earlier post.


  class Object
    def find_methods(*args)
      method.select do |name|
        begin
          method(name).arity == args.size and
          yield clone.send(name, *args)
        rescue NameError, TypeError
          nil
        end
      end
    end
  end

which reminds me; why no rescue clauses on do … end blocks?

said on 18 May 2006 at 19:00
I’ve posted my own improvements It fixes the following:
  • Missing support for varargs and blocks
  • Failed to catch some things
  • Output from printing methods is annoying
I hadn’t looked at the comments here (just the main post), but I believe I coincidentally fixed the following:
  • Supression of warnings
  • Fork error
  • Optional arguments

My recursion prevention hack isn’t as elegant as Vrensk’s :(

said on 19 May 2006 at 01:53

is there a version of the .irbrc file that includes all these improvements ?

said on 19 May 2006 at 19:22

I’ve made a combo version and included some credits.

said on 20 May 2006 at 23:55

FYI : Steven Grady posted MethodFinder on ruby-talk in 2002.

Based on all people’s programs, I come out mine versions like: (1) simple MethodFinder

(2) permutation arguments MethodFinder

said on 24 May 2006 at 15:39

Now to integrate this with rspec and koders.com. I get direct deposit, so all I need now is a hologram of me typing madly. Hmm, maybe as long as CVS commits keep happening, a cardboard version will work just as well.

Comments are closed for this entry.