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
Execjs::Programerror in Welcome#Index Typeerror: Object Doesn't Support This Property or Method
Rake Db:Migrate Is Being Aborted Due to Rake Version Difference
Devise Install from Existing Model/Database
Accessing Variables from Included Files in Ruby
Generate a Nested JSON Array in Jbuilder
Using Ruby to Generate Sha512 Crypt-Style Hashes Formatted for /Etc/Shadow
Rails 3.2 Activeadmin 'Collection Is Not a Paginated Scope.' Error
Getting Ruby Function Object Itself
What's the Best Background Job Management Library for Rails
How to Render a String as an Erb File
Ruby: How to Write a Bang Method, Like Map
Sign Out Specific User with Devise in Rails
Get an Array from a Rails Form
Recovering from a Broken Tcp Socket in Ruby When in Gets()
Open-Uri Returning Ascii-8Bit from Webpage Encoded in Iso-8859
Wicked_Pdf: Footer Height/Styling
(New to Rails) Rails Server Error: Cannot Load Such File -- Bootsnap/Setup (Loaderror)