This question already has answers here:
Ruby does not 'ensure' when I 'retry' in 'rescue'
(2 answers)
Closed 8 years ago.
Here's a snippet
def take_resource
puts "resource taken"
end
def free_resource source
puts "resource freed from #{source}"
end
def do_stuff
tries = 0
begin
take_resource
raise 'oops'
rescue
if tries < 3
tries += 1
free_resource 'rescue'
retry
end
raise
ensure
free_resource 'ensure'
end
end
do_stuff
# ~> -:13:in `do_stuff': oops (RuntimeError)
# ~> from -:28:in `<main>'
# >> resource taken
# >> resource freed from rescue
# >> resource taken
# >> resource freed from rescue
# >> resource taken
# >> resource freed from rescue
# >> resource taken
# >> resource freed from ensure
Here we see that ensure clause is not invoked when we retry the block. Why is that? Is there a logical explanation to this? I thought that ensure is called ensure for a reason: it always runs. Well, it turned out that I was wrong.
And while we're on it: do you know about other gotchas in this area (exception handling)?
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)
Related
below is the code which is called by the gtk closing action -->
def on_main_window_destroy(object)
begin
$client.send(':exit')
Thread.kill $client.response
rescue
puts 'exiting'
end
Thread.kill $receiving_message
Gtk.main_quit()
exit
end
which results in this output.
app.rb:81:in `exit': exit
from app.rb:81:in `on_main_window_destroy'
from /home/user/.gem/ruby/2.4.0/gems/gobject-introspection-3.1.8/lib/gobject-introspection/loader.rb:110:in `invoke'
from /home/user/.gem/ruby/2.4.0/gems/gobject-introspection-3.1.8/lib/gobject-introspection/loader.rb:110:in `block in define_singleton_method'
from app.rb:97:in `<main>'
the program works fine .. and it doesn't create a mess for me .. but i want to know the reasons for these errors so that i can handle it.
The Kernel#exit raises an exception to terminate the program, that looks like be the exception message you're asking about:
Initiates the termination of the Ruby script by raising the SystemExit exception. This exception may be caught.
As for the "block in define_singleton_method" part of the stack trace, while ruby has a define_singleton_method, if you look at line 110 of that file specified, you'll see that the method you are in is also called define_singleton_method and you are inside a block in that method:
def define_singleton_method(klass, name, info)
# ...
singleton_class.__send__(:define_method, name) do |*arguments, &block|
arguments, block = prepare.call(arguments, &block) # <<< Line 110
# ...
end
end
I'm not sure why you're actually seeing that output instead of just exiting silently like you normally would, a possibility is that somewhere in the code, something is just rescuing the base Exception, instead of StandardError, which is generally a bad idea, though they may be just logging/outputting and re-raising it (which as seen in some of those answers, is OK), it's all just speculation without digging around a lot further into the code (it might not even be in this gem)
I know how to catch a SIGINT, but I only know how to put the block somewhere random in my code. I need to be able to catch SIGINT because if file writing is in progress (my database writes to a file frequently), it corrupts the file. What's the ideal way to handle this?
Edit
In one instance, I handled this by executing my writing function if I detect a SIGINT, but I'm realizing this won't help if I'm literally in the middle of writing to a file using Marshal.
Is there no way in Ruby to catch a SIGINT and tell it to ignore it until it, for example, finishes writing?
Edit 2
Nevermind, just figured out a possible answer... see below.
I ended up using a rescue block, which rescues Interrupt. In the block, I turn a boolean to true. The boolean is checked at the beginning of each of my thread pool tasks and if its true, skips the entire function.
Edit
In regards to my edit in my original post: Would the best way to handle interrupts in file writing be to use a boolean again? For example, create a boolean called interrupt and at the end of writing to the file, check to see if boolean is true and if so, abort the program? I couldn't use a rescue to catch the interrupt then, but I could use trap, no?
That's what I would do :
Signal.trap("INT"){
if $lock
puts "Not yet done. Please don't interrupt"
else
puts "Goodbye!"
exit
end
}
def something_long_that_shouldnt_be_interrupted
$lock = true
10.times do |i|
puts "Writing #{i+1}/10. Don't interrupt"
sleep 1
end
$lock = false
end
def some_other_process_that_can_be_interrupted
10.times do |i|
puts "Unimportant stuff #{i+1}/10."
sleep 1
end
end
something_long_that_shouldnt_be_interrupted
some_other_process_that_can_be_interrupted
# Writing 1/10. Don't interrupt
# Writing 2/10. Don't interrupt
# ^CNot yet done. Please don't interrupt
# Writing 3/10. Don't interrupt
# Writing 4/10. Don't interrupt
# ^CNot yet done. Please don't interrupt
# Writing 5/10. Don't interrupt
# ^CNot yet done. Please don't interrupt
# ^CNot yet done. Please don't interrupt
# Writing 6/10. Don't interrupt
# ^CNot yet done. Please don't interrupt
# Writing 7/10. Don't interrupt
# Writing 8/10. Don't interrupt
# ^CNot yet done. Please don't interrupt
# Writing 9/10. Don't interrupt
# Writing 10/10. Don't interrupt
# Unimportant stuff 1/10.
# Unimportant stuff 2/10.
# Unimportant stuff 3/10.
# Unimportant stuff 4/10.
# ^CGoodbye!
If you use it more than once, you could define :
def do_not_disturb(&block)
$lock = true
block.yield
$lock = false
end
do_not_disturb do
# code that shouldn't be interrupted
end
I am writing to a file instance. While the program is still running, the file is always empty. When I check the file after the script has executed, the file has content.
class A
def initialize
#file_ref=File.new("/user/shared/ruby/ruby-example/test.html","w+")
end
def fill
#file_ref.write("whatever\nwhatever\nwhatever\n")
end
end
The Main script:
require_relative 'A'
a=A.new
a.fill
puts File.size("/user/shared/ruby/ruby-example/test.html")
After the A instance has done its job, the puts statement will print "0" as if the file is empty. Indeed it is during program execution, but if I start irb:
puts File.size("/user/shared/ruby/ruby-example/test.html")
# => 27
$ cat test.html
whatever
whatever
whatever
Is my code wrong?
Is it normal that streams are flushed only after the execution of a process?
Ruby flushes IO buffers when you call IO#close or IO#flush. Since you are not calling neither close nor flush the buffers are flushed when the program terminates and the opened file descriptors are released.
Given your simple example a possible solution is:
class A
def initialize
#file_ref_name = '/user/shared/ruby/ruby-example/test.html'
end
def fill
File.open(#file_ref_name, 'w+') do |file|
file.write("whatever\nwhatever\nwhatever\n")
end
end
end
Passing a block to IO#open makes the opened file (the file variable in this example) to be closed (and therefore flushed) once the execution of the block terminates.
Please note that Ruby (to my knowledge since version 1.9) features a one liner shortcut for simple file writes as well, flush included:
File.write('/path/to/file.txt', 'content')
I know that the global variable $! holds the most recent exception object, but I am having confusion with the syntax below. Can anyone help me understand the following syntax?
rescue $!
This construct prevents exception from stopping your program and bubbling up the stack trace. It also returns that exception as a value, which can be useful.
a = get_me_data rescue $!
After this line, a will hold either requested data or an exception. You can then analyze that exception and act accordingly.
def get_me_data
raise 'No data for you'
end
a = get_me_data rescue $!
puts "Execution carries on"
p a
# >> Execution carries on
# >> #<RuntimeError: No data for you>
More realistic example
lines = File.readlines(filename) rescue $!
You either get the lines or the error (if file doesn't exist, you don't have permissions, etc). In any case, execution doesn't stop.
Is there a Perl equivalent END block in Ruby? In Perl, if I specify an END block, the code in that block will get executed no matter where the program bails out. It is great functionality for closing open file handles. Does Ruby support similar functionality? I tried Ruby's "END{}" block but that doesnt seem to get called if I had an exit in the code due to an error.
Thanks!
Use at_exit, which will run regardless of whether an exception was raised or not:
at_exit { puts 'exited!' }
raise
prints "exited" as expected.
You should only consider this if you cannot use an ensure, as at_exit causes logic to reside far away from where the actual exit occurs.
Yes. A block may have an 'ensure' clause. Here's an example:
begin
# This will cause a divide by zero exception
puts 3 / 0
rescue Exception => e
puts "An error occurred: #{e}"
ensure
puts "I get run anyway"
end
Running this produces:
An error occurred: divided by 0
I get run anyway