RubyCGI.org

RubyでCGIを作ろう


4章5節 カレンダー完成 更新日付: [2002-05-17]


 今回はカレンダー完成です。いろいろと機能を付けたいと考えていたらけっこう長いプログラムになってしまいました。

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

#!/usr/local/bin/ruby

class Calendar #カレンダークラス作成
WeekName = [
'Su', #Sun
'Mo', #Mon
'Tu', #Tue
'We', #Wed
'Th', #Thu
'Fr', #Fri
'Sa', #Sat
]

WeekColor = [
'#ff0000', #Sun
'#000000', #Mon
'#000000', #Tue
'#000000', #Wed
'#000000', #Thu
'#000000', #Fri
'#0000ff', #Sat
]

TodayColor = '#ffff00'

TodayShellHead = "\033[7m"
TodayShellFoot = "\033[m"

def initialize(year, month) #カレンダークラス初期化
@year = year.to_i
@month = month.to_i
@wday = []
@daydata = []

if (@month < 1) || (12 < @month)
raise "Month Error"
end

if (@year < 1) || (2037 < @year)
raise "Year Error"
end

nowday = Time.local(Time.now.year, Time.now.month, Time.now.day, 0, 0, 0)

(1..31).each do |day|
itsday = Time.local(@year, @month, day, 0, 0, 0)

@daydata[day] = 'today' if nowday == itsday

if day > 28 && itsday.month != @month
@wday[day] = nil
else
@wday[day] = itsday.wday
end
end
end

def html_print #HTML表示メソッド
print_data = "<table border=\"3\" cellspacing=\"0\" cellpadding=\"2\">\n"
print_data += "<caption>#{@year}年 #{@month}月</caption>\n"
print_data += "<tr>"
(0 .. WeekName.length - 1).each do |i|
print_data += "<th><font color=\"#{WeekColor[i]}\">#{WeekName[i]}</font></th>"
end
print_data += "</tr>\n"

(1 .. @wday.length).each do |day|
break unless @wday[day]

if day == 1
print_data += "<td></td>" * @wday[day]
elsif @wday[day] == 0
print_data += "<tr>"
end

if @daydata[day] == 'today'
print_data += "<td align=\"right\" bgcolor=\"#{TodayColor}\">"
else
print_data += "<td align=\"right\">"
end

print_data += "<font color=\"#{WeekColor[@wday[day]]}\">#{day}</font></td>"

if @wday[day] == 6
print_data += "</tr>\n"
end
end
print_data += "</table>\n"
print_data
end

def shell_print #SHELL表示メソッド
print_data = "#{@year} #{@month}".center(20) + "\n"
WeekName.each{|i| print_data += " #{i}"}
print_data += "\n"

(1 .. @wday.length).each do |day|
break unless @wday[day]

if day == 1
print_data += " " * @wday[day]
end

if @daydata[day] == 'today'
print_data += ' ' + TodayShellHead + sprintf("%2d", day) + TodayShellFoot
else
print_data += ' ' + sprintf("%2d", day)
end

if @wday[day] == 6
print_data += "\n"
end
end
print_data
end
end


year = ARGV.shift || Time.now.year
month = ARGV.shift || Time.now.month

if ENV['REQUEST_METHOD'] #HTML
print "Content-type: text/html\n\n"
print "<html><body>\n"
begin
calen = Calendar.new(year, month)
print "#{calen.html_print}\n"
rescue
print "#{$!}\n"
end
print "</body></html>\n"
else #SHELL
begin
calen = Calendar.new(year, month)
print "#{calen.shell_print}\n"
rescue
print "#{$!}\n"
end
end

-- プログラム 終わり --

cal.cgiを作成して実行してみましょう。カレンダーが表示されますね。
「4.3. とりあえずカレンダー」との違いは
・曜日が付いた
・土日の表示に色が付いた
・今日の日付にバックの色が付いた(tableのbgcolorを表現ブラウザのみ対応)

