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

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."

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.

How to handle exceptions in Find.find

I'm processing files and directories looking for the most recent modified file in each directory. The code I have works but, being new to Ruby, I'm having trouble handling errors correctly.
I use Find.find to get a recursive directory listing, calling my own function newestFile for each directory:
Find.find(ARGV[0]) { |f|
if File.directory?(f)
newestFile(f)
end
}
In the directory tree there are folders I do not have permission to access, so I want to ignore them and go on to the next, but I cannot see how to incorporate the exception handling in to the Find.find "loop".
I tried to put begin..rescue..end around the block but that does not allow me to continue processing the loop.
I also found this SO question: How to continue processing a block in Ruby after an exception? but that handles the error in the loop. I'm trying to recover from an errors occurring in Find.find which would be outside the exception block.
Here's the stack trace of the error:
PS D:\dev\ruby\> ruby .\findrecent.rb "M:/main/*"
C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `open': Invalid argument - M:/main/<A FOLDER I CAN'T ACCESS> (Errno::EINVAL)
from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `entries'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `block in find'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `catch'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `find'
from ./findrecent.rb:17:in `<main>'
How do I add exception handling to this code?
I had a look in the code where the exception is being generated and the method contains the following block:
if s.directory? then
begin
fs = Dir.entries(file)
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
next
end
... more code
Performing a horrible hack I added Errno::EINVAL to the list of rescue errors. My code now executes and goes through all the folders but I can't leave that change in the Ruby library code.
Internally find is using Dir.entries, so maybe I need to rewrite my code to process the folders myself, and not rely on find.
I would still like to know if there is a way of handling errors in this sort of code construct as from reading other code this type of small/concise code is used a lot in Ruby.
Do you get this error on your newestFile function or when you try to run File#directory??
If this happens in newestFile you can do something like this:
Find.find(ARGV[0]) do |f|
if File.directory?(f)
newestFile(f) rescue nil
end
end
This just ignores any errors and punts until the next folder. You could also do some nicer output if desired:
Find.find(ARGV[0]) do |f|
if File.directory?(f)
begin
newestFile(f)
rescue
puts "error accessing: #{f}, you might now have permissions"
end
end
end
If the error happens in the File#directory? you need to wrap that section as well:
Find.find(ARGV[0]) do |f|
begin
if File.directory?(f)
newestFile(f)
end
rescue
puts "error accessing: #{f}, you might now have permissions"
end
end
Like you mentioned if the error is occurring in the Find#find itself then you can't catch that from the block. It would have to happen inside of that method.
Can you confirm that the exception is happening in that method and not the subsequent ones by pasting a stack trace of the exception?
Edit
I was going to suggest traversing the directories yourself with something like Dir#entries so you would have that capacity to catch the errors then. One thing I am interested in is if you leave of the * in the call from the command line. I am on MacOS so I can't duplicate 100% what you are seeing but If I allow it to traverse a directory that I don't have access to on my mac it prints debug info about what folders I can't access but continues on. If I give it the * on the other had it seems to do nothing except print the error of the first folder it can't access.
One difference in my experience on the MacOS is that it isn't actually throwing the exception, it is just printing that debug info to the console. But it was interesting that the inclusion of the * made mine stop completely if I didn't have access to a folder.
You can be reactive or proactive, either works, but by testing in advance, your code will run a little faster since you won't be triggering the exception mechanism.
Instead of waiting for a problem to happen then trying to handle the exception, you can find out whether you actually should try to change to a directory or access a file using the File class's owned? and grpowned? methods. From the File documentation:
grpowned?(file_name) → true or false
Returns true if the named file exists and the effective group id of the calling process is the owner of the file. Returns false on Windows.
owned?(file_name) → true or false
Returns true if the named file exists and the effective used id of the calling process is the owner of the file.
That means your code can look like:
Find.find(ARGV[0]) do |f|
if File.directory?(f) && %w[grpowned? owned?].any?{ |m| File.send(m.to_s, f) }
newestFile(f)
end
end
Ruby will check to see if the directory entry is a directory and whether it is owned or grpowned by the current process. Because && is short-circuiting, if it's not a directory the second set of tests won't be triggered.
On some systems the group permissions will give you a better chance of having access rights if there are lots of shared resources, so that gets tested first, and if it returns true, any? will return true and the code will progress. If false is returned because the group permissions don't allow access, then owned? will test the file and the code will skip or step into newestFile. Reverse those two tests for speed depending on the set-up of your system. Or, run the code one with using time ruby /path/to/your/code then twiddle the two and run it again. Compare the resulting times to know which is faster on your system.
There are different schools of thought about whether using exception handling to control program flow is good and different languages prefer different things in their programming styles. To me, it seems like code will always run faster and more safely if I know in advance whether I can do something, rather than try and have it blow up. If it blows up in an expected way, that's one thing, but if it blows up in ways I didn't expect, then I might not have exception handling in place to react correctly, or it might trigger other exceptions that mask the true cause. I'd rather see if I can work my way out of a situation by checking the state, and then if all my attempts failed, have an exception handler that lets me gracefully exit. YMMV.
Finally, in Ruby, we don't name methods using Camelcase, we use snake_case. snake_case_is_easier toReadThanCamelCase.

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?

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.

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.

Resources