Difference between $! versus a variable with rescue - ruby

When rescuing from an exception, there are two ways to refer to the raised exception:
begin
...
rescue Exception => e
handle_the_error(e)
end
and
begin
...
rescue Exception
handle_the_error($!)
end
I believe they are interchangeable, but are they? Is there any situation where one should be used over the other?

I also think these snippets are interchangeable. But you should always prefer explicit variables to thread-global magic.
One case where $! magic var is handy:
result_or_error = perform_some_operation() rescue $!
For those who don't know that this line means:
It's so called "inline rescue". Format is this:
<expr1> rescue <expr2>
First, expr1 is evaluated. If no exception was raised, its value is returned. But if there was an exception, then expr2 is evaluated and its value returned.
So, in this case, if perform_some_operation() raised an exception, variable result_or_error would be set to an instance of that exception (because $! returns last error).

Related

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.

Catch a parameter error within a function?

Is it possible for a method to handle an error that happens in a parameter?
e.g.
def add(arg1, arg2)
# If the value causes an error, turn it into 0
rescue nil
0
end
arg1 + arg2
end
add(2, 2+nil) => 2
I understand that shielding parameters and eval-ing it later can be a solution, but in my scenario it becomes too cumbersome to do that.
The problem with this is the error occurs on the line where you provide the arguments as arguments are evaluated prior to the method being called.
Every method call is roughly equivalent to this:
arg1 = 2
arg2 = 2 + nil
add(arg1, arg2)
In this case you can see how the argument itself produces an error. The only way to defer the evaluation of an argument is via a block:
add(2) do
2 + nil
end
Changing your definition to:
def add(v)
v + yield
rescue
v + 0
end
Capturing all exceptions blindly is usually a super bad plan, you may have some kind of fundamental mistake in there. It's best to avoid capturing exceptions unless you have expectations as to what kind they will be.
Here's a version of the method with no exception handling:
def add(*args)
args.map(&:to_i).inject(:+)
end
Now nil.to_i returns 0 so you're covered:
add(2,2,nil)
That will evaluate to 2+2+0 internally.
Generally it's a bad plan to try and bury errors like 2+nil. That's a fundamental mistake that should be addressed. If you wanted to handle nil values you would use .to_i to map them down to something usable.
In this case your add method isn't still called and you have to rescue in the caller.

Why does a SASS exception become `nil` by calling `backtrace` on it?

The code below defines a hook Kernel#at_exit to capture an exception and do things at exit, and then raises a Sass::SyntaxError by passing an invalid SASS string.
require "sass"
module Kernel
at_exit do
puts "--Before backtrace"
p $!
$!.backtrace
puts "--After backtrace"
p $!
end
end
Sass::Engine.new("Invalid {sass").render
The output it gives is as below:
...
--Before backtrace
#<Sass::SyntaxError: ...>
--After backtrace
nil
...
It indicates that $! was a Sass::SyntaxError, but it became nil right after backtrace has been called on it. Why did $! change just by calling backtrace on it?
This effect does not seem to happen when Sass::SyntaxError is raised manually as follows:
raise Sass::SyntaxError.new("foo")
or when a different type of error is raised (may be wrong).
Edit
I am not sure, but probably sass manipulates the backtrace using set_backtrace when a sass error is raised. This is to provide information about where the sass syntax error was caused in a sass file. And the different behaviour between manually raising an error and programatically raising an error is reminiscent of a bug in Ruby 2.1 that half-way implemented backtrace_locations, but returned nil in some cases. I have a broad guess that these factors are interfering, but am not sure.
The reason it is happening is because this method overrides backtrace method:
def backtrace
return nil if super.nil?
return super if sass_backtrace.all? {|h| h.empty?}
sass_backtrace.map do |h|
"#{h[:filename] || "(sass)"}:#{h[:line]}" +
(h[:mixin] ? ":in `#{h[:mixin]}'" : "")
end + super
end
Where sass_backtrace is an array of hashes populated in initializer. Line which causes $! to be nil is:
return super if sass_backtrace.all? {|h| h.empty?}
This happens only when all? returns nil. I did some fiddling with it, and I found out that the problem always occurs, when we call any iterator which doesn't finish the whole iteration (all? terminates iteration when encounter the first not satisfying element). The problem might be simply reproduced with:
at_exit do
p $! #=> #<RuntimeError: hello>
[<any_non_empty_array>].all? {false}
# Those would break $! as well
# [<ANA>].any? {true}
# [1,2,3].find {|n| n.even?}
# Those will not break $!
# [<ANA>].any? {false}
# [<ANA>].all? {true}
# [1,2,3].find {|n| n > 4}
p $! #=> nil
end
raise 'hello'
The only reason I can think of why it would work like that is that ruby loops are controlled internally with exceptions. When iteration is to be stopped, it is done with special type of exception being raised and rescued outside the loop.
My guess is that Ruby creators didn't want this control exception to be visible in $! variable, since this would suggest that something went wrong, and decided to set it back to nil.

