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

Pretty neat rick. This comes in handy in irb.

said on

That’s very cool. Like it a lot!

said on

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
said on
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

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
Usage:
irb(main):005:0> how?(", ", [1,2,3]) >> "1, 2, 3" 
[1, 2, 3].*(", ")
=> nil
said on

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

said on

Kian:

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

said on

ruby, work smarter not harder?

said on
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

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

said on

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

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

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

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

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

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

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

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

That’s a good Vrensk!

said on

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

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

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

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

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

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
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

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

said on

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

said on

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

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.