How to know what exceptions to rescue - ruby

I want a class that will load and save settings in yaml file, and displays an error if something happens. I must catch exceptions and I must know what to catch. There is no guarantee what exception will be raised; user might press CTRL+C or memory may run out, but YAML.load_file can raise only a limited number of exceptions. There is nowhere listed what exceptions a function YAML.load_file might raise.
How can I catch only them when I don't know what those exceptions are?
This question has been asked, but there is no real answer:
How to know what exceptions to rescue? Answer is about exceptions in general
How to deal with not knowing what exceptions can be raised by a library method in Ruby?
Ruby How to know what to rescue?
Where to find (or how to read) ruby documentation?
Documentation for Ruby exceptions

Sometimes you just don't know which kind of exception can be thrown, for that the generic rescue catching exists.
begin
do_something
rescue KnownException
treat_exception
# generic exception
rescue Exception => e
# you don't know which exception has been raised but all info is in e
print "Ups I don't know this Exception:#{e.class} error: #{e.message}"
raise
end

How can I catch only them when I don't know what those exceptions are?
What are you going to do when you catch them, I wonder? It makes little sense to catch exceptions for the sake of catching. Swallowing exceptions is especially bad practice.
My rule of thumb is: catch only those I can recover from (this implies already knowing what they are). Let the rest bubble up and crash the program or possibly be catched in one of outer scopes (which will know how to recover from this concrete one).
How to discover currently loaded exception classes
This almost 10 year old code snippet still works today:
exceptions = []
tree = {}
ObjectSpace.each_object(Class) do |cls|
next unless cls.ancestors.include? Exception
next if exceptions.include? cls
exceptions << cls
cls.ancestors.delete_if {|e| [Object, Kernel].include? e }.reverse.inject(tree) {|memo,cls| memo[cls] ||= {}}
end
indent = 0
tree_printer = Proc.new do |t|
t.keys.sort { |c1,c2| c1.name <=> c2.name }.each do |k|
space = (' ' * indent); space ||= ''
puts space + k.to_s
indent += 2; tree_printer.call t[k]; indent -= 2
end
end
tree_printer.call tree
Run it in the rails console and you'll see a lot of exception classes. :)

The source code is the documentation.
-- Matz

Related

Ruby console - Multiline command with exit

