解説 10月(5)

今回の学習項目です。


課題1

※ CGIのプログラムを作成する課題では、必ず作成したプログラムのソースコードとWebページのURLを提出してください。

課題1-1

課題1-1では、次のようなアクセスカウンターのCGIプログラムを作成します。

アクセスカウンターのCGIプログラム

課題1-1-1

まず、カウンタープログラムの仕組みを理解しておきましょう。
次のような、実行回数をカウントするRubyのプログラムを作成しなさい。(CGIのプログラム ではなく、Rubyのプログラムを作成すればよい。)

[h209xxx@ls01 h209xxx]% ruby counter.rb
現在の実行回数は1回です。

[h209xxx@ls01 h209xxx]% ruby counter.rb
現在の実行回数は2回です。

[h209xxx@ls01 h209xxx]% ruby counter.rb
現在の実行回数は3回です。

[カウンタープログラムのつくりかた]

カウンターのプログラムは次のようにして作ります。

1. 実行回数の記録されたファイルから数字を読み込む。
2. 読み込んだ数字を数に変換し、1増やす。
3. 実行回数の記録されたファイルに現在の実行回数を書き込む。
4. 現在の実行回数を表示する。

[ファイル操作のおさらい]

ファイルを開くときは次のように書きます。

fo = open("ファイル名", "r")    #読み込みモードでオープン。
fo = open("ファイル名", "w")    #書き込みモードでオープン。
fo = open("ファイル名", "a")    #追加書き込みモードでオープン。

読み込みと書き込みは次のようにします。

str = fo.gets         #ファイルから一行読み込む。
ary = fo.readlines    #ファイルからすべての行を読み込む。
fo.print str          #ファイルに書き込む。

開いたファイルは閉じましょう。

fo.close                        #ファイルをクローズ。

課題1-1-2

今度はCGIのプログラムを作成し、オフラインで(LinuxOS上で)実行して、プログラム が正しく動作することを確認しなさい。

[h209xxx@ls01 h209xxx]% ./counter.cgi
Content-type: text/html; charset=euc-jp

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-JP">
<title>アクセスカウンターのプログラム</title>
</head>
<body>
現在のアクセス数は 4 です。<br>
<br>
<a href="counter.cgi">更新</a>
</body>
</html>

[オフラインで実行する理由は?]

作成したCGIのページがブラウザに表示されない場合、様々な要因が考えられます。 ファイル名が間違っている、ファイルが正しい場所に置かれていない、実行権限が 与えられていない、文字コードが間違っている、プログラムの実行過程でエラーが 発生した、などがよくある原因です。一つ一つ確認して、どこに問題があるのか 見極める必要があります。

プログラムにバグがある場合、ブラウザからアクセスするだけでは、プログラムの どの部分に問題があるのか特定するのは難しいでしょう。そのようなときには、CGIプロ グラムをオフラインで(コマンドを入力してLinuxOS上で)実行してみましょう。 エラーメッセージが表示されますので、プログラム自体に問題があるのか、それとも 他に原因があるのかなど、ブラウザからの実行では得られない多くの情報を得ることが できます。

CGIプログラムをオフラインで実行するときには、次のようにファイル名を指定して 実行するようにしましょう。

./ファイル名

