roy > naoya > 基礎プログラミングII > (8)CGI[1]

(8) CGI[1]

[1]CGIスクリプトでa要素やimg要素を使用する際の""の取り扱い

タグを記載する場合<a href="hoge.html">トップページ</a>や<img src="hoge.jpg">のように""を使用する場合がある。これらをCGIスクリプト(Rubyプログラム)の中で記載する場合

print"<a href="hoge.html">トップページ</a>\n"

というようにprint ""で周囲をくくる必要があるため、""の中に""が入り込むという状況になる。この場合どれがprintの"でどれがa要素の"なのかが不明確になり、うまく取り扱うことができない。そのためprint""の""以外で""を使用する場合は"の手前に\(バックスラッシュ)をつけて区別する。

""の取り扱い

<a>や<img>を使用する場合、内部で使用する"の手前には\をつける。

print"<a href=\"hoge.html\">トップページ</a>\n"

[2]練習

前回の授業で作成した、shinri.rbに、shinri.htmlへのリンクを貼り、""の手前にバックスラッシュが必要であることを確認しよう。

print"<p><a href="shinri.html">元のページに戻る</a></p>\n"

[3]ファイルのアクセス権について

CGIスクリプトとして働くRubyプログラムは、cgi-binディレクトリに保存した上で、ktermより次のコマンドを入力しなければ正しく動かすことが出来なかった。

sime{c1xxxxx}% chmod 755 ◯◯.rb[Return]
または
sime{c1xxxxx}% chmod +x ◯◯.rb[Return]

chmodコマンドはファイルへのアクセス権を設定するコマンドである。では、その後の755は何を意味するのだろうか。これを理解するためには、まずアクセス権について理解する必要がある。

ファイルのアクセス権

ktermを使い、lsコマンドに引数-laをつけ、ディレクトリ内のファイル一覧を表示してみよう。どのディレクトリで行っても構わない。コマンドを入力すると、ファイル名以外に-rw-r--r--というような不思議な記号が表示されることが確認できる。

sime{c1xxxxx}% ls -la[Return]
drwxr-xr-x   2 naoya    st2005       512  9月 29日  19:10 ./
drwxr-xr-x  13 naoya    st2005      1024  9月 29日  16:03 ../
-rw-r--r--   1 naoya    st2005        46  9月 24日  16:09 .htaccess
-rw-r--r--   1 naoya    st2005      3743  9月 29日  19:10 10-access.htm
-rw-r--r--   1 naoya    st2005      5934  2月 15日 2008年 10-css.htm
-rw-r--r--   1 naoya    st2005      2319  2月 15日 2008年 10-image.htm
-rw-r--r--   1 naoya    st2005      4828  9月 29日  19:06 10-open.htm
-rw-r--r--   1 naoya    st2005     22966  9月 29日  18:42 10.htm
-rw-r--r--   1 naoya    st2005     22501  9月 24日  16:19 9.htm
-rwxr-xr-x   1 naoya    st2005       807  9月 24日  16:45 kakaku.rb*
-rw-rw-rw-   1 naoya    st2005       144  9月 29日  19:06 open-cgi.dat
-rwxr-xr-x   1 naoya    st2005       887  9月 29日  19:01 open-cgi.rb*

これが各ファイルのアクセス権を示したものである。先頭は、「-」または「d」となっているが、これはファイルとディレクトリを区別するもので、「-」はファイル、「d」はディレクトリとなる。

先頭の1桁を除外すると、残りは9桁である。これは3桁×3つに分割することができる。最初の3桁は自分自身(u)のアクセス権、次の3桁は自分と同一グループのユーザー(g)のアクセス権、最後の3桁は他のユーザー(o)のアクセス権である。

u (User)

g (Group)

o (Other)

r

w

x

r

w

x

r

w

x

ここで、rはReadの略であり読み込む権利を、wはWriteの略であり編修する権利を、xはeXecuteの略で実行する権利を表している。r、w、xと表示されている場合はその権利があり、-の場合はその権利がないという意味になる。

ファイルを作成した直後のアクセス権の設定は、rw-r--r--となっているが、これは次のような意味となる。

  • 自分(u):r(読み込み)とw(編集)のみ可
  • グループ(g):r(読み込み)のみ可
  • 他人(o):r(読み込み)のみ可

CGIスクリプトとして使用するRubyプログラムは、自分だけでなく他人が使用する可能性があるため、gやoに実行する権利を与える必要があるし、CGIの中でopenメソッドを使ってファイルの読み書きをする場合は、gやoにファイルを読み込む権利や編修する権利を与える必要がある。

