What Is the Purpose of Redo and Retry Statements in Ruby

What is the purpose of redo and retry statements in Ruby?

The idea is that you change something before calling redo or retry, in the hopes that the whatever you were doing will work the second time. I don't have an example for redo, but we have found uses for retry in the application I'm working on. Basically, if you have a bit of code that might fail due to something external (e.g. network), but performing a precondition check every time you run the code would be too expensive, you can use retry in a begin...rescue block. Not sure if that was clear, so I'll get right to the example.

Basically, we have some code that accesses a remote directory using Net:SFTP. The directory should exist, but in some exceptional cases it will not have been made yet. If it's not there, we want to try once to make it. But performing the network access to check if the directory exists every time would be too expensive, especially since it's only in exceptional cases that it won't be there. So we do it as follows:

tried_mkdir = false
begin
# Attempt to access remote directory
...
rescue Net::SFTP::StatusException
raise if tried_mkdir
tried_mkdir = true
# Attempt to make the remote directory
...
retry
end

How does the retry statement work in Ruby?

Your WHILE does not behave like a regular while since in your case i<3 is evaluated at call time once. The while statement evaluates it each time.

If you want to write a while equivalent it's important that your condition be something that can be evaluated, not something that is already evaluated.

You can fix that by accepting a Proc as a condition:

def so_long_as(cond)
loop do
return unless cond.call

yield
end
end

Then you call it like this:

i = 0
so_long_as(-> { i < 3 }) do
print i
i += 1
end

Where that now prints 012 and terminates properly.

What's important to note is that retry only works within a begin/end context, not a regular method, that now you have to use redo instead:

i = 0
redone = false
so_long_as(-> { i < 3 }) do
print i
unless (redone)
redone = true
redo
end
i += 1
end

Where that prints 0012 now.

There's been some significant changes to how redo and retry work that are worth reading up on.

Ruby's redo method vs while loop

The redo command restarts the current iteration of a loop (without checking termination conditions in while or advancing iterators in for, for example), you still need a loop of some description (such as a while loop).

That's evidenced by the answer you link to, which contains:

nums = Array.new(5){[rand(1..9), rand(1..9)]}
nums.each do |num1, num2|
print "What is #{num1} + #{num2}: "
redo unless gets.to_i == num1 + num2
end

The .each provides the looping structure there and all the redo does is restart that loop (without advancing to the next nums element) if you get the answer wrong.

Now you could actually use a while loop there as the controlling loop, advancing to the next one only if you get it right:

nums = Array.new(5){[rand(1..9), rand(1..9)]}
index = 0
while index < 6 do
num1 = nums[index][0]
num2 = nums[index][1]
print "What is #{num1} + #{num2}: "
if gets.to_i == num1 + num2 then
index = index + 1
end
end

or within the .each itself without redo:

nums = Array.new(5){[rand(1..9), rand(1..9)]}
nums.each do |num1, num2|
answer = num1 + num2 + 1
while answer != num1 + num2 do
print "What is #{num1} + #{num2}: "
answer = gets.to_i
end
end

but neither of them are as elegant as the redo solution, which provides a more expressive way of controlling loops, an extension to the usual control you see in other languages, such as continue or break.

Is it possible to use `retry` keyword inline with `if`?

Ok, thank you all for answers! I understood what was a problem, but your workarounds wasn't what I need. Actually, rescue part was for restarting input prompt in case of illegal input from a user, while my 'retry' inside begin was to restart block from another user input (y/n question).

So after some investigation, this code would work flawlessly:

begin
loop do
bet = Dialogs.enter_your_bet(gapes[0],gapes[1])
approx.calculate_average_profit(bet)
approx.print_profits_table
break if !Dialogs.play_again?
end
rescue;retry
end

And again, thanks for been so awesomely active community. Take care!

Handle Interrupt and retry in Ruby until .. do loop

Solved. I just needed to wrap the loop inside a begin .. end

test = "hey"
begin
until test == "hi" do
test = gets.chomp
end
rescue Interrupt
retry
end

How to store retry statement in Proc object

Try to change the retry to redo. I think some changes were made in Ruby 1.9 as the retry keyword was abused.

The difference can be found here: http://rubyquicktips.com/post/1122838559/redo-vs-retry See the comment at the bottom.

Can I jump back to the beginning of a method using 'redo' in Ruby?

He must have meant to use retry, not redo.

How do you limit retry & rescue in this Ruby example?

You can just write a 5.times plus a break on success inside the loop, or abstract the pattern to keep the logic separate from the looping. An idea:

module Kernel
def with_rescue(exceptions, retries: 5)
try = 0
begin
yield try
rescue *exceptions => exc
try += 1
try <= retries ? retry : raise
end
end
end

with_rescue([ProtocolError], retries: 5) do |try|
protocol = (try == 0) ? 'HTTP/1.1' : 'HTTP/1.0'
send(protocol)
end


Related Topics



Leave a reply



Submit