機能としては

cal.cgi?1999+1

とURLに入れてカレンダーを表示させてみて下さい。
 1999年の1月のカレンダーが表示されます。西暦1年の1月から西暦2037年の12月まで対応しています。

 なお、おまけ機能としてシェル上でも使えるようにしてあります。
シェル上で

./cal.cgi

と打つ事でカレンダーを表示します。

./cal.cgi 1999 1

とする事で1999年の1月のカレンダーが表示されます。

 機能の説明はこんなところにしてプログラムの中を説明します。

 その前にクラスのinitializeとインスタンス変数について説明します。initializeは特殊なメソッドでnewでクラスの初期化が行われたときに呼ばれるメソッドです。具体的な使われ方はこのカレンダープログラムを見てください。(このプログラムではcalendar.newで呼ばれています。)

 インスタンス変数とはインスタンス内(ここではCalendarクラス内)でのみ有効な変数です。よくinitializeの中で設定され、他のメソッド内で利用されます。このカレンダープログラムでも同様の使い方をしています。


1行目はお決まりです。
3〜116行目はcalendarクラスです。この部分は後から説明します。
119行目では1つ目の引数があればそれをyearに入れ、なかったら今日の年をyearに入れています。
120行目では2つ目の引数があればそれをmonthに入れ、なかったら今日の年をmonthに入れています。
引数とはプログラムが起動される時に渡される何らかの文字列の事を指します。
ウェブでは

cal.cgi?1999+1

の1999の部分が1つ目の引数となり1の部分が2つ目の引数となります。
シェルでは

./cal.cgi 1999 1

の1999の部分が1つ目の引数となり1の部分が2つ目の引数となります。

122行では環境変数ENVの中のREQUEST_METHODを見て、これが存在したらCGIとして130行目までを実行し、そうでなければ132行目のelseに飛びます。CGIでは必ずENV['REQUEST_METHOD']が設定されるはずなのでCGIとして実行されているかそれ以外の方法で実行されているかを判別しています。RubyではENVを使って環境変数を参照したり、変更したりすることが出来ます。
シェル上で
printenv
と打つと現在の環境変数の状態を調べる事が出来ます。

CGIだった場合は
123行目でCGIのお決まりを行います。
124行目でHTMLのヘッダを表示します。
125行目でbeginを設定し
126行目でcalenにCalendarクラスを設定します。
127行ではcalen.html_printを呼び出しその結果を表示します。
128行目では126,127行目で何らかの例外(エラー)が発生したらここに処理を移します。
おそらく年か月の数字がおかしかったために例外が発生しているものと思われます。
129行目では例外の内容を表示します。
$!は直前に起こった例外のメッセージを入れてある組みこみ変数です。
130行目は125行目のbeginの終わりです。

SHELLだった場合は132行目以降を実行しますがCGIだった場合とほとんど同じ事をやっているので説明は省きます。違うのはcalen.html_printの呼び出しがcalen.shell_printに変っている事だけです。

それでは問題のCalendarクラスを解説します。

まずCalendarクラスの大まかな流れです。

4〜27行目はクラス内で使われる定数(曜日の名前など)を設定しています。
29〜56行目のinitialize内ではしていされた年月のカレンダー用のデータ(曜日や当日かどうか)を作成しています。ここで作成されるデータは全てインスタンス変数の中に入ります。
58〜90行目のhtml_printではinitializeで作成されたインスタンス変数を利用してカレンダーのHTMLを作成して呼び出された時の帰り値にしています。
92〜93行目のshell_printではhtml_printと同様の事をシェル用にやっています。この内容はhtml_printと余り変らないので説明はしません。興味のある人は解析してみて下さい。

 では細かい内容にいきます。
