What is the clearest way to open a file and do "rescue" when it cannot be opened in Ruby
Asked Answered
A

2

9

I am now dealing with this issue by using the following code

begin
  File.open(filename, 'r')
rescue
  print "failed to open #{filename}\n"
  exit
end

Is there any way to do it more easily like Perl 'open (IN, $filename) || die "failed to open $filename\n"' Thanks.

Ageold answered 6/4, 2014 at 18:44 Comment(13)
That feature of Perl is horrible because it allows you to silently ignore any errors. Ruby is really doing the right thing here.Hopkins
open || die does not "silently ignore any errors". In fact, the Ruby code above is merely printing an error message and continuing on, where the Perl example stops execution of the program.Kimmel
@AndyLester incorrect. The call to exit raises the SystemExit exception and does NOT continue on unless it is explicitly caught. ruby-doc.org/core-2.1.1/Kernel.html#method-i-exit. The only thing I see the example doing "wrong" is not setting the appropriate exit status.Coprophilia
At the time I answered the question 16 minutes ago, the exit was not in the question. The exit was added five minutes after I posted my comment above. Here's the original question that I responded to. stackoverflow.com/revisions/22898310/1Kimmel
Yes you are right I added it later based on your comments. Sorry for this issue. I have mentioned in the question now. Thanks.Ageold
@amesee, Non-ruby programmer here. Is rescue an exception "catch"? Would would happen if the rescue block wasn't there? It looks to me like the OP is trying to replicate the default behaviour!Loyceloyd
Yes rescue is analogous to catch in languages like C++ and Java. If the rescue block wasn't there then the program will print out the exception and just end since the exception wasn't caught (again, similar to C++ and Java). What do you mean by "default behavior?"Coprophilia
@amesee: What I think Ikegami means, and I agree with, is that what the OP is seeking is just a bare File.open(filename, 'r'), which would terminate the program with a message just like the Perl code he shows.Weatherford
@Weatherford sure that works if the OP is ok with outputting the message in the Exception. If a different message needs to be output the exception should be caught and handled in the rescue block.Coprophilia
I believe the correct thing to do is check to see if the file is readable prior to trying to open it. Don't rely on exceptions to provide flow-control in your program. Exceptions are for handling unexpected conditions; you know its possible to have an unreadable file so test for it.Pontiac
@the Tin Man, That's backwards. Doing two checks is bad. The answer could change between the first check and open, so the first check is useless.Loyceloyd
@theTinMan there is a chance that the file can become unreadable after it is determined readable (by File::exists? or File::readable? for example) and before the attempt to open with File::open or File::new. It's also not flow control; We're trying to open a file. The failure to open a file is an unexpected condition which is why it raises the Exception in the first place.Coprophilia
I understand the argument that the file could become unreadable in the (tiny) instant between the test and opening it, however, in 30+ years programming I've never seen it happen. There are other exceptions and issues that could occur after the file is opened that wouldn't be handled either. If you're going to handle the exception, go all-in and handle permission and IOError failures, and maybe even Interrupts depending on the task. The assumption that only one thing can go wrong, and handling it alone, is dangerous/naive because it lulls people into a false sense of security.Pontiac
A
9
File.open("doesnotexist.txt", 'r')

Is enough. If the file does not exist, this will raise an Exception. This is not caught, so the program exits.

# =>test6.rb:1:in `initialize': No such file or directory @ rb_sysopen - doesnotexist.txt (Errno::ENOENT)
Anywhere answered 6/4, 2014 at 20:24 Comment(1)
What about Errno::EISDIR and Errno::EACCES?Evolutionist
C
7

I'm not sure what you're trying to accomplish other than trying to write Perl with Ruby. You have to consider the fact that Perl's open returns "nonzero on success, the undefined value otherwise. If the open involved a pipe, the return value happens to be the pid of the subprocess."

Ruby's File::open however raises an exception Errno::ENOENT which is completely different behavior than returning some Ruby equivalent to undefined (I guess nil).

Write Ruby if your tool is Ruby. Write Perl if your tool is Perl. Don't write Perl if your tool is Ruby. Don't write Ruby if your tool is Perl.

UPDATE:

As @steenslag answered, simply not rescueing the exception is sort of an equivalent since the exception will act as an implicit die equivalent of Perl.

File.open filename

However you are now restricted to outputting the exception's message value as the output. For example:

in `initialize': No such file or directory @ rb_sysopen - filename (Errno::ENOENT)

If you need to output your own message you'll have to catch the exception and handle it. This is the behavior of File.open so you'll have to use it as intended. I would also argue you should explicitly specify the exception type to catch, as well as write to $stderr:

begin
  File.open filename do |f|
    puts f.gets
    # ...
  end
rescue Errno::ENOENT => e
  $stderr.puts "Caught the exception: #{e}"
  exit -1
end

Which would output the message specified to standard error and exit the program with an non-zero status (which is the behavior of die):

Caught the exception: No such file or directory @ rb_sysopen - filename

For the record I absolutely love Perl. Ruby actually shares a lot of similar characteristics of Perl (among other languages and paradigms). But when I'm writing Perl I use Perl idioms, and when I write Ruby I use Ruby idioms. The reason I'm stressing "don't write Perl with Ruby" so much is because languages have their idioms for a reason and you can end up making your colleagues mad if you don't do things "the Ruby way" or "the Perl way" with each respective language.

Coprophilia answered 6/4, 2014 at 19:44 Comment(3)
Your diatribe is rude and unnecessary. I don't see any attempt to "write perl with ruby". The OP is asking about a simple way to achieve a similar result in Ruby to the Perl code that he shows.Weatherford
@Weatherford Sorry if it seemed rude, My intention wasn't to attack or force something on anyone. The OP is literally asking how to do something in ruby that is usually done in perl so it's pretty clear the OP is trying to write perl with ruby. I also pointed out the differences between the behavior of the perl function and ruby method in order to explain why "the perl way" isn't appropriate. If you're writing ruby you should do it the ruby way: Either let it raise the exception and end it all, or catch the error and handle it appropriately.Coprophilia
Ruby raises other exceptions besides ENOENT on a failed open. "Errno::EACCES: Permission denied", for instance.Pontiac

© 2022 - 2024 — McMap. All rights reserved.