Ruby Does Not 'Ensure' When I 'Retry' in 'Rescue'

Ruby does not 'ensure' when I 'retry' in 'rescue'

The ensure section is executed when leaving the begin statement (by any means) but when you retry, you're just moving around inside the statement so the ensure section will not be executed.

Try this version of your example to get a better idea of what's going on:

attempts = 0
begin
make_service_call()
rescue Exception
attempts += 1
retry unless attempts > 2
exit -1
ensure
puts "ensure! #{attempts}"
end

Why is ensure clause not called when retrying?

ensure is called when the block is exited, whether via an exception or normally. retry merely transfers the execution point to the start of the block, hence you're still in the block, and ensure isn't called.

Consider that ensure exists to help with cleaning up resources when exiting the block. If you're still in the block, then presumably you're still using the resources.

This is the expected behaviour.

These keywords are described in detail in the Programming Ruby book (http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html)

Ruby Retry and ensure block is not working

Sorry about the previous suggestion. I've just noticed the actual cause of error. The following works as you'd assume it to:

def doCalc
begin
print( "Enter a number: " )
aNum = gets().chomp()
result = 100 / aNum.to_i
#rescue Exception => e # - dont catch Exception unless you REALLY need it.
rescue => e # - this catches StandardError and is usually enough
result = 0
puts( "Error: #{e}\nPlease try again." ) # <-- changed here
retry # retry on exception
else
msg = "Result = #{result}"
ensure
msg = "You entered '#{aNum}'. " + msg
end
return msg
end

You've actually had THREE errors thrown one after another.

First error ocurred as you planned - during the parsing and division. And it was caught by the rescue Exception => e properly.

But you cannot "add" an "exception" to a "string". Trying to "asd" + e + "asd" caused another error to be thrown what crashed your 'rescue' block. However, the immediatelly following 'ensure' block must run, so while your rescue block was about to quit and rise another error, the ensure block fired.

And the ensure block failed to add msg to the string, since msg was nil at the moment. So, for the third time an exception was raised, and it successfully has hidden all the previous exceptions.

In short: Note how I now replaced the add-exception-to-a-string with string interpolation. You cannot add e directly. You have to either use interpolation, or call e.to_s manually.

What is the difference when using ensure and else after rescue?

1.

Using ensure, baz_process is alway executed even though foo_process throw exception, return from method.

For example,

def foo_process
puts 'foo_process'
return 111
end

def f1
begin
return foo_process
rescue
puts 'bar_process'
ensure
puts 'baz_process'
end
end

def f2
begin
return foo_process
rescue
puts 'bar_process'
end
puts 'baz_process'
end

>> f1
foo_process
baz_process
=> 111
>> f2
foo_process
=> 111

Can I put retry in rescue block without begin ... end?

To answer the question you're asking, no. rescue can only be used from within a begin..end block or method body.

begin
bad_method
rescue SomeException
retry
end

def some_method
bad_method
rescue SomeException
retry
end

rescue_from is just a framework helper method created because of how indirect the execution is in a controller.

To answer the question you're really asking, sure. You can override create_or_update with a rescue/retry.

module NonUniquePkeyRecovery
def create_or_update(*)
super
rescue ActiveRecord::RecordNotUnique => e
raise unless e.message.include? '_pkey'
self.class.connection.reset_pk_sequence!(self.class.table_name)
retry
end
end

ActiveSupport.on_load(:active_record) do
include NonUniquePkeyRecovery
end

Ruby rescue and retry specific code block

retry will execute the entire begin block, so in your case all the email loops will run again.

Here's a quick example, which will print integers 1 through 7 continuously (terminate with CTRL-C, as it will infinite loop):

begin
(1..10).each do |x|
puts x
if x > 6
STDIN.gets # press enter to do another iteration
raise ArgumentException
end
end
rescue
retry # loop will restart from 1
end

`retry` doesn't evaluate begin block again

It would execute 5 times if an error had been raised, which is not the case.

Try this

i = 0
begin
p "5 times"
raise "error"
rescue
i += 1
retry if i < 5
end

If you want code to be executed even when there are no exceptions use ensure, but you can't retry from there.

i = 0
begin
p "5 times"
rescue
# won't run if there are no exceptions
i += 1
retry if i < 5
ensure
# always runs
end


Related Topics



Leave a reply



Submit