予期せぬ結果が生じたときには 例外 が発生される。
文法的にはエラーのない以下のプログラムを動かしてみよ。
#!/usr/bin/env ruby # coding: euc-jp file = ARGV.shift || "nonexistent.txt" open(file, "r") do |f| while line = f.gets print line end end
実行すると例外が発生する。
./openerr.rb:4:in `initialize': No such file or directory - nonexistent.txt (Errno::ENOENT)
おみくじプログラムの例に戻る。 改良すべき2つの点,
にはそれぞれ落し穴がある。
バックアップファイルへの移動ができるかどうかを確かめようとして バックアップファイルの存在性と書き込み可能性を確かめたくなるが, 実際にはこれは「バックアップファイルを作れるかどうか」には 関係ない。以下の実験で確認できる。
mkdir dirtest cd dirtest touch foo foo.bak chmod -w foo.bak (書き込み禁止にする) echo hoge > foo.bak zsh: permission denied: foo.bak (エラーになる) mv foo foo.bak override r--r--r-- yuuji/wheel for foo.bak? n (警告が出る。ここではnと答える) irb irb> File.rename("foo", "foo.bak") => 0 irb> exit ls -lF total 0 -rw-r--r-- 1 yuuji wheel 0 Jun 8 13:12 foo.bak
ファイルをrename
する場合は,
移動先ファイルを修正するのではないので移動先ファイルのパーミッションは
関係ない。元のファイルを消してそのファイルを作る権利があるかが
意味を持つので,ディレクトリに書き込み権があるかが意味を持つ。
touch foo foo.bak chmod -w . (ディレクトリを書き込み禁止にする) irb irb> File.rename("foo", "foo.bak") Errno::EACCES: Permission denied - foo or foo.bak from (irb):1:in `rename' from (irb):1
これは,ファイルを新規作成するときも同様の要件となる。
以上のことから,おみくじの例での改良2点,
は,ディレクトリへの書き込み権限を検査する必要があることが分かる。
#!/usr/bin/env ruby # coding: euc-jp #Foretune teller - version 1 ftune = %w,大吉 吉 中吉 小吉 半吉 末吉 小凶 凶, outfile = "fortune.txt" srand result = ftune[rand(ftune.length)] if test(?w, ".") then if test(?s, outfile) then ext = File.extname(outfile) bak = File.basename(outfile, ext)+".bak" File.rename(outfile, bak) end open(outfile, "w") do |f| f.printf("今日の運勢は %s\n", result) end STDERR.puts "#{outfile}を見よ。" end
しかしこれでも不十分で,ファイルシステムが溢れるなど,
書き込みに失敗する要因は他にいくらでもあり,それらに対する
事前予想を if
で書き連ねてもプログラムの本筋が見づらくなる。
このような場合begin〜rescue〜end
構文を用いる。
エラーが起きない理想的な場合の処理のみをまず書き,エラーが起きた
場合の処理をまとめて別箇所に書くことで,処理の流れがはっきりし,
エラーが起きた場合の対処ももれなく書くことが容易になる。
#!/usr/bin/env ruby # coding: euc-jp #Foretune teller - version 2 ftune = %w,大吉 吉 中吉 小吉 半吉 末吉 小凶 凶, outfile = "fortune.txt" srand result = ftune[rand(ftune.length)] begin if test(?s, outfile) then ext = File.extname(outfile) bak = File.basename(outfile, ext)+".bak" # File.rename(outfile, bak) end open(outfile, "w") do |f| f.printf("今日の運勢は %s\n", result) end STDERR.puts "#{outfile}を見よ。" rescue abort(<<MSG) 書き込みに失敗しました。ディレクトリへの書き込み権限を確認して下さい。 MSG end
この場合は例外発生時に復旧せず,異常終了している。
そのためのメソッドが abort
である。