Ruby error handling similar to Perl - ruby

In Perl I handle errors with:
eval{
};
if($#) {
}
In Ruby I have used:
begin
rescue Exception => e
sleep 2
end
Is this correct way to do in Ruby, and will this work if the Internet or a server goes down?
If the above is wrong are there any ways of doing it in Ruby similar to Perl?

If you need to rescue from a possible exception, you got it right. You have to:
begin
# do some useful but dangerious work
rescue StandardError => e
# something went wrong, try to work around it;
# object "e" containts usefull error information
ensure
# anyway, cleanup after doing what you've started
end
P.S. If the server goes down literally (e.g. the hardware is off) – no exception code-handling will help you out.
P.P.S. The Internet probably won't go down anytime soon.

Related

Ruby exception hashrocket; is there an alternative json-styled way to catch an exception?

So, for hashes in Ruby you can use hashrockets like this:
corned_beef = {
:ingredient1 => "beef",
:ingredient2 => "potatoes"
}
or the more concise json-ish style.
corned_beef = {
ingredient1: "beef",
ingredient2: "potatoes"
}
Is there a json-ish styled way to catch Ruby exceptions? The normal way is this:
begin
# ...blah blah...
rescue ActiveRecord::RecordNotFound => e
logger.debug { "Where's da beef?" }
rescue => e
logger.debug { "#{e.message}\nBacktrace Begin:\n #
{e.backtrace.join("\n")}" }
else
# ...blah blah...
end
I've started to hate seeing hashrockets in my code, even for this. Someone please educated me.
EDIT:
For some reason, this has attracted comments from the kind of people who have code-religious arrogant condescending judgement. This is a forum for questions, if you don't like the question, kindly close your window. Ruby was optimized for programmer happiness. My question is seeking what I deem cleaner sexier code. What is not wanted is an expression of lots of opinions that do nothing toward helping achieve an answer. I am a good programmer with legacy code that has been in production serving billions and is probably older than most of you. Please stop shoveling pointless opinions if it doesn't answer the question. So far, it doesn't look like what I'm seeking exists. That's fine.
If you absolutely want to get rid of it, you can fall back to some of Ruby's Global Variables, specifically
$!
The exception information message set by 'raise'.
$#
Array of backtrace of the last exception thrown.
begin
raise ArgumentError, 'Your argument is invalid'
rescue ArgumentError
puts "#{$!.message}\nBacktrace Begin:\n#{$#.join("\n")}"
# or
puts "#{$!.message}\nBacktrace Begin:\n#{$!.backtrace.join("\n")}"
end
I've never used any of the globals in an any real applications, so not sure what type of things you might want to watch out for (if multiple threads throwing different errors simultaneously* might be an issue, for example).
No, AFAIK this is the syntax required for creating a reference to the caught exception.

Is it possible to use `retry` keyword inline with `if`?

I have to surround everything with begin-rescue-end block. I've written code that looks like:
begin
bet = Dialogs.enter_your_bet
approx.calculate_average_profit(bet)
approx.print_profits_table
retry if Dialogs.play_again?
rescue;retry
end
The line retry if Dialogs.play_again? caused the following error:
./main:14: Invalid retry
./main: compile error (SyntaxError)
Is it possible to make this kind of inline retry to work with if clause without regular if-end multiline approach?
retry works in rescue blocks (or in iterators). And it works with if. Try this:
begin
bet = Dialogs.enter_your_bet
approx.calculate_average_profit(bet)
approx.print_profits_table
rescue
retry if Dialogs.play_again?
end
Ok, thank you all for answers! I understood what was a problem, but your workarounds wasn't what I need. Actually, rescue part was for restarting input prompt in case of illegal input from a user, while my 'retry' inside begin was to restart block from another user input (y/n question).
So after some investigation, this code would work flawlessly:
begin
loop do
bet = Dialogs.enter_your_bet(gapes[0],gapes[1])
approx.calculate_average_profit(bet)
approx.print_profits_table
break if !Dialogs.play_again?
end
rescue;retry
end
And again, thanks for been so awesomely active community. Take care!
redo is used for control flow.
Quoting the docs: "In Ruby 1.8 you could also use retry where you used redo. This is no longer true, now you will receive a SyntaxError when you use retry outside of a rescue block. See Exceptions for proper usage of retry."

ruby - how can i still do something when there's error (example: NameError)

i manage to do "something"(like deleting files,etc) when exit or exit! is called from Object.
by changing the method exit and exit! inside Object class. (at_exit is too unreliable)
but then the "something" never execute if there's error such as NameError, etc.
is there way so i can make "something" that still execute if there's error.
(any error possible if necessary).
something like at_exit but works with all errors
thanks in advance for assistance. and forgive me if there's already question ask for this.
i do search a lot before asking here. and found nothing.
edit: i don't know where the original author of the code place the method. since the original author load it from dll files in the exe program the author used for launcher. (i can only edit After the original code take place...). so i think i need another approach for this... but i manage to make a workaround for my problem... by putting begin rescue in other places that send data to the original object. i filter the data send and throw my own error before it reach the main program... so i guess this works too.
Borrowing from an answer on a different thread, and definitely along the lines of what Marek commented, this is how you should handle errors in Ruby:
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
end
Original credit: Begin, Rescue and Ensure in Ruby?

Alternative to "rescue Exception"

I get some unexpected errors on occasion such as timeout errors, 503 errors, etc. There are errors that I don't even know about that I may receive. I can't account for all of them by doing something like:
rescue Timeout::Error => e
It's also a terrible idea to rescue Exception.
What is an alternative that I could use? I want my code to rescue all of them when there is an error; if there is no error, I need it to be avoided. I want to be able to kill my script but not skip over syntax errors, etc.
You can rescue for StandardError, or simply rescue, which are the same:
rescue StandardError => e
# or
rescue => e
You can see in the following table which exceptions are rescued from StandardError - Note that they are a subset from Exception, and conceitually should be errors that are OK to be catch.
Of course you can have gems that defines exception in the wrong place, but this should not happen in well-developed gems.
(source: rubylearning.com)
I personally like to rescue only exceptions I know how to handle, except when it is to add in a log/backtrace system to consult the errors later. If this is the case, I usually rescue StandardError

How to make script continues to run after 'raise' statement in ruby?

I'm checking to see if there is any error message in a log file. If an error message found in a log file, then I use 'raise' statement to report the founding. However ruby stops running after executed the 'raise' statement, even when I use 'rescue'. I'd like script continue checking next log file for error after the 'raise' statement, but not sure how. Any help would be appreciated!
logs_all = s.sudo "egrep -i '#{error_message}' #{log_file}"
logs_all.each do |hostname, logs|
unless logs.empty?
puts line, "Unhappy logs on #{hostname}", line, logs
happy = false
end
begin
raise "Unhappy logs found! in #{log_file}" unless happy
rescue raise => error
puts error.message
end
end
Your rescue statement doesn't look right:
rescue raise => error
should be:
rescue => error
which is equivalent to:
rescue StandardError => error
If you rescue an exception and don't re-raise it, ruby will continue on. You can easily verify this with something like:
3.times do |i|
begin
raise "Raised from iteration #{i}"
rescue => e
puts e
end
end
You'll see that three lines of output are printed.
As a general practice though, you should avoid rescuing Exceptions unless you're going to do something at runtime to rectify the problem. Rescuing and not re-throwing exceptions can hide problems in your code.
And more generally, please follow Sergio's advice above and don't use exceptions as control flow.
Further Reading
I recommend looking over the Exceptions section of this ruby style guide - it will give you some quick pointers in the right directions.
Also, this answer about never rescuing Exception
You are using exceptions as control flow mechanism. Don't.
What is it that you want to do with unhappy logs? Print them? To a file, maybe? Do that, don't raise exceptions.

Resources