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
How to Configure Mongomapper and Activerecord in Same Ruby Rails Project
What's the Fastest Way to Check If a Word from One String Is in Another String
How to Assign Multiple Values to a Hash Key
Using Ruby and Net-Ssh, How to Authenticate Using the Key_Data Parameter with Net::Ssh.Start
Help Understanding Yield and Enumerators in Ruby
Ruby on Rails, Paperclip, Amazon Aws S3 & Heroku
Rails Sort Tags by Most Used (Tag.Posts.Count)
How to Make Users Automatically Follow Admin User on Sign Up
Run Rails Commands Outside of Console
Define_Method: How to Dynamically Create Methods with Arguments
Differencebetween Ruby's Send and Public_Send Methods
Is It Acceptable Practice to Patch Ruby's Base Classes, Such as Fixnum
What Is the Activemodel Method Attribute "_Was" Used For
How to Debug Ruby on Rails in Eclipse Aptana Plugin
Make: /Usr/Bin/Mkdir: Command Not Found During 'Gem Install Nokogiri' in Ubuntu 20.04