Quoting Erik Veenstra <google / erikveen.dds.nl>:

> (By the way, in your example, you use an exception to do some
> business logic: "If the file doesn't exist, create it". In OO
> theory, this isn't the right way to go...)

Sometimes optimistically trying an operation and catching exceptions
is the only way to do something atomically.

 data = nil
 if File.exist? 'data.txt'
   File.open ( 'data.txt', 'r' ) { |stream|
     data = stream.map { |line| MAPPING[line.chomp] }
   }
 else
   File.open( 'data.txt', 'w' ) { |stream|
     MAPPING.keys.each { |key| stream.puts key }
   }
   data = MAPPING.values
 end

Elsewhere, at just the wrong moment:

 $ rm data.txt

Then:

 theory-meets-reality.rb:3:in `open`: No such file or directory -
data.txt (Errno::ENOENT)
         from theory-meets-reality.rb:3
 zsh: exit 1     ruby theory-meets-reality.rb

I general you want to avoid a check-then-do idiom with IO
operations, because:

 1) it introduces a race condition

 2) the check may not be reliable for other reasons (c.f. the
troubles with access() on POSIX systems, or on Windows,
GetEffectiveRightsFromAcl, particlarly with Samba shares in certain
configurations)

Of course, even my exception-based version wasn't free of race
conditions -- see if you can spot the others that I didn't address.

-mental