3行目からクラスが始まります。
4〜12行目はWeekName定数(曜日の名前)を作成しています。
14〜22行目はWeekColor定数(HTMLで使う曜日の色)を作成しています。
24行目はTodayColor定数(HTMLの時の今日の色)を作成しています。
26行目はTodayShellHead定数(シェルの時の今日の日付につけるコードのヘッダ)を作成しています。
27行目はTodayShellFoot定数(シェルの時の今日の日付につけるコードのフッダ)を作成しています。

29行目で#カレンダークラス初期化開始です。
30行目では初期化の際に受け取ったyearをIntegerに変更してインスタンス変数@yearに入れています。
31行目では初期化の際に受け取ったmonthをIntegerに変更してインスタンス変数@monthに入れています。
32,32行目ではインスタンス変数@wday,@daydataを初期化しています。
35〜37行目では@monthが1より少ないか12より大きかったら例外を発生(エラーを発生)させています。
39〜41行目では@yearが1より少ないか2037より大きかったら例外を発生(エラーを発生)させています。
このraiseという命令は例外を発生させる命令です。クラスを呼び出した方でエラーが発生します。
43行目では今の日のTimeオブジェクトをnowdayに入れています。
45行目では1〜31までをdayに入れてeachでループさせています。このループの終わりは55行目です。
46行目では@year,@month,dayで示される日のTimeオブジェクトをitsdayに入れています。
48行目ではnowdayとitsdayが一致したら@daydata[day]に"today"を入れています。
50〜54行目ではdayが28を越していてitsdayの月が@monthと一致しなければ 51行目を実行し、@wday[day]にnil(何もないという意味)を入れ一致しなければ 53行目を実行し、@wday[day]にitsdayの曜日を入れています。
56行目でinitializeの終わりです。

58行目でからhtml_printが始まります。
59〜60行目でprint_dataの初期化と出力のHTMLのヘッダを作成しています。
ここでテーブルの始まり、年月の表示などを行っています。
61〜65行目でカレンダーの上につく曜日の表示をprint_dataに追加しています。
67行目では1〜31までをdayに入れてeachでループさせています。このループの終わりは87行目です。
68行目では@wday[day]の中身が無かったらbreakでループを脱出します。
70〜74行目では dayが1だったらprint_dataに""を@wday[day]個だけ追加しています。
それ以外で@wday[day]が0だったらprint_dataに""を追加しています。
76〜80行目では @daydata[day]が'today'だったらprint_dataに""を追加し(バックに色をつけている)そうでなかったらprint_dataに""を追加しています。
82行目ではprint_dataに"#{day}"を追加し、日付を表示しています。
84〜87行目では@wday[day]が6なら(土曜日なら)print_dataに"\n"を追加してテーブルの1行の終わりをしていしています。
87行目のendで67行のループの終わりです。
88行目ではprint_dataに"\n"を追加し、テーブルを閉じています。
89行目では帰り値をprint_dataにしています。
90行目でhtml_printを終わっています。

この後はshell_printなのですがここでは説明は省きます。

こんな感じでカレンダーの説明は終わりです。前に作ってあったカレンダーをクラスを使って、汎用性の高いものに直して、ちょっと高機能にした感じです。クラスを使う事で119行目以降だけでプログラムの流れがわかるようになります。

WindowsやMSDOSなどのUNIX以外のシェルでも26,27行の中身を空にすれば問題無く動くんじゃないかと思います。(あくまで予想ですが)


今回始めて使った命令など
インスタンス変数
initialize(Objectの説明の中に説明がある)
initialize(メソッド定義の中にも説明がある)
ENV
$!
raise(シンタックスの中の説明)
raise(ファンクションの中の説明)

RubyによるCGIプログラミング
RubyによるCGIプログラミング

<− 前へ : 4.4. コマンド出力を使ってカレンダー | 次へ : 5. 自分のパソコンをチャットサーバーにしよう −>

RubyでCGIを作ろうトップ

| トップページ | 書籍「Ruby/GTKプログラミング入門」 | 書籍「RubyによるCGIプログラミング」 | RubyでCGIを作ろう | リンク集 | ツールなど | 自己紹介 |