Reputation: 53
I feel a bit odd, as there are a number of questions a bit like this, but not close enough, or no answer.
[EDIT: I am rephrasing the question to make it clearer]
I have a loop that does a number of things, and was playing with it to see if various options would work to make things more readable, cleaner. In the end, I messed something up and it threw and exception, which was caught with rescue. So far so good. I cannot, however, find any way to make ruby just continue with the following statements in that same iteration of the loop - the exception always casues the following statements and it moves on to the next iteration - default behaviour. Redo or retry would be pointless and result in an infinite loop, hitting the same error and exception again, etc,
I there any way to force ruby to handle the error in some way then just continue where it left off?
Here is a sample, stolen from I don't know where and adapted.
3.times do |i|
begin
# first bunch of stuff to do
# second bunch of stuff to do
# third bunch of stuff to do
# fourth bunch of stuff to do
rescue => e
p e
end
end
Basically, in this example, I want all four of the bunches of stuff to be executed before moving on to the next iteration of the loop, even if one of them should cause an error. I wasn't to process the error, allowing me to see the exception result, etc, but effectively ignoring it and continuing on to do all that other bunches of stuff.
Any ideas? It might be something really obvious that I have just never seen before.
Upvotes: 4
Views: 9184
Reputation: 796
You're defining a begin-end
block with a rescue
clause. If any exception is raised by the block and there's a matching rescue clause, the block will stop executing and the rescue clause will be executed. If there's no matching rescue block, the error will bubble up (and hopefully be handled by another block, otherwise it'll be unhandled and your script will stop!) If there's an ensure
clause, that'll get run too even if there's an exception.
So where does that leave us? If you want to defend against failure of individual steps and continue regardless, each step needs its own block:
3.times do |i|
begin
first_thing
rescue => e
puts "The first thing blew up! #{e.inspect}"
puts "I'll carry on anyway ¯\\_(ツ)_/¯"
end
begin
second_thing
third_thing
rescue => e
puts "Either the second or third thing blew up... #{e.inspect}"
puts "Straight on to the fourth thing!"
end
begin
fourth_thing
rescue => e
puts "Fourth thing blew up! #{e.inspect}"
end
end
It's a bit unusual to have a block like this which should carry on executing if something goes wrong - that's usually a good way to make one of the next steps go wrong too! You might need to make sure that you still have data integrity at each point, and that the later steps don't depend on something which might have failed to happen in the earlier steps.
Upvotes: 3
Reputation: 30115
A begin
block (including implicit ones from say def
) ends on the first exception. If you want to do somthing seperately after regardless of success/failure, then put it outside the block.
3.times do |i|
begin
raise "Raised from iteration #{i}"
rescue => e
p e
end
puts "I'm after the exception"
end
If you want to do something even after a return
, break
, etc. then use an ensure
block.
5.times do |i|
begin
break if i == 3
raise "Raised from iteration #{i}"
rescue => e
p e
ensure
puts "I always run #{i}"
end
end
Which outputs:
#<RuntimeError: Raised from iteration 0>
I always run 0
#<RuntimeError: Raised from iteration 1>
I always run 1
#<RuntimeError: Raised from iteration 2>
I always run 2
I always run 3
If you really want to ignore and continue from many statements that might throw, they each need a seperate rescue
block. You could do this with another wrapper method. However be very careful that the exception being ignored really do not matter than that continuing is safe.
Exception by design, are meant to abort the following operations and try to avoid leaving inconsistent and unknown program states.
def suppress
yield
rescue => e
puts "Supressing #{e}"
end
5.times do |i|
suppress { raise "I throw sometimes #{i}" if i <= 3 }
suppress { raise "I throw sometimes too #{i}" if i > 2 }
puts "After possible exceptions #{i}"
end
Outputs:
Supressing I throw sometimes 0
After possible exceptions 0
Supressing I throw sometimes 1
After possible exceptions 1
Supressing I throw sometimes 2
After possible exceptions 2
Supressing I throw sometimes 3
Supressing I throw sometimes too 3
After possible exceptions 3
Supressing I throw sometimes too 4
After possible exceptions 4
Upvotes: 2