Understanding syntax of the statement "rescue ErrorType1, ErrorType2 => ex"

A quick query:
How would a Java programmer will understand the following Ruby statement:
rescue ErrorType1, ErrorType2 => ex
That is, I want to put brackets/parenthesis around it explicitly.
So, is it?
rescue(ErrorType1, {ErrorType2 => ex})
or,
rescue({[ErrorType1, ErrorType2] => ex})
or, something else...
About the syntax:
rescue ErrorType1, ErrorType2 => ex
Please note following:
There is no hash involved
'rescue' is not a method, you can't even write it as rescue(ErrorType1, ErrorType2 => ex)
Ruby places a reference to raised associated exception into the
global variable $!.
In the above form, the 'rescue' takes a special argument where
you give the name of a local variable to receive the matched
exception, which is more readable then using $!.
Now, look at the syntax again...
rescue is a control structure with it's own syntax, it's not a method call, so your 2nd and 3rd code blocks are invalid syntax, you aren't not passing any arguments.
rescue <exception-class1>[, <exception-class2>] => <a variable to assign the exception to>
so when doing rescue TypeError, StandardError => my_exception it will catch any TypeError or StandardError exception that is raised and assign it to the my_exception local variable.
I suggest the recently translated Ruby Hacking Guide (search for "rescue").
Look at the below code :
begin
a=1/0
rescue => e
p e.class
p defined?(e)
end
# >> ZeroDivisionError
# >> "local-variable"
Where e is a local variable to that exception handling block. In Ruby local variables are created using assignment operation,but in case of exception handling,reference to the currently raised exception is assigned to the local variable e using the hash rocket(=>),instead of =. This is as per design. Don't think of that it is a Hash.
In Ruby we use one or more rescue clauses to tell Ruby the types of exceptions we want to handle.If you write a rescue clause with no parameter list, the parameter defaults to StandardError. Each rescue clause can specify multiple exceptions to catch. At the end of each rescue clause you can give Ruby the name of a local variable to receive the matched exception. The parameters to the rescue clause can also be arbitrary expressions (including method calls) that return an Exception class. If we use raise with no parameters, it re-raises the exception.Handling an Exception
Out of three of your codes only valid is rescue ErrorType1, ErrorType2 => ex. Others will give you syntax error.
Hierarchy(partial) :
StandardError
|
IndexError
|
KeyError
You can specify the error class names as arguments to the rescue list,in any order.On runtime ruby will pick up the correct one from the list. Look the code below :
begin
a = {}
a.fetch(:b)
rescue StandardError,KeyError,IndexError => e
p e.class
end
# >> KeyError
begin
a = {}
a.fetch(:b)
rescue KeyError,StandardError,IndexError => e
p e.class
end
# >> KeyError
If you think,you would tell Ruby interpreter on runtime, which one to match first using paren from the argument list,Ruby will not allow you to do so,in return it will throw to you syntax error. Same also for below :
begin
a = {}
a.fetch(:b)
rescue StandardError => e1
p e1.class
rescue IndexError => e2
p e2.class
rescue KeyError => e3
p e3.class
end
# >> KeyError
Note: If we want to catch more exception classes, we can just write them in line. When we want to handle different errors differently, we can specify several rescue clauses.Ruby Hacking Guide

Waiting for an element without throwing an exception if timed out

I know of the method Element#wait_until_present(t), but if this method times out it throws a timeOut exception.
Is there a method that just waits for t seconds and then returns true if the element became present or false otherwise?
I know it can be done with a simple begin..rescue..end statement, but I'm looking for something that doesn't use exceptions.
You can write a short-hand rescue clause like this:
element_present = browser.element.wait_until_present rescue false
puts "element not present" unless element_present
This does however result in a false value on any Exception at all and not just with TimeoutError. I still prefer to use it since if there's any Exception at all then it would be safer to assume that the element was not present.
Looks like there is no other method that will do what i'm looking for ,
so here is the simplest method to achieve this :
#check method for Element#wait_until_present(t)
def check_if_present(element,t)
raise ArgumentError, 't must be a number ' unless t.is_a? Numeric
begin
element.wait_until_present(t)
true
rescue Watir::Wait::TimeoutError
false
rescue
raise "Something wrong with the element"
end
end
If you do not want an exception the below code can be handy:
sleep 'your time here' eg: sleep 20 - this will wait for 20 secs.
then check for your element now:
'your element'.exists? -this will return true/false
you will not get an exception this way.
Another best way is to write your wait_for_load method based on your needs.

Resources