Ruby style question from a newby: exceptions

Hi,

Any hints on the best way to catch exceptions from the point of view of
good style for the following?

···

---
puts "Create connection..."
imap = IMAP.new('sslmail.somewhere.com', 993, true)
puts "Capabilities: cap = #{imap.capability().join(" ")}"
puts "Authenticate..."
logged_in = false

begin
  imap.authenticate('CRAM-MD5', 'myname', 'secret')
  logged_in = true
rescue Net::IMAP::NoResponseError => e
  puts " Failed, #{e}"
end

puts "Try Login..."
if ! logged_in then
  begin
    imap.login('myname', 'secret')
    logged_in = true
  rescue
    puts " Failed, #{e}"
  end
end
---

What am I trying to do and what don't I like? Basically I want to try a
sequence of things until one of them works --- first I want to use the
authenticate method and if that fails to use the login method. Each of
these methods indicates failure by raising an exception. I want to log
each failure (because I'm experimenting with the library at this
stage). In this example I want to log each failure and on failure I
want to cacade to an alternative strategy.

How could this code be better written so that it is easier to read and
understand the flow of the code? Would it be possible to achieve
something like the following?

---
exceptions = until_success \
  { imap.authenticate ... },
  { imap.login ... }

if exceptions.failed then
  puts "Unable to login: #{exceptions.join('\n')}"
else
  puts "Login successfull. Record of unsuccessfull attempts: \n \
#{exceptions.join("\n ")}"
end
---

with a function until_success taking a sequence of blocks as arguments?

regards,

Richard.

Is the following irb session informative?

    irb(main):013:0> c = 0
    irb(main):014:0> begin
    irb(main):015:1* p c
    irb(main):016:1> raise
    irb(main):017:1> rescue
    irb(main):018:1> c += 1
    irb(main):019:1> retry if c < 5
    irb(main):020:1> end
    1
    2
    3
    4
    => nil

···

--
kjana@dm4lab.to November 17, 2005
Better late than never.

YANAGAWA Kazuhisa wrote:

    irb(main):019:1> retry if c < 5

Hi Richard,

Here's a variation on that theme:

···

#--------------------------------------------------------

METHOD_Q = [ [:aaa, TypeError],
             [:bbb, NoMethodError],
             [:ccc, ArgumentError],
             [:ddd, NameError],
           ].freeze

def log(item); puts "LOG: #{item}"; end

def aaa; 11 + 'Two Two'; end
def bbb; 12.sort; end
def ccc; .each(13) {}; end
def ddd
  puts "4th attempt"
  # NOCONST # <--- uncomment
end

catch(:done) do
  stack = METHOD_Q.dup
  begin
    meth, exc = stack.shift
    unless meth
      log "Tried everything :("
      throw(:done)
    end
    send(meth)
    log "Success at last!"
  rescue exc => e
    log "#{meth} error"
    log "^ #{e.message}"
    retry
  end
end

=begin

LOG: aaa error
LOG: ^ String can't be coerced into Fixnum
LOG: bbb error
LOG: ^ undefined method `sort' for 12:Fixnum
LOG: ccc error
LOG: ^ wrong number of arguments (1 for 0)
4th attempt
LOG: Success at last!

=end
#--------------------------------------------------------

daz