How can I write resuming into loops in Ruby? Here is a sample code.
#!/usr/bin/ruby
#
a = [1,2,3,4,5]
begin
a.each{|i|
puts i
if( i==4 ) then raise StandardError end # Dummy exception case
}
rescue =>e
# Do error handling here
next # Resume into the next item in 'begin' clause
end
However, when running, Ruby returns the error message
test1.rb:13: Invalid next
test1.rb: compile error (SyntaxError)
I'm using Ruby 1.9.3.
You should use retry instead of next; But this will cause infinite loop (retry restart from the beginning of the begin)
a = [1,2,3,4,5]
begin
a.each{|i|
puts i
if i == 4 then raise StandardError end
}
rescue =>e
retry # <----
end
If you want skip an item, and continue to next item, catch the exception inside the loop.
a = [1,2,3,4,5]
a.each{|i|
begin
puts i
if i == 4 then raise StandardError end
rescue => e
end
}
Move your exception catching into the each block e.g:
a = [1,2,3,4,5]
a.each do |i|
puts i
begin
# Dummy exception case
if( i==4 ) then raise StandardError end
rescue =>e
# Do error handling here
end
end
Related
I'm using a gem that's throwing an exception in a background thread as below. I'd like to catch this exception but not sure about how to go about it. How would one go about handling exceptions in library threads?
#this class is in my code
class MQTT
def self.connect
#client = Client.connect(options)
end
ende
This class is in the library which is packaged as a gem, so I technically don't have access to it:
class Client
def self.connect(*args, &block)
client = Client.new(*args)
client.connect(&block)
return client
end
def connect(clientid=nil)
# Start packet reading thread
#read_thread = Thread.new(Thread.current) do |parent|
Thread.current[:parent] = parent
loop { receive_packet }
end
end
def receive_packet
begin
# Poll socket - is there data waiting?
result = IO.select([#socket], nil, nil, SELECT_TIMEOUT)
# Pass exceptions up to parent thread
rescue Exception => exp
unless #socket.nil?
#socket.close
#socket = nil
end
Thread.current[:parent].raise(exp)
end
end
end
I think you have 3 options.
You could return the exception to the calling thread:
def receive_packet
raise "Exception in #{Thread.current}"
rescue Exception => exp
return exp
end
t1 = Thread.new do
receive_packet
end
puts "t1: #{t1.value.inspect}"
You could catch the exception on joining the thread (note you could reraise here or use an ensure block to make sure your socket is closed):
def receive_packet
raise "Exception in #{Thread.current}"
rescue Exception => exp
# reraise the exception
raise exp
end
t = Thread.new do
receive_packet
end
begin
t.join
rescue => e
puts "Exception caught from joined thread #{e.message} "
end
or you set #abort_on_exception = true so that exceptions kill all threads:
Thread.abort_on_exception = true
begin
Thread.new do
receive_packet
end
sleep 1
rescue => e
puts "Exception raised immediately to main thread: #{e.message}"
end
Update Based on what you have above and your comment I guess you need to wait for the threads calling receive_packet to finish. So you would have to join them:
class Client
def self.connect(*args, &block)
client = Client.new(*args)
client.connect(&block)
return client
end
def initialize(args)
#count = 0
end
def connect(clientid=nil)
puts "Connecting. Thread.current is #{Thread.current}"
# Start packet reading thread
#read_thread = Thread.new(Thread.current) do |parent|
Thread.current[:parent] = parent
loop { receive_packet }
end
end
def receive_packet
begin
# Poll socket - is there data waiting?
# result = IO.select([#socket], nil, nil, SELECT_TIMEOUT)
sleep 0.1
#count += 1
puts "count is now #{#count}"
if #count == 3
raise "WOOT: #{#count}"
end
# Pass exceptions up to parent thread
rescue Exception => exp
unless #socket.nil?
#socket.close
#socket = nil
end
puts "Reraising error #{exp.inspect} from #{Thread.current} to #{Thread.current[:parent]}"
Thread.current[:parent].raise(exp)
end
end
end
class MQTT
def self.connect
#client = Client.connect(options = {})
end
end
begin
MQTT.connect
Thread.list.each do |t|
# Wait for the thread to finish if it isn't this thread (i.e. the main thread).
t.join if t != Thread.current
end
rescue => e
puts "Exception from child thread: #{e.inspect}"
end
The following code works:
collection.each do |i|
begin
next if i > 10
i += 1
rescue
puts "could not process #{ i }"
end
end
However, when we refactor:
collection.each do |i|
begin
increment i
rescue
puts "could not process #{ i }"
end
end
def increment i
next if i > 10
i += 1
end
I get invalid next error. Is this a limitation of Ruby (1.9.3)?
Does the begin rescue block work the same way if there is an exception in the increment method?
Your next statement must occur inside a loop. There's no loop inside your increment method.
Exceptions will 'bubble up', so if there's an exception in your increment method, it will be caught by the rescue section of the calling method.
I am looking for a solution of classic problem of exception handling. Consider following piece of code:
def foo(n)
puts " for #{n}"
sleep n
raise "after #{n}"
end
begin
threads = []
[5, 15, 20, 3].each do |i|
threads << Thread.new do
foo(i)
end
end
threads.each(&:join)
rescue Exception => e
puts "EXCEPTION: #{e.inspect}"
puts "MESSAGE: #{e.message}"
end
This code catches the exception after 5 seconds.
But if I change the array as [15, 5, 20, 3], above code catch the exception after 15 seconds. In short, it always catch the exception raised in first thread.
Any idea, why so. Why doesn't it catch the exception after 3 seconds each time? How do I catch the first raised exception by any thread?
If you want any unhandled exception in any thread to cause the interpreter to exit, you need to set Thread::abort_on_exception= to true. Unhandled exception cause the thread to stop running. If you don't set this variable to true, exception will only be raised when you call Thread#join or Thread#value for the thread. If set to true it will be raised when it occurs and will propagate to the main thread.
Thread.abort_on_exception=true # add this
def foo(n)
puts " for #{n}"
sleep n
raise "after #{n}"
end
begin
threads = []
[15, 5, 20, 3].each do |i|
threads << Thread.new do
foo(i)
end
end
threads.each(&:join)
rescue Exception => e
puts "EXCEPTION: #{e.inspect}"
puts "MESSAGE: #{e.message}"
end
Output:
for 5
for 20
for 3
for 15
EXCEPTION: #<RuntimeError: after 3>
MESSAGE: after 3
Note: but if you want any particular thread instance to raise exception this way there are similar abort_on_exception= Thread instance method:
t = Thread.new {
# do something and raise exception
}
t.abort_on_exception = true
Thread.class_eval do
alias_method :initialize_without_exception_bubbling, :initialize
def initialize(*args, &block)
initialize_without_exception_bubbling(*args) {
begin
block.call
rescue Exception => e
Thread.main.raise e
end
}
end
end
Postponed exceptions processing (Inspired by #Jason Ling)
class SafeThread < Thread
def initialize(*args, &block)
super(*args) do
begin
block.call
rescue Exception => e
#exception = e
end
end
end
def join
raise_postponed_exception
super
raise_postponed_exception
end
def raise_postponed_exception
Thread.current.raise #exception if #exception
end
end
puts :start
begin
thread = SafeThread.new do
raise 'error from sub-thread'
end
puts 'do something heavy before joining other thread'
sleep 1
thread.join
rescue Exception => e
puts "Caught: #{e}"
end
puts 'proper end'
This will wait for the first thread to either raise or return (and re-raise):
require 'thwait'
def wait_for_first_block_to_complete(*blocks)
threads = blocks.map do |block|
Thread.new do
block.call
rescue StandardError
$!
end
end
waiter = ThreadsWait.new(*threads)
value = waiter.next_wait.value
threads.each(&:kill)
raise value if value.is_a?(StandardError)
value
end
Jason Ling's answer will miss out any arguments passed to Thread.new. This will break Puma and other gems. To avoid this problem, you can use:
Thread.class_eval do
alias_method :initialize_without_exception_bubbling, :initialize
def initialize(*args, &block)
initialize_without_exception_bubbling(*args) {
begin
block.call(*args)
rescue Exception => e
Thread.main.raise e
end
}
end
end
begin
#some routine
rescue
retry
#on third retry, output "no dice!"
end
I want to make it so that on the "third" retry, print a message.
Possibly not the best solution, but a simple way is just to make a tries variable.
tries = 0
begin
# some routine
rescue
tries += 1
retry if tries <= 3
puts "no dice!"
end
loop do |i|
begin
do_stuff
break
rescue
raise if i == 2
end
end
or
k = 0
begin
do_stuff
rescue
k += 1
k < 3 ? retry : raise
end
begin
#your code
rescue
retry if (_r = (_r || 0) + 1) and _r < 4 # Needs parenthesis for the assignment
raise
end
There is another gem named retry-this that helps with such a thing.
ruby-1.9.2-p0 > require 'retry-this'
ruby-1.9.2-p0 > RetryThis.retry_this(:times => 3) do |attempt|
ruby-1.9.2-p0 > if attempt == 3
ruby-1.9.2-p0 ?> puts "no dice!"
ruby-1.9.2-p0 ?> else
ruby-1.9.2-p0 > puts "trying something..."
ruby-1.9.2-p0 ?> raise 'an error happens' # faking a consistent error
ruby-1.9.2-p0 ?> end
ruby-1.9.2-p0 ?> end
trying something...
trying something...
no dice!
=> nil
The good thing about such a gem as opposed to raw begin..rescue..retry is that we can avoid an infinite loop or introducing a variable just for this purpose.
class Integer
def times_try
n = self
begin
n -= 1
yield
rescue
raise if n < 0
retry
end
end
end
begin
3.times_try do
#some routine
end
rescue
puts 'no dice!'
end
The gem attempt is designed for this, and provides the option of waiting between attempts. I haven't used it myself, but it seems to be a great idea.
Otherwise, it's the kind of thing blocks excel at, as other people have demonstrated.
def method(params={})
tries ||= 3
# code to execute
rescue Exception => e
retry unless (tries -= 1).zero?
puts "no dice!"
end
Proc.class_eval do
def rescue number_of_attempts=0
#n = number_of_attempts
begin
self.call
rescue => message
yield message, #n if block_given?
#n -= 1
retry if #n > 0
end
end
end
And then you can use it as:
-> { raise 'hi' }.rescue(3)
-> { raise 'hi' }.rescue(3) { |m, n| puts "message: #{m},
number of attempts left: #{n}" }
I know of the standard technique of having a begin rescue end
How does one just use the rescue block on its own.
How does it work and how does it know which code is being monitored?
A method "def" can serve as a "begin" statement:
def foo
...
rescue
...
end
You can also rescue inline:
1 + "str" rescue "EXCEPTION!"
will print out "EXCEPTION!" since 'String can't be coerced into Fixnum'
I'm using the def / rescue combination a lot with ActiveRecord validations:
def create
#person = Person.new(params[:person])
#person.save!
redirect_to #person
rescue ActiveRecord::RecordInvalid
render :action => :new
end
I think this is very lean code!
Example:
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
ensure
# ensure that this code always runs
end
Here, def as a begin statement:
def
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
ensure
# ensure that this code always runs
end
Bonus! You can also do this with other sorts of blocks. E.g.:
[1, 2, 3].each do |i|
if i == 2
raise
else
puts i
end
rescue
puts 'got an exception'
end
Outputs this in irb:
1
got an exception
3
=> [1, 2, 3]