上のように実行することで、ブラウザからCGIプログラムを実行するときと同じ状況で プログラムを実行することができます。ファイルのパーミッションに問題がある場合や 、CGIプログラムの1行目(#!/usr/bin/ruby)に間違いがある場合には実行することが できませんので、該当箇所を修正してください。

[HTMLファイルとCGIファイルの違いは?]

サーバーに置かれたHTMLファイルやCGIファイルにブラウザでアクセスすると、 どちらのファイルもHTML文書(Webページのソース)をブラウザに返してきます。 ブラウザは受け取ったHTML文書を元にWebページを形成して表示するわけですが、 HTMLファイルとCGIファイルでは、サーバー内での処理の過程に次のような違いが あります。

HTMLファイルの場合
1. ブラウザがサーバーに対してページを要求する。
2. サーバーはファイルに書かれているHTML文書をブラウザに返す。
3. ブラウザはサーバーから受け取ったHTML文書をWebページとして表示する。
CGIファイルの場合
1. ブラウザがサーバーに対してページを要求する。
2. サーバーはファイルに書かれているプログラムを実行する。
3. サーバーは実行結果(print文による出力のHTML文書)をブラウザに返す。
4. ブラウザはサーバーから受け取ったHTML文書をWebページとして表示する。

どちらの場合も、サーバーがブラウザに対してHTML文書を返すことに違いはありま せん。異なる点は、HTMLファイルの場合ファイルに書かれている内容がそのまま 返されるのに対し、CGIファイルの場合はプログラムが実行され、出力された文字列が ブラウザに返されるところです。そのため、CGIのプログラムでは、 ブラウザに返すHTML文書(HTMLのタグ)を文字列としてprintする必要があるの です。(タグをそのままプログラムの中に書いてしまうと、実行してもエラーになって しまいます。HTMLのタグはプログラムの中では文字列として 扱わなければいけません。

[CGIファイルのパーミッションは?]

STドメインのCGIサーバーにCGIのプログラムを置く場合、ファイルのパーミッション はuser(所有者)に対してx(実行)を許可する必要があります。 許可しない状態では、ブラウザからアクセスしたときにプログラムが実行されませんので、 実行結果をブラウザに表示することもできません。

この場合のパーミッションの変更には、次のようなコマンドを使用します。

chmod 700 ファイル名

#もしくは

chmod u+x ファイル名

CGIプログラムを設置するサーバーによっては、このようなCGIファイルやHTMLファイル のパーミッションの設定が、STドメインのCGIサーバーの設定とは異なる場合があります。 別のサーバーにCGIプログラムを設置する際には、必ず設置先のサーバーの設定を確認する ようにしましょう。

[CGIのページで文章を改行するには?]

Webページに表示する文章を改行するためには、改行タグ(<br>タグ)を使用 する必要があります。

一行目。<br>二行目。  #ブラウザで表示すると<br>の部分で改行されます。

CGIのプログラムはHTML文書(Webページのソース)を出力します。そのため、 改行コードを出力するだけでは、Webページのソースが改行されるだけで、Webページ の文章自体を改行することはできません。

Webページの文章を改行するときは、改行の働きを持つ改行タグをHTML文書に挿入 する必要があるので、次のように改行タグをprintする必要があります。

print "一行目。<br>二行目。"

課題1-1-3

サーバー上に作成したCGIファイルにブラウザでアクセスして、Webページが正しく表示されることと、プログラムが正しく動作していることを確認しなさい。 作成したCGIプログラムのURLを提出すること。

課題1-2

次のページを見てください。

アクセスカウンターのCGIプログラム その2

上のページからたどれる3つのページはそれぞれ別のCGIファイルで作成されていて、各ページで個別にアクセス数を記録しています。 (ページごとにアクセス数が違っているはずです。ページごとに別のテキストファイルにアクセス数を記録しています。)

CGIのプログラムでは、複数のページで同じような機能を必要とすることが少なくありません。 (例えば、ユーザー認証や、データの読み込みなど。) そのような場合、同じプログラムをたくさんのCGIファイルに書き込んでいくのは手間がかかりますし、バグを修正するときや機能に変更を加えるときに、いくつものファイルを書き換えるのはあまり効率の良いやり方とは言えません。

このような、複数のCGIプログラムで共通して使用する機能は、部品化(コンポーネント化)して一つのファイルに記述しておいて、その機能を必要とするCGIプログラムが読み込んで利用できるようにしておくとよいでしょう。 こうしておけば、プログラムに修正や変更を加えるときに書き換える箇所が少なくなります。

ここからは、コンポーネント化した部品をCGIプログラムで利用するための練習を行います。 具体的には、アクセスカウンターの機能をクラスとして定義し、CGIプログラムに読み込んで(requireして)部品として利用します。

課題1-2-1

まず、アクセスカウンターのクラスを定義しておきましょう。下のCounterクラスの定義を完成させて、counter.rbという名前のファイルに保存しなさい。

#counter.rb

class Counter
  def initialize(引数)
    #引数として受け取ったテキストファイル名をインスタンス変数に代入する。
  end

  def count_up
    #テキストファイルからアクセス数を取得する。
    #アクセス数をカウントアップする。
    #テキストファイルにアクセス数を保存する。
    #アクセス数を返す。
  end
end

クラスの定義が完成したら、requireを使用して別のファイルからCounterクラスを読み込み、カウンターオブジェクトを生成してテキストファイルのアクセス数が増加することを確認しなさい。

#テスト用の適当なファイル

require "counter.rb"    #Counterクラスを読み込む。

counter = Counter.new("counter2a.txt")    #カウンターオブジェクトの生成。
p counter.count_up                        #カウントアップ。

[newメソッドの引数にテキストファイル名を指定する理由は?]

この次の課題では、3つのCGIファイルを作成し、ページごとにアクセス数を記録できるようにしてもらいます。 それぞれのCGIプログラムでrequireを使用して、Counterクラスを読み込んで利用します。

#counter2a.cgi(最初のページのCGIプログラム)
require "counter.rb"    #Counterクラスを読み込む。

counter = Counter.new("counter2a.txt")    #テキストファイル名を指定して
                                            #オブジェクトを生成する。
print "このページのアクセス数は", counter.count_up, "です。"
#counter2b.cgi(2番目のページのCGIプログラム)
require "counter.rb"    #Counterクラスを読み込む。

counter = Counter.new("counter2b.txt")    #テキストファイル名を指定して
                                            #オブジェクトを生成する。
print "このページのアクセス数は", counter.count_up, "です。"

こうしておけば、3つのCGIファイルにいちいちアクセスカウンターのプログラムを記述しなくて済みます。

カウンターオブジェクトを生成するときにテキストファイル名を指定するのは、どのCGIファイルがどのテキストファイルにアクセス数を記録しているか、Counterクラスに知らせる必要があるからです。

[テキストファイル名をインスタンス変数に代入する理由は?]

initializeメソッドでは、受け取ったテキストファイル名をインスタンス変数に代入しておきましょう。

def initialize(filename)
  @filename = filename
end

変数には有効範囲(スコープ)がありますので、普通の変数を別のメソッドの内部で使用することはできません。 インスタンス変数に代入しておけば、テキストファイル名を表す文字列を、Counterクラスの別のメソッド内でも自由に扱うことができるようになります。

def count_up
  fo = open(@filename, "r")    #テキストファイルを開く。

  ...
end

今回の課題1では、Counterクラスにcount_upメソッドを用意するだけですので、テキストファイル名をインスタンス変数に代入しておくメリットはあまり感じられないかもしれません。 しかし、他にもメソッドを定義する必要が出てきたときのことを考えてみてください。 Counterクラスはアクセスカウンターのクラスですから、おそらくほとんどのメソッド内で現在のアクセス数の値を扱う必要があるでしょう。 そのとき、どのメソッドの内部からでもテキストファイルを開けるようにしておくと、何かと都合が良さそうです。

#現在のアクセス数を表す画像ファイルのパスを返すメソッド(ボーナス課題で使用)
def image_files(image_dir, extension)
  fo = open(@filename, "r")    #テキストファイルを開いて、
  ...                           #現在のアクセス数を取得する。
  ...
end

[アクセス数を返す理由は?]

アクセスカウンターのCGIプログラムでは、現在のアクセス数をページに表示する必要があります。 count_upメソッドがアクセス数を返さなければ、Counterクラスを読み込んで利用するCGIプログラムは、Webページにアクセス数を表示することができません。 Counterクラスの内部から外部のCGIプログラムへ、returnを使ってアクセス数を返すようにしておきましょう。

def count_up
  ...

  count = count + 1

  ...

  return(count)
end

returnの返す値は、アクセス数を表す数であることに注意してください。 "このページのアクセス数は○○です" というような文字列を返してしまうと、Counterクラスを利用するすべてのCGIプログラムで同じ言い回しを使わなければいけなくなってしまいます。 何度も書かなくてよいのでその方が楽に思えるかもしれませんが、自由に変更することができないため、使い方が限定されてしまいます。 クラスには必要最低限の機能(アクセス数をカウントアップして返す機能)だけを用意して、どのように使用するかはクラスを利用するCGIプログラム側で決められるようにしておきましょう。

課題1-2-2

Counterクラスを使用したアクセスカウンターのCGIプログラムを作成しなさい。 要するに、 アクセスカウンターのCGIプログラム その2のようなものができればよい。 ただし、先程作成したCounterクラスの定義をcounter.rbという名前のファイルに保存し、3つのCGIプログラムから読み込んで使用すること。また三つのアクセスカウンターがそれぞれ別々にアクセスをカウントすることを確かめなさい。
提出するのは最初のページのプログラムとURLだけでよい。

[定義したクラスをCGIプログラムに読み込むときの注意点]

別のファイルに書かれたRubyのプログラムを読み込んで使用するときは、requireを使います。

require "counter.rb"

ファイルに書かれているプログラムは、読み込まれるときに一度実行されますので、余計な処理を実行してしまわないように、読み込まれるファイル(counter.rb)には次のように、クラスの定義だけを書いておきましょう。

#余計な処理を書かない。

class Counter
  ...
  ...
  ...
end

#余計な処理を書かない。

また、このファイルはCGIプログラムから読み込まれて使用されますので、実行属性を付与する必要はありません。

ls -l counter.rb

-rw-------    #Ownerに読み書きが許可されていればよい。

ファイルの冒頭に #!/usr/bin/ruby や $KCODE = "e" などを書く必要もありません。 それらはCGIファイルに書かかれていますので、こちらのファイルに書く必要はありません。

プログラミングIIIのホームに戻る