Yet another question on exceptions

E

Eric Jacoboni

Hi,

If i use File::eek:pen to open/create a file objet, and if i use a block
with it, the file will be automatically close when the block ends:

Now, considering this buggy snippet:

begin
File.open("my_file") do |fic|
fic.puts("bla")
end
rescue Exception => e
STDERR.puts(e)
exit(1)
end

Here, the "not opened for writing" exception will be raised, the block
end will never be reached and the file will remain opened, isn't it ?
To circumvent this, i have to make fic a global variable and put the
appropriate code in a ensure clause (or open a begin/ensure in the
File.open block to close the file... too bad)

So, my question is: what's the benefit of this idiom versus this one:

begin
fd = File.new("my_file")
...
rescue Exception => e
STDERR.puts(e)
exit(1)
ensure
fd.close if fd and not fd.closed?
end

Is there something i've missed?
 
R

Robert Klemme

Eric said:
Hi,

If i use File::eek:pen to open/create a file objet, and if i use a block
with it, the file will be automatically close when the block ends:

Now, considering this buggy snippet:

begin
File.open("my_file") do |fic|
fic.puts("bla")
end
rescue Exception => e
STDERR.puts(e)
exit(1)
end

Here, the "not opened for writing" exception will be raised, the block
end will never be reached and the file will remain opened, isn't it ?
To circumvent this, i have to make fic a global variable and put the
appropriate code in a ensure clause (or open a begin/ensure in the
File.open block to close the file... too bad)

So, my question is: what's the benefit of this idiom versus this one:

begin
fd = File.new("my_file")
...
rescue Exception => e
STDERR.puts(e)
exit(1)
ensure
fd.close if fd and not fd.closed?
end

Is there something i've missed?

Yes, as Grennady pointed out. The benefit of using File#open with a block
is exactly that you do not have to do the closing yourself. Less code,
less errors.

Btw, your first example is overly complex. This does the same:

File.open("my_file") do |fic|
fic.puts("bla")
end

17:53:40 [~]: ruby -e 'File.open("dsdsd") {|io| p io}' ; echo $?
-e:1:in `initialize': No such file or directory - dsdsd (Errno::ENOENT)
from -e:1
1
17:53:55 [~]:

Kind regards

robert
 
E

Eric Jacoboni

Robert Klemme said:
Btw, your first example is overly complex. This does the same:

File.open("my_file") do |fic|
fic.puts("bla")
end

Actually, i took the habit to always handle exceptions myself, hence
the block begin/rescue...

Furthermore, it was a toy exemple: i like my scripts exit with
different values for different problems.
 
D

David Vallner

D=C5=88a Piatok 10 Febru=C3=A1r 2006 18:33 Eric Jacoboni nap=C3=ADsal:
Actually, i took the habit to always handle exceptions myself, hence
the block begin/rescue...

Furthermore, it was a toy exemple: i like my scripts exit with
different values for different problems.

Well, File.open in the block version will reraise the exception precisely t=
o=20
let you do that, it's not supposed to silently ignore errors.

Isn't there a POSIX C header that defines various types of standard error=20
codes for programs to return? In case I'm not imagining things, you might=20
want to look into it and whether we can access those constants from Ruby to=
=20
do things The Right Way.
 
E

Eric Jacoboni

David Vallner said:
Well, File.open in the block version will reraise the exception precisely to
let you do that, it's not supposed to silently ignore errors.

Ok, perhaps i'm not clear or perhaps i've missed something...

Suppose i want my script exit with different values depending on the
error cases:

% ruby -e 'File.open("no.txt") {|f| line = f.gets}'
-e:1:in `initialize': No such file or directory - no.txt (Errno::ENOENT)
from -e:1
% echo $?
1
% ruby -e 'File.open("yes.txt") {|f| f.puts("bla")}'
-e:1:in `write': not opened for writing (IOError)
from -e:1
from -e:1
[titine]:~/Desktop % echo $?
1

As it's clearly not the same error, i don't want the same return
status. As i don't know how to manage this gracefully with Ruby, i'm
using a well-know idiom (at least for me...). That said, if there a
more rubywaying solution, i buy it.
 
R

Robert Klemme

2006/2/10 said:
David Vallner said:
Well, File.open in the block version will reraise the exception precise= ly to
let you do that, it's not supposed to silently ignore errors.

Ok, perhaps i'm not clear or perhaps i've missed something...

Suppose i want my script exit with different values depending on the
error cases:

% ruby -e 'File.open("no.txt") {|f| line =3D f.gets}'
-e:1:in `initialize': No such file or directory - no.txt (Errno::ENOENT)
from -e:1
% echo $?
1
% ruby -e 'File.open("yes.txt") {|f| f.puts("bla")}'
-e:1:in `write': not opened for writing (IOError)
from -e:1
from -e:1
[titine]:~/Desktop % echo $?
1

As it's clearly not the same error, i don't want the same return
status. As i don't know how to manage this gracefully with Ruby, i'm
using a well-know idiom (at least for me...). That said, if there a
more rubywaying solution, i buy it.

Actually, catching those exceptions and exiting depending on them is
perfectly ok. Although I would question the habit to always handle
exceptions yourself. Often it's not needed and one can end up handling
exceptions too far down the call hierarchy. The nice thing about
Ruby's exceptions is that they are unchecked (borrowing this term from
Java), which basically means that they are not declared. At times this
can make it difficult to figure what exceptions can be thrown from a
method but surprisingly often this is not an issue (at least not for
me, maybe in larger software systems).

Kind regards

robert


PS: A variant you could do for multiple return values to avoid lots of
similar rescue clauses:

RV =3D Hash.new(1).merge(
Exception =3D> 1,
ERRNO::Foo =3D> 2
)

begin
...
rescue Exception =3D> e
STDERR.puts e
exit RV[e.class]
end
 
D

David Vallner

D=C5=88a Piatok 10 Febru=C3=A1r 2006 19:43 Eric Jacoboni nap=C3=ADsal:
David Vallner said:
Well, File.open in the block version will reraise the exception precise= ly
to let you do that, it's not supposed to silently ignore errors.

Ok, perhaps i'm not clear or perhaps i've missed something...

Suppose i want my script exit with different values depending on the
error cases:

% ruby -e 'File.open("no.txt") {|f| line =3D f.gets}'
-e:1:in `initialize': No such file or directory - no.txt (Errno::ENOENT)
from -e:1
% echo $?
1
% ruby -e 'File.open("yes.txt") {|f| f.puts("bla")}'
-e:1:in `write': not opened for writing (IOError)
from -e:1
from -e:1
[titine]:~/Desktop % echo $?
1

As it's clearly not the same error, i don't want the same return
status. As i don't know how to manage this gracefully with Ruby, i'm
using a well-know idiom (at least for me...). That said, if there a
more rubywaying solution, i buy it.

If you want to assign your own error codes, feel free to do so. As I said,=
=20
that's why File will reraise the exception. What I wanted to emphasise is=20
that you don't need to take care of cleanup of file handles while still bei=
ng=20
able to handle errors as you want to.

There's nothing wrong with what you wrote in the original post, as long as =
you=20
keep to printing the error message to standard error to keep with=20
conventions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top