Trying code blocks in Ruby (aka Prototype’s Try.these)

In: Ruby

28 Jan 2008

Prototype’s Try.these is a really useful bit of code (though I doubt it sees much application outside of JavaScript libraries). I have this evil bit of code somewhere, for example:

var results = Try.these(
  function() { return response.responseText.evalJSON(true); },
  function() { return eval('(' + response.responseText + ')'); }
);

Evilness personified in its evaled glory, but that’s besides the point here.

Just for fun, I tried to do this in Ruby, just to see how easy it was, and came up with this try method:

module Kernel
  def try(*these)
    raise ArgumentError, 'try requires at least 2 arguments' if these.size <= 1
    fallback = these.pop unless these.last.respond_to?(:call)
    these.each { |candidate| begin return candidate.call rescue next end }
    fallback || raise(RuntimeError, 'None of the given procs succeeded')
  end
end

Which you can (ab)use like this:

try(
  Proc.new { open('http://finance.yahoo.com/foo/') },
  lambda { open('http://finance.google.com/bar/') },
  proc { open('http://finantiq.com/flomp/') },
  :fallback
)

Real world applications would be, like the example above, retrieving content from several unreliable websites to retrieve currency rates.

You can find the specs for Kernel#try here: http://pastie.org/144339.

So the question now is, is there a better way to implement this, or a more idiomatic Ruby way?

2 Responses to Trying code blocks in Ruby (aka Prototype’s Try.these)

Avatar

Jan De Poorter

January 29th, 2008 at 4am

What’s wrong with ruby’s native begin/rescue

?

Avatar

Chu Yeow

January 29th, 2008 at 9am

Well, nothing wrong really – this is largely just a convenient wrapper around being/rescue that allows you to pass arbitrary procs around and an easy way to specify a fallback value.