I have a simple ruby script I would like to run in the rails console, using bundle exec rails c
ids = [1, 2]
if ids.length() > 5
puts "More than 5 ids, quitting"
exit
end
ids.each do |id|
puts id
end
The rails console quits the program as soon as it sees the exit command. What would be the best way around this?
One way to do this is to use throw/catch:
ids = [1, 2]
catch(:bail) do
if ids.length > 5
puts "More than 5 ids, quitting"
throw :bail
end
ids.each do |id|
puts id
end
end
catch :bail defines a block that is labeled with the symbol :bail (the name doesn't really matter). It will execute normally until you throw :bail at which point Ruby zips up the call stack until its finds a corresponding catch. If no matching catch is found an UncaughtThrowError will be raised.
This is somewhat like break but can be used in any context and to break out of deeply nested constructs.
Another alternative would be to simply define a method or raise (and rescue) an exception if the input is invalid.
Raise ArgumentError When Passed Arguments are Somehow Invalid
There are a lot of ways to fix your code, but your essential problem is that Kernel#exit will exit both a standard REPL session and an application, not simply return from a method or break out of an if-statement. Since your code doesn't show that you're inside a method, you can't use return because there's no appropriate method or closure to return from. You could possibly work around calling #exit with nested irb sessions or the like, but that's ultimately solving the wrong problem.
In most cases, you want to raise an exception with a useful message rather than simply exit when something goes unfixably wrong. The following would do that in your application, while in an irb or pry console it will raise the exception but remain within your REPL session:
ids = [1, 2]
raise ArgumentError, "More than 5 ids: #{ids}" if ids.length > 5
ids.each { |id| puts id }
If you were inside a method, you could use #warn and then return an appropriate return value instead. However, since you are presumably inside the top-level object of your REPL, there's nothing to return from, so this is likely your best general-purpose option.

Ruby Syntax of '=>' (hashrocket)

I tried this earlier and everyone got off on rescue block syntax. Please don't go there. Given the following working code:
begin
(1..1000).each do |i|
puts i
sleep 1
end
rescue Exception => e
puts "\nCaught exception..."
puts "Exception class: #{e.class}"
end
Pressing CTRL+C while it is running prints out "Caught exception...", as expected. What exactly is going on syntax wise in the rescue line, particularly between Exception and the variable e with the => in between?
The word "rescue" is a keyword... part of the ruby language. "e" is a variable, and could just as functionally be "a", "b", or "c". The following code works just as well.
begin
(1..1000).each do |i|
puts i
sleep 1
end
rescue Exception => b
puts "\nCaught exception..."
puts "Exception class: #{b.class}"
end
What are "Exception" and "=>"? Is there another way to write this expression to make it more intelligible from a syntactical point of view? I don't think we're dealing with a hash here because the following code compiles but throws an error as soon as CTRL+C is pressed (undefined local variable or method `e').
begin
(1..1000).each do |i|
puts i
sleep 1
end
rescue { Exception => b }
puts "\nCaught exception..."
puts "Exception class: #{b.class}"
end
Can someone explain what is going on? and specifically what language element '=>' (hashrocket) is in this specific example since it seems to have nothing to do with hashes?
I'm sorry to inform you that this is just one-off syntax that doesn't really have any relation to other Ruby syntax.
Given the expression:
begin
# ...
rescue FooError, BarError => ex
# ...
end
FooError, BarError is the list of exception classes (usually subclasses of StandardError) that will be rescued. This behaves just like an argument list, so you can (if you want) do stuff like this:
my_exception_classes = [ FooError, BarError ]
begin
# ...
rescue *my_exception_classes => ex
# ...
end
It's worth noting that you shouldn't, generally, use Exception here because it will rescue all exceptions, including things like SignalException::Interrupt and NoMemoryError, which usually isn't what you want.
=> is just syntax, and arguably not the best choice of syntax for the reason that it leads to questions like your own.
ex is the name of a local variable into which the exception object will be put.
Digging deeper
If you're into reading parser grammars, it's always fun looking at Ruby's YACC grammar in parse.y. It's not especially easy to read, but we can see the grammar for a rescue expression, called opt_rescue in the grammar, here:
opt_rescue : k_rescue exc_list exc_var then
compstmt
opt_rescue
k_rescue is of course the keyword rescue. exc_list is the list of exception classes which, like I said, is just like an argument list:
exc_list : arg_value
exc_var is the part where the variable to put the exception in is designated:
exc_var : tASSOC lhs
And here, tASSOC is of course our friend the hashrocket (=>), and lhs, i.e. “left-hand side,” is an expression you'd find to the left of an assignment expression (like, say, the name of a variable).
compstmt is basically “any valid Ruby code,” and then there’s opt_rescue again, since you can (optionally) have many rescues in a begin or def block.
As you can see, the only thing this syntax has in common with a Hash is tASSOC.

How to Conditionally Rollback a Transaction Using the PG Ruby Gem

I am currently taking a databases course and one of the lab questions has me stumped as to how to implement the above and indeed, if it was even possible. I have tried searching the docs but the transaction method defined is quite vague.
This is the first time I have tried to do any database manipulation WITHOUT the comfort blanket of Rails and so I am a little lost. I have managed to create a connection to my postgresql database and can execute statements, the final thing that I need to do is rollback a transaction based on a simple condition.
Allow me to show you the code:
require 'pg'
#conn = PG::Connection.open(:dbname => 'db_15_11_labs')
#conn.prepare('insert', 'INSERT INTO house (housenumber, street) VALUES ($1, $2) returning id')
#words = ["Foo", "Bar", "Bash", "Bang"]
def populate
100.times.each_with_index do |i|
# cycle through the #words array for street names, use i as house number
ins = #conn.exec_prepared('insert', [i, "#{#words1[i % 4]} street"])
end
end
Basically, the condition is that if the returned id (ins[0]['id']) is even, rollback the transaction. I figure that if ins[0]['id'] % 2 == 0 I need to throw some sort of exception but how do I do that and more importantly, how do I encode that information into the syntax given in the docs?
Thank you for your time.
EDIT 1
I have now managed to get the syntax that will allow for the conditions to be placed within the defined transaction as follows:
#conn.transaction do |conn|
ins = #conn.exec_prepared('insert', [i, "#{#words1[i % 100]} street"])
end
and so this question really becomes more "how do I throw and catch an exception when ins[0]['id'] % 2 == 0
I have tried following a couple of simple tutorials on raising exceptions but doing the following:
throw :id_exception if (#ins[0]['id'].to_i % 2) == 0
catch :id_exception do
puts "caught :id_exception #{}"
end
inside the transaction results in an 'throw': uncaught throw :id_exception (ArgumentError)
Transactions rollback automatically if an exception is raised inside the block. So you just need to create a little Exception class for yourself, raise one inside the block, and then arrange to rescue it so your program doesn't error-exit due to the raised exception.
Try something like this:
class MyLittleIdError < StandardError
end
begin
#conn.transaction do |conn|
ins = conn.exec_prepared('insert', [i, "#{#words1[i % 100]} street"])
raise MyLittleIdError if (ins[0]['id'].to_i % 2) == 0
end
rescue MyLittleIdError
puts "aha! we have rolled back."
end

ruby "on error resume next" function

Is there a way of doing the old "on error resume next" routine in ruby?
I've got array of value filled in dynamically from elsewhere (read from MQTT topics to be precise) then I want to do a bunch of numeric calculations on them and publish the results. The values SHOULD be numeric but are possibly missing or non-numeric.
At the moment my code looks something like
values=[]
//values get loaded here
begin
Publish('topic1',value[0]*10+value[1])
rescue TypeError,NoMethodError,ZeroDivisionError
end
begin
Publish('topic2',value[3]/value[4])
rescue TypeError,NoMethodError,ZeroDivisionError
end
//etc etc
If the calculation fails for any reason the program should just skip that step and go on.
It works but surely theres a better way than all those identical begin..rescue blocks? Ruby is about "DRY" after all..
Is there a way of re-writing the above so that a single begin..rescue construct is used while still allowing all calculations to be attempted?
UPDATED
How safe to do something like
def safe_Publish(topic,value)
return if value.nil?
Publish(topic,value)
end
and call with
safe_Publish('topic2',(value[3]/value[4] rescue nil))
The main problem is that the above catches ALL exceptions not just the ones I'm expecting which makes me a little nervous.
The on error resume next coding style is really dangerous - as it makes finding new bugs you accidentally introduce to your program very hard to find. Instead, I would just write a different version of publish that doesn't throw those exceptions:
def try_publish(topic_name)
begin
Publish('topic1',yield)
rescue TypeError,NoMethodError,ZeroDivisionError
# are you sure you don't want to do anything here? Even logging the errors
# somewhere could be useful.
end
end
You can then call this with:
try_publish('topic1') { value[0]*10+value[1] }
If TypeError,NoMethodError or ZeroDivisionError are thrown by the expression, they will be caught and ignored.
Now your original method won't require any rescues.
If you really wanted an on error resume next, you could possibly do it by monkey patching the raise method in Kernel, but that would be a horrible idea.
If you think a bit more carefully about what you are doing, and why you want on error resume next, I think you will see that you don't really need to suppress all exceptions. As the other posters pointed out, that would make it hard to find and fix bugs.
Your problem is that you have a bunch of numbers scraped from the Internet, and want to run some calculations on them, but some may be invalid or missing. For invalid/missing numbers, you want to skip over any calculations which would use those numbers.
A few possible solutions:
Pre-filter your data and remove anything which is not a valid number.
Put each calculation you want to do into a method of its own. Put a rescue Exception on the method definition.
Define "safe" wrappers for the numeric classes which don't raise exceptions on divide by zero, etc. Use these wrappers for your calculations.
The "wrappers" might look something like this (don't expect complete, tested code; this is just to give you the idea):
# This is not designed for "mixed" arithmetic between SafeNumerics and ordinary Numerics,
# but if you want to do mixed arithmetic, that can also be achieved
# more checks will be needed, and it will also need a "coerce" method
class SafeNumeric
attr_reader :__numeric__
def initialize(numeric)
#__numeric__ = numeric.is_a?(String) ? numeric.to_f : numeric
end
def zero?
#__numeric__.zero?
end
def /(other)
if other.zero? || #__numeric__.nil? || other.__numeric__.nil?
SafeNumeric.new(nil) # could use a constant for this to reduce allocations
else
SafeNumeric.new(#__numeric__ / other.__numeric__)
end
end
def to_s; #__numeric__.to_s; end
def inspect; #__numeric__.inspect; end
# methods are also needed for +, -, *
end
Then use it like:
numbers = scraped_from_net.map { |n| SafeNumeric.new(n) }
# now you can do arithmetic on "numbers" at will
This shows how to wrap a bunch of quick operations into a loop with each one being protected by a begin/rescue:
values = [1,2,3,0,4]
ops = [ ->{values[0]/values[1]}, ->{values[2]/values[3]} ]
ops.each do |op|
begin
puts "answer is #{op.call}"
rescue ZeroDivisionError
puts "cannot divide by zero"
end
end
I prefer the safe_publish method, however, as you can unit test that and it encapsulates the logic of making safe calls and handling errors in a single place:
def safe_publish(topic, &block)
begin
value = block.call
publish(topic, value)
rescue
# handle the error
end
end
and then you can call this with code like:
safe_publish 'topic0' do
value[0]*10+value[1]
end

Ruby Exception.to_s doesn't match expected string

I have a rescue block which checks for the correct exception thrown or not as follows:
rescue Exception
if $!.to_s() != "myException"
Err("Unexpected error :" + $!)
end
else
Err("No error")
My $!.to_s() contains large strings as follows when I puts like:
puts $!.to_s()
Output of above puts before if statement is:
myException \n
th sdfsj dsjhf sdfj \n
asdj jkds fdf j
So in if statement I want to compare the first line from output of $!.to_s() with string in double quotes.
Any suggestions how to resolve this?
That makes my eyes hurt. Please do object-oriented programming in ruby.
class MyException < StandardError; end
will define a new exception you can rescue by
begin
do_something_that_raises_expected_exception
rescue MyException
do_something_else
end
So you won't need any String comparison.
If you want to go down your road, use string.match exception_name, but I advise against it.
Another word on rescuing: Don't do rescue Exception if possible, just use rescue, because Exception captures stuff like SyntaxError too (which is likely not intended). And exceptions are frowned upon for flow control in ruby, as opposed to python.
Although I fully agree with Tass, here is how you can achieve what you want:
if $!.to_s !~ /\AmyException\s*\n/
...
or
if $!.to_s.lines.first != "myException \n"
...
But please: Don't do that.

Resources