Dynamic method execution problem

All,

I have been trying to figure out a way to execute a series of methods,
only 1 of which is expected to actually run without exception, in a
particular order. I haven't figured out how to use send or Method#call,
so I was messing with eval, but not with much success (I have problems
with argument passing, calling the right method, and so on.)

I have been trying this, but it doesn't quite work. (As background
information, I'm using the Watir test library, and I want to interact
with a web page that may have 1 of 4 different elements, each of which
I have methods to access and use.)

# Each item in this array is a different method name (I changed the
names to
# "methodn" from more useful names for the purposes of this question)
methods = [
  "method1",
  "method2",
  "method3",
  "method4"
]

# The ie variable here refers to a Watir::IE object
begin
  my_method = methods.shift
  puts "Trying #{my_method}"
  eval "#{my_method}(ie)"
end

rescue UnknownObjectException
  if methods.length > 0
    retry
  else
    raise
  end

Any suggestions? I'm new to Ruby, so perhaps there's a totally
different approach that is the standard way to address this. (I was
trying stuff inspired from PickAxe 2nd Ed, pg.112 and pg.407-409).

Thanks in advance!

Joe

<snip definition of methods array>

# The ie variable here refers to a Watir::IE object
begin
  my_method = methods.shift
  puts "Trying #{my_method}"
  eval "#{my_method}(ie)"
end

rescue UnknownObjectException
  if methods.length > 0
    retry
  else
    raise
  end

First, a fix... you don't want the end to the begin block until after
the rescue, example:

begin
  # do risky operation
rescue
  # recover from failure
end

Then, translating to use send instead of eval:

begin
  my_method = methods.shift
  puts "Trying #{my_method}"
  send(my_method.to_sym, ie)
rescue UnknownObjectException
  retry unless methods.empty?
  raise
end

(I also changed the content of the rescue clause to something that
flows a bit nicer for me, but this is just a style issue. What you had
works fine)

The send method sends the method identified by the first argument to
the receiver (here the implicit self) with the remainder of the
arguments.

Myself, rather than using retry, I'd do something like this:

methods = [ :method1, :method2, ... ]

methods.find do |method|
  puts "Trying #{method}"
  begin
    send( method, ie )
    method
  rescue
  end
end or raise "No applicable method found"

This makes the nature of looping through the methods more explicit,
rather than relying on a combination of .shift, .empty? and retry.

Jacob Fugal

···

On 9/9/05, joe.yakich@gmail.com <joe.yakich@gmail.com> wrote:

Jacob,

Wow, that's much cleaner and clearer. It even works, too. :slight_smile:

Thank you!

Joe

[snip]
begin
  my_method = methods.shift
  puts "Trying #{my_method}"
  send(my_method.to_sym, ie)
rescue UnknownObjectException
  retry unless methods.empty?
  raise
end

You don't need to convert my_method to a symbol explicitly. Just pass
it on, and send will handle it on its own. That way you don't take on
responsibility for argument checking of send and if somehow send is
changed to take something that can not be represented as a symbol -
and your code gets feeded with it - the code will continue to work.
The send example may be silly because it's unlikely to change, but I
think it is a general guideline not to assume too much about the
called methods but let the called methods figure it out for
themeselves. Also I don't think this is opposed to "fail early" but
that would be open to some discussion by people more intelligent than
me.

best regards,

Brian

···

[snip]

Jacob Fugal

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/