Rescuing and catching at the same time - ruby

If I wanted to both rescue a potential error and catch a potential throw, how should I nest them? Are the two below equivalent, and it is just a matter of preference?
begin
catch(:some_throw) do
...
end
rescue SomeError
...
end
catch(:some_throw) do
begin
...
rescue SomeError
...
end
end

Its an opinion-based question, and one can argue either way. So, in my opinion...
If you are planning to return a value via throw, then, second option seems useful as it will let you rescue an error and throw some kind of default value.
Even if you were using throw and catch just to manage loop iterations and breaking out of it on certain conditions, second option still seems more readable and encapsulates all the logic inside the catch block.

They are not entirely equivalent. With the first alternative, the catch will only intercept values being thrown in the begin clause, while the second includes ones from rescue too.
That being said, if you are in the case, when the two are equivalent (aka you don't throw :some_throw in the rescue clause):
The argument for the first alternative would be that we tend to think that begin - rescue blocks enclose "regular" statements. throw - catch being rarely used and having a non-error semantic are more "regular"-y.
The argument for the second alternative would be that one should strive to enclose the least amount (only the possibly failing) of code in begin - rescue clauses.
Personally, I like the first one better.

Related

Using error raising to control if/else statement

Learning ruby here, how can I use if an error was raised to control an if/else statement. In ruby pseudocode it would be something like.
if block.call raise?
#if block doesn't exist and an error is raised do this
else
#if block does exist do this
end
You could use an inline rescue like this:
if block.call rescue false
else
end
Though you shouldn't do this. The reason being that this will rescue any error in the block. Say you you mispelled some word in the block definition. Your code will act like this is intended, and you might have a hard time tracking it down.
In this case, to check if the block was given you can simply say if block. If you're using yield instead of the &block parameter, you can use the special method if block_given?.
Of course you can achieve conditional logic using rescues, but that's not what they're intended for, and it's not making things much simpler in most cases.

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 Exceptions in a loop

I have a ruby script that loops through a list of shortened urls (around 2,000 - 3,000 at a time). At the moment everything is peachy until a hit a url that is malformed, timedout etc. When an error occurs my script dies. How can I setup my loop to skip to the next record when/if such an error occurs.
my loop looks like this:
blah.foo do |barurl|
mymethod(barurl)
my mymethod looks like this:
def mymethod(barurl)
begin
stuff
...
return expandedurl
rescue
return "Problem expanding link"
end
end
Should my begin/end logic be wrapped around my loop instead of the method?
Because you need to skip the malformed url, you should use the exception message to control the loop
blah.foo do |barurl|
begin
mymethod(barurl)
rescue YourTypeOfException
next
end
end
and inside the method raise the exception
def mymethod(barurl)
stuff
...
raise YourTypeOfException, "this url is not valid"
...
end
I found the existing answers unsatisfying, and reading the documentation suggests to me that the OP had something more like the example suggested there in mind:
[0, 1, 2].map do |i|
10 / i
rescue ZeroDivisionError
nil
end
#=> [nil, 10, 5]
The docs specifically note that a rescue block permits the loop to continue on a caught exception (as indicated by the example).
Yes. All your method does is consume the exception and return another arbitrary object in order to indicate an error.
Your method shouldn't handle its own exceptional conditions. It is just rude on its part to make assumptions about how the caller will react.
blah.foo do |url|
begin
my_method url
rescue
next
end
end
Whether to skip to the next URL or print a message is not a decision the method should be making. Its only concern should be working with the URL.
With that said, you should simply let it propagate and only rescue from it when you can actually deal with it. Don't rescue from a TimeoutError if all you can do is return :timeout.
Do rescue when you need to clean up resources or simply let the user know an error occurred.
Also, rescuing from every possible error just to make them go away is a nice way to introduce bugs. Be as specific as possible.
having exception handling within your method is proper way of doing it, so your implementation is fine
i can only point some ruby sytax sugar to you:
def some_method
# here goes the code
rescue Exception => e
# here goes specific exception/error handling
rescue
# here goes error handling (not Exception handling though!)
else
# do this block when no exceptions are raised
ensure
# do this every time
end
btw you don't need return statements, last value of code block is always returned implicitly
ah i guess i misread your question in the "how to skip next record"
if you want to skip the record after current one that was incorrect you would have to return error code from your parsing method and set up skipping within your loop using break or next keywords
It should be inside the loop, so the loop structure isn't exited on an exception. But it looks like it already is--if you're rescuing inside the method that causes the exception, the loop should already continue normally, because it shouldn't be seeing the exception.

ignoring errors and proceeding in ruby

whenever there is an exception call that is raised, the script terminates.
do i have to resort to putting each action ? it gets very complicated fast.....
begin
#someaction
begin
#someaction2
rescue
end
rescue
end
You could use some sort of AOP mechanism to surround every method call with exception handling code (like Aquarium: http://aquarium.rubyforge.org/), or put rescue nil after every line of code, but I'm guessing that if you need to do that, then the exceptions raised are not really signalling exceptional situations in your app (which is bad) or you want to try to continue even in a situation where there's really no point to do so (which is even worse). Anyway I'd advise you to reconsider what you really need to do, because it seems to me that you are approaching the problem in a wrong way.
It's difficult to give a specific answer because I don't know what your program does.
But in general terms, I find that the best way to deal with this is to put the code that could fail into one or more seperate methods (and perhaps the method name should reflect this).
This has a number of advantages. First of all, it means that the rest of your code doesn't have to be hedged around with exception handling; secondly, if the "dangerous" actions are carefully split up into logical groups, you may be able to do exception handling on the method, not the actual actions. Stupid example:
my_list = get_list("one") # perfectly safe method
my_list.each do |x|
begin
x.dangerous_file_method() # dangerous method
rescue
x.status = 1
end
end

What is the difference between Raising Exceptions vs Throwing Exceptions in Ruby?

Ruby has two different exceptions mechanisms: Throw/Catch and Raise/Rescue.
Why do we have two?
When should you use one and not the other?
raise, fail, rescue, and ensure handle errors, also known as exceptions
throw and catch are control flow
Unlike in other
languages, Ruby’s throw and catch are not used for exceptions.
Instead, they provide a way to terminate execution early when no
further work is needed.
(Grimm, 2011)
Terminating a single level of control flow, like a while loop, can be done with a simple return. Terminating many levels of control flow, like a nested loop, can be done with throw.
While the exception mechanism of raise and rescue is great for abandoning execution when things go wrong, it's sometimes nice to be able to jump out of some deeply nested construct during normal processing. This is where catch and throw come in handy.
(Thomas and Hunt, 2001)
References
Grimm, Avdi. "Throw, Catch, Raise, Rescue… I’m so Confused!" RubyLearning Blog. N.p., 11 July 2011. Web. 1 Jan. 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/.
Thomas, Dave, and Andrew Hunt. "Programming Ruby." : The Pragmatic Programmer's Guide. N.p., 2001. Web. 29 Sept. 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html.
I think http://hasno.info/ruby-gotchas-and-caveats has a decent explanation of the difference:
catch/throw are not the same as raise/rescue. catch/throw allows you to quickly exit blocks back to a point where a catch is defined for a specific symbol, raise rescue is the real exception handling stuff involving the Exception object.
https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise offers an excellent explanation that I doubt I can improve on. To summarize, nicking some code samples from the blog post as I go:
raise/rescue are the closest analogues to the throw/catch construct you're familiar with from other languages (or to Python's raise/except). If you've encountered an error condition and you would throw over it in another language, you should raise in Ruby.
Ruby's throw/catch lets you break execution and climb up the stack looking for a catch (like raise/rescue does), but isn't really meant for error conditions. It should be used rarely, and is there just for when the "walk up the stack until you find a corresponding catch" behaviour makes sense for an algorithm you're writing but it wouldn't make sense to think of the throw as corresponding to an error condition.
What is catch and throw used for in Ruby? offers some suggestions on nice uses of the throw/catch construct.
The concrete behavioural differences between them include:
rescue Foo will rescue instances of Foo including subclasses of Foo. catch(foo) will only catch the same object, Foo. Not only can you not pass catch a class name to catch instances of it, but it won't even do equality comparisons. For instance
catch("foo") do
throw "foo"
end
will give you an UncaughtThrowError: uncaught throw "foo" (or an ArgumentError in versions of Ruby prior to 2.2)
Multiple rescue clauses can be listed...
begin
do_something_error_prone
rescue AParticularKindOfError
# Insert heroism here.
rescue
write_to_error_log
raise
end
while multiple catches need to be nested...
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
A bare rescue is equivalent to rescue StandardError and is an idiomatic construct. A "bare catch", like catch() {throw :foo}, will never catch anything and shouldn't be used.

Resources