アクセス権の変更方法[1] ビットパターンによる指定

u, g, oそれぞれについて3種類の権利を設定することができる。許可する場合を1、許可しない場合を0とした場合、u, g, oそれぞれについて000, 001, 010, 011, 100, 101, 110, 111という8種類の組み合わせが想定できる。これを3桁の2進数と見なし、10進数に直した値を使って指定しようとする方法である。

組み合わせ

r(Read)

w(Write)

x(eXecute)

10進数

000

-

-

-

0

001

-

-

OK

1

010

-

OK

-

2

011

-

OK

OK

3

100

OK

-

-

4

101

OK

-

OK

5

110

OK

OK

-

6

111

OK

OK

OK

7

sime{c1xxxxx}% chmod 755 hoge.rb[Return]

この場合、hoge.rbに対して、Userはr,w,xいずれも許可、GroupとOtherはr,xのみ許可という設定をしていることになる。

アクセス権の変更方法[2] シンボルを用いて指定

ユーザーを表すu, g, oと、アクセス権を表すr, w, xを組み合わせて指定する方法である。例を示しながら説明する。

sime{c1xxxxx}% chmod u+x hoge.rb[Return]
#=>Userにx(eXecute)を追加
sime{c1xxxxx}% chmod o-r hoge.rb[Return]
#=>Otherからr(Read)を除去
sime{c1xxxxx}% chmod +x hoge.rb[Return]
#=>User、Group、Otherにx(eXecute)を追加
sime{c1xxxxx}% chmod -w hoge.rb[Return]
#=>User、Group、Otherからw(Write)を除去

[4]CSSを切り替える

以下は、色占いのCGIである。いずれかの色を選んだ上で「占う」ボタンを押すと、占い結果が表示されるが、その際に背景色を選択色にするということを考えてみる。

占いです。今の気分を表す色を選んでね。

背景色の変更はbody要素に対してCSSのbackground-colorプロパティを指定すれば良い。CSSによるスタイルの指定にはいくつかの方法があるが、ここでは<head></head>内に記載する方法を示す。

入力フォーム

<form method="POST" action="./color.rb">
<p>占いです。今の気分を表す色を選んでね。</p>
<p>
<input type="radio" name="color" value="red">赤
<input type="radio" name="color" value="blue">青
<input type="radio" name="color" value="green">緑
</p>
<p>
<input type="submit" name="ok" value="占う">
<input type="reset" name="ng" value="やめておく">
</p>
</form>

使用しているのはラジオボタンである。ラジオボタンはいずれか1つしか選択することができないようにするため、name属性を3つともcolorに指定している。選択した色をvalue、colorをkeyとしてcolor.rbに引き渡される。

CGIスクリプト(color.rb)

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

select = c["color"]

print "<html>\n"
print "<head>\n"
print "<title>診断結果</title>\n"
print "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n"
print "<style type=\"text/css\">\n"
print "<!--\n"

if select == "red"
  print "body {background-color: #ff0000}\n"
  uranai = "元気いっぱいですね"
elsif select == "blue"
  print "body {background-color: #0000ff}\n"
  uranai = "落ち込んでいますね"
elsif select == "green"
  print "body {background-color: #00ff00}\n"
  uranai = "目が疲れていますね"
end

print "-->\n"
print "</style>\n"
print "</head>\n"
print "<body>\n"

print "<h1>診断結果</h1>\n"

print "<p>あなたが選んだのは#{select}です。#{uranai}。</p>\n"

print "</body>\n"
print "</html>\n"

CSSを使用する場合、<head></head>内に
<meta http-equiv="Content-Style-Type" content="text/css">
と書く必要がある。

Rubyプログラムの中に書くため、print""をつけ、""内で使用されている"はバックスラッシュをつけて
print "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n"
となる。

CSSのスタイル自体は

