3.5. 掲示板完成(バグ無し版)


やっと問題の起こらない(はず)の掲示板の完成です。
一応形だけできた状態(2章)から結構かかりましたね。
このようにプログラミングとは特例のような事を常に考えなければなりません。
それを行わないとセキュリティーホールと言われるものができてしまったりします。
特にCGIはネットワークを通じてたくさんの人が扱うので注意が必要です。
と、年寄りのお説教みたいのはさておいてプログラムを見てみましょ。

※ごとけんさんにアドバイスをいただき、おかしな部分があったので1999/4/19にこのページは修正しました。前のページも一応取っておいてあります。比較したい方は間違いのある 3.5. 掲示板完成(バグ無し版)

-- プログラム 始まり -- bbs2.cgi

#!/usr/local/bin/ruby

print "Content-type: text/html\n\n"

require "cgi-lib"

input = CGI.new

require "kconv"



#初期設定

CR = "\015"

LF = "\012"

EOL = CR + LF

BR = "<br>"

FILENAME = "bbs2.log"

LOCKFILENAME = "bbs2.log.lock"

MAXLOG = 10

$log = []



#ファイルロッククラス作成

class FileLockDir

  def lock(lockdir)

    begin

      Dir.mkdir(lockdir)

      lockflag = true

    rescue Errno::EEXIST

      lockflag = false

    end

    return(lockflag)

  end



  def unlock(lockdir)

    begin

      Dir.rmdir(lockdir)

      lockflag = true

    rescue

      lockflag = false

    end

    return(lockflag)

  end

end



#ログファイルの読みこみ関数

def read_log_file

  c = 0

  begin

    fh = open(FILENAME)

  rescue Errno::ENOENT

    return

  end



  fh.each{|l|

    $log[c] = l

    c += 1

    if c >= MAXLOG

      break

    end

  }

  fh.close

end



#ログファイルの書き込み関数

def write_log_file(writedata)

  $log.unshift("#{writedata}\n")

  c = 0

  fh = open(FILENAME, "w")

  $log.each{|l|

    fh.print l

    c += 1

    if c >= MAXLOG

      break

    end

  }

  fh.close

end







#ファイルロッククラスの初期化

fillock = FileLockDir.new



#フォームからのデータ読みこみと日本語変換と改行の変換

if input["senddata"]

  senddata = Kconv.tosjis(input["senddata"])

  senddata.gsub!(/#{EOL}/, BR)

  senddata.gsub!(/#{CR}/, BR)

  senddata.gsub!(/#{LF}/, BR)

end



#ログファイルの読みこみ関数を呼び出す

read_log_file



#書き込みがあったら

if senddata

  begin



#ファイルロック設定

    lockd = fillock.lock(LOCKFILENAME)



    unless lockd

      print <<EOF;

<html><body>

他の人が書き込み中です。
しばらく待ってからもう一度書いて下さい。 </body></html> EOF end #ログファイルの書き込み関数を呼び出し write_log_file(senddata) ensure #ファイルロック解除 fillock.unlock(LOCKFILENAME) if lockd end end #HTMLの表示 print <<EOF; <html> <body> <form method="POST"> <textarea name="senddata" cols="80" rows="10"></textarea> <input type="submit" value="送信"> </form> <hr> EOF $log.each{|l| print "#{l}\n<hr>\n" } print <<EOF; </body> </html> EOF
-- プログラム 終わり --

ずいぶんと長いプログラムになっちゃいましたね。
でもほとんどが今までにやってきたことをくっつけているだけなので説明しなければならない部分はそんなにはないと思います。
でも長くても関数、クラスを使っているので比較的見やすくなっていると思います。
プログラムが見やすくなるというのは関数や、クラスを使う事の一番のいい部分かもしれませんね。

たいていの部分はすでに説明が終わっているので飛ばしながら説明します。

1行目から5行目まではモジュールの呼び出しなどのお決まりです。
7行目から15行目までは定数の設定や配列の初期化などを行っています。
17行目から38行目はファイルのロックを行うクラスの作成です。詳しくは「3.4. クラスを作ってみよう」を参照して下さい。
40行目から57行目はログの読みのみの関数を作成しています。
59行目から72行目はログの書き込みの関数を作成しています。
ログの読みこみ書き込みの関数に関して「3.1. 日本語の変換と関数の作成」を参照して下さい。

76行目はクラスの初期化を行い、filelockという名で利用できるようにしています。
79行目から85行目はフォームからのデータ読みこみと日本語変換と改行の変換を行っています。「3.1. 日本語の変換と関数の作成」を参照して下さい。
88行目でログファイルの読みこみ関数を呼び出しています。
91行目でsenddataが存在していたらifの中を実行します。
92行目はロックを行ったあとで例外(エラー)が起こるとロックが残ってしまうのでもし例外が起こってもロック解除を行う(105行目のensure以下)ようにしています。(ここがごとけんさんにいただいたアドバイスです。)
95行目でファイルのロックを行ってその結果をlockdに入れています。 97〜103行目ではlockdがfalseなら失敗したらメッセージを出して終了という意味です。このunlessはifの反対の命令です。ifは「以下の条件を満たしていたら何々をしろ。」という命令でunlessは「以下の条件を満たさない場合は何々をしろ。」という命令です。ifとunlessの使い分けは好みですね。
106行目でログファイルの書き込み関数を呼び出しを行います。

108行のensureは例外無くbeginが終わるか、例外が起こるかしたらensureからendまでの間を実行しろという意味です。
110行目でlockdがtrueなら先に作ってあったロックファイルの削除を行います。
112行目で91行のifの終わりです。ここまでの部分でロックを掛けてファイルの書きこみを行っています。

114行目以降でHTMLの表示で119から121行目でログの表示を行っています。

今回は大体の部分はすでに説明が終わっていたので、流れだけをさらっと説明しました。
どうでしょうか。
掲示板もいろいろと考えて作るとちょっと面倒になってしまいます。
HTMLをいじることでこの掲示板に色をつけたり、表示形式を変えたりすることでさまざまな形の掲示板に変えることが出来ます。
が、ここではCGIとrubyの説明に絞りたいのでその説明は行いません。プログラムの作りさえわかっていれば表示を変更するのは簡単に行えると思います。
今回の話しはいろいろと面倒なものが出てきてしまったので次回はもっと楽なものに取り組みたいと思います。
ごとけんさんにいただいたアドバイスにより書きこみの部分が多少変更になりました。
これによりログファイルの読みこみ関数などで例外が起こってもロックがあとに残らないようになりました。 こういうちょこっとした部分は忘れやすいことなので皆さんもプログラムを組む際に気をつけましょう。


今回始めて使った命令など

| | メニュー戻る | |

LinkExchange Japan
LinkExchange Japan Member