From: Joel VanderWerf Date: 2005-07-30T04:24:40+09:00 Subject: Re: Allow retry to take arguments? Ara.T.Howard wrote: > On Fri, 29 Jul 2005, Austin Ziegler wrote: > >> It's a neat idea, though, and I think that it could be generally >> useful, not just database useful. > > > i use these in my personal lib (http://codeforpeople.com/lib/ruby/alib/): > > # > # initiates a catch block to do 'something'. if the method > try_again! is > # called the block is done over - if the method give_up! is called > the block > # is aborted. calls to attempt may be nested. > # > def attempt label = 'attempt' > ret = nil > n_attempts = 0 > loop{ break unless catch("#{ label }"){ ret = yield(n_attempts > += 1) } == 'try_again' } > ret > end > # > # see attempt > # > def try_again! label = 'attempt' > throw "#{ label }", 'try_again' > end > # > # see attempt > # > def give_up! label = 'attempt' > throw "#{ label }", 'give_up' > end > > usage like > > attempt do |n_attempts| > begin > db = connect > rescue > sleep 2 and try_again! if n_attempts < 3 > end > end > > and can be nested like > > attempt('connect') do |connect_attempts| > begin > db = connect > > attempt('transaction') do |transaction_attempts| > begin > db.execute sql > rescue => e > case e > when ConnectionError > sleep 2 and try_again!('connect') if connection_attempts < 3 > when TransactionError > sleep 3 and try_again!('transaction') if > transaction_attempts < 4 > else > raise > end > end > end > > rescue > sleep 2 and try_again!('connect') if connection_attempts < 3 > end > end > > > and end up using it alot for network type code. > > -a Well, that's timely and useful for some network code I'm working on that has to expect a lot of disconnects and timeouts. I'll try wrapping the attempt as an object: class Attempt attr_reader :attempts, :label def initialize label @label = "__attempt__#{label}__".intern @attempts = 0 end TRY_AGAIN = :try_again GIVE_UP = :give_up def try_again @attempts += 1 throw @label, TRY_AGAIN end def give_up throw @label, GIVE_UP end end def attempt label att = Attempt.new(label) ret = nil op = Attempt::TRY_AGAIN while op == Attempt::TRY_AGAIN op = catch att.label do ret = yield(att) end end ret end x = 0 r = attempt 'add' do |add| begin x += 1 if x < 10 raise ArgumentError end rescue ArgumentError add.try_again if add.attempts < 15 end x end p r -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407