<style type="text/css">
<!--
  body {background-color: #ff0000}
-->
</style>

で指定されている。color.rbではbody {background-color: #ff0000}の部分をif文でくくり、条件に応じて色を変更している。

外部CSSファイルを呼び出す

CSSによるデザインの指定を行う場合、上述の通り<head></head>内に具体的に書いてもよいが、別にファイルを作成しておいても良い。この場合拡張子がcssのテキストファイルを作成しておき、<head></head>内には<style></style>で具体的に指定するかわりに

<link rel="stylesheet" href="hoge.css" type="text/css">

として呼び出すこともできる。これにより、hoge.cssに記載したスタイルが呼び出したhtmlファイルに適用される。

CSSファイルのサンプル(2010.css; このページで使用しているCSS)

CSSのプロパティの詳細についてはGoogle等で「CSS プロパティ」をキーワードにして検索してみること。

[5]openメソッドと組み合わせる

通常のプログラムと同じように、openメソッドを使って外部のファイルからの読み込みや、書き出しをすることができる。

猫と犬のどちらが好きですか?

入力フォーム(choice.html)

<form method="POST" action="./open-cgi.rb">
<p>
猫と犬のどちらが好きですか?<br>
<input type="radio" name="choice" value="cat">猫
<input type="radio" name="choice" value="dog">犬<br>

<input type="submit" name="ok" value="投票">
<input type="reset" name="ng" value="リセット">
</p>
</form>

質問にはラジオボタンを用いている。2つのラジオボタンを同時に選択することが出来ないよう、name属性をchoiceに統一している。選択したボタンに対応するvalue属性の値がkeyであるchoiceに対応するvalueとして、CGIスクリプト(open-cgi.rb)に送られる。

CGIスクリプト(open-cgi.rb)

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

data = c["choice"]

cat = 0
dog = 0

open("open-cgi.dat","r+:utf-8") do |hoge|
  while line = hoge.gets
    if /cat/ =~ line
      cat += 1
    elsif /dog/ =~ line
      dog += 1
    end
  end

  if data == "cat"
    cat += 1
    hoge.printf("%s\n",data)
  elsif data == "dog"
    dog += 1
    hoge.printf("%s\n",data)
  end
end

print"<html>
<head><title>結果発表</title></head>
<body>\n"

print"<h1>投票数:#{cat+dog}</h1>\n"

print"<table border=1>\n"

print"<tr><td>猫</td><td>"
1.upto(cat) do |i|
  if i%50 == 0
    print"■<br>"
  else
    print"■"  
  end
end
printf(" %d</td></tr>\n",cat)

print"<tr><td>犬</td><td>"
1.upto(dog) do |i|
  if i%50 == 0
    print"■<br>"
  else
    print"■"  
  end
end
printf(" %d</td></tr></table>\n",dog)	

print"</body>\n"
print"</html>\n"

入力フォームで選択されたデータは、ハッシュ変数cのkeyとvalueという形で取り出されている。その後、openメソッドを用いて、open-cgi.datを開いている。モードはr+とし、読み込みも書込みも出来るようにしている。open-endの中で、まず、while-endを使ってファイル内の値を順番にとりだし、これまでの得票数をcatとdogにそれぞれ代入しておき、その下のif-endで今回の得票の加算と、ファイルへの書込みを行っている。

HTMLへの結果出力時には、1票を■1つで表現し、それぞれ得票数を示している。演算子%は割り算の余りという意味であり、50で割ったときの余りが0の場合のみ<br>をつけることで、50票ずつ折り返して表示できるようにしている。

このCGIをうまく動かすためには、事前にopen-cgi.datを作成しておく必要がある。その上で、自分以外の第三者がこのファイルへの読み書きができるようファイルのアクセス権限を変更する必要がある。具体的にはktermでchomodコマンドを使用して次のように入力する。

sime{c1xxxxx}% chmod 666 open-cgi.dat[Return]

[6]自分自身にデータを引き渡す

複数ページを1つのプログラムで作る

次のCGIを試してみよう。

迷路から脱出せよ

これは迷路の中を進んでゴールにたどり着くゲームの簡略版である。何度も進路を選択して進んでいき、最終的にゴールにたどり着くが、このようなページは単一のプログラムで書くことができる。プログラムを見てみよう。

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

number = c["rd"]

if number == ""  #初回起動時のみ
 number = "1"    #numberに"1"を代入
end

if number != "7" #numberが"7"以外の場合は(7はゴール)

 print"<html><head><title>クイズ</title></head><body>\n"
 #formタグやOKボタンは1つで共用
 print"<form method=\"POST\" action=\"./sample.rb\">\n"
 if number == "1"
   print"<p>どっちへ進む?</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"2\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"3\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"5\">上\n"
 elsif number == "2"
   print"<p>さてどうしよう</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"3\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"5\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"6\">上\n"
 elsif number == "3"
   print"<p>だんだんわからなくなってきたぞ。次はどうする?</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"1\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"2\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"5\">上\n"
 elsif number == "4"
   print"<p>ゴールが近づいてきたような気がする</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"2\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"1\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"7\">上\n"
 elsif number == "5"
   print"<p>だめだどこにいるかわからない</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"2\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"6\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"1\">上\n"
 elsif number == "6"
   print"<p>ここはどこだろう</p>\n"
   print"<input type=\"radio\" name=\"rd\" value=\"3\">右\n"
   print"<input type=\"radio\" name=\"rd\" value=\"4\">左\n"
   print"<input type=\"radio\" name=\"rd\" value=\"5\">前\n"
   print"<input type=\"radio\" name=\"rd\" value=\"2\">上\n"
 end

 print"<input name=\"ok\" type=\"submit\" value=\"送信\">\n"
 print"<input name=\"ng\" type=\"reset\" value=\"リセット\">\n"
 print"</form>\n"
else
 print"<p>おめでとう! ゴールです</p>\n"
end
print "</body>\n"
print "</html>\n"

選択したラジオボタンのvalueの値に応じて、if文で異なるメッセージが表示されるようになっている。

値を次の次に引き渡す

5問のクイズを1ページに1つずつ出題する場合、最初の問題や2つ目の問題に対する回答を最終ページに持ち越すことは通常はできない。しかし、<input type="hidden">を使うと持ち越すことができる。

次の次のページにデータを引き渡す

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"


data = c["rd"]
hoge = c["dat"]

hoge = hoge + data  #直前に選んだデータをhogeに追加

hogehoge = hoge.split(",")  #hoge内のデータを,ごとに配列に分割


print"<html><head><title>好きな果物</title></head><body>\n"


print"<form method=\"POST\" action=\"./sample2.rb\">\n"
print"<p>ひとつ選択</p>\n"
print"<input type=\"radio\" name=\"rd\" value=\"りんご,\">りんご\n"
print"<input type=\"radio\" name=\"rd\" value=\"バナナ,\">バナナ\n"
print"<input type=\"radio\" name=\"rd\" value=\"みかん,\">みかん\n"
print"<input type=\"hidden\" name=\"dat\" value=\"#{hoge}\"><br>\n"
    #type="hidden"で入力パーツを表示せず、hogeをプログラムに引き渡す
print"<input name=\"ok\" type=\"submit\" value=\"送信\">\n"
print"<input name=\"ng\" type=\"reset\" value=\"リセット\">\n"
print"</form>\n"


print"<p>選んだもの</p>"
for i in hogehoge
  printf("<p>%s</p>",i)
end



print "</body>\n"
print "</html>\n"

print"<input type=\"hidden\" name=\"dat\" value=\"#{hoge}\"><br>\n"の部分で、typeをhiddenとしているため入力用のパーツは何も表示させずに、データをプログラムに引き渡すことができる(ここではkeyがdat、valueを変数hoge)。

splitメソッドは変数.split(区切り)という形式で用いることで区切りの位置で変数内の値を区切って配列に変換することができる。今回のケースでは、hogeは通常変数であり、この変数内には"りんご,バナナ,バナナ,みかん"のように文字列が,を挟んで合体したような形で代入されている。これをsplitメソッドを使ってカンマの位置で切ることで、hogehogeは["りんご","バナナ","バナナ","みかん"]のようになる。なお変数hogeで各果物の間にカンマがついているのはラジオボタンのvalueの部分でvalue=\"バナナ,\"のようにカンマをつけているためである。

[7]データを永続させる(PStoreクラス)

>>More

[8]CGIでのPStoreの利用

>>More

[9]自由課題グループ分け

次回から自由課題実施期間となる。これまで学んできたことを駆使してグループごとにプログラムを作成する。5~6人のグループを作る。これまでに獲得してきた点数に基づき、各グループの戦力が均衡するようにグループ分けを行う。どのようなプログラムを作成するか考えておくと良い。

[10]レポート課題

問題(10点満点):CGIを使って自由に何かを作成する。10点満点だが、単に授業用のページ(他の先生も含む)に掲載されているものを複写し、多少言葉を変えただけの場合はプログラム点の配点を最低点とする。


  • 提出先:課題提出用メールアドレス
  • 提出期限:第1提出期限、第2提出期限を設定
  • メールのSubject:課題7
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
  1. データを入力するWebページのURL
  2. 作成したプログラム(CGIスクリプト)
  3. プログラムの説明
  4. 感想

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(1~5点)、説明(2点)
  • プログラムの説明:何をするCGIなのか、Webページの入力フォームから受け取った値をどのように処理しているのかについて
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点することがある。一度読み直してから提出すること。
  • 驚異的に良くできているレポートについては満点を超える得点をつけることがある。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。

Tips:emacsでの日本語入力のオンオフはCtrl-oです

Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする

Tips:Mewによるメールの送り方はMewコマンドを参照