Ruby で掲示板の CGI を作る

サーバサイドのプログラミング能力が貧弱なので、勉強の第一歩として掲示板を作ってみた。ついでに Ruby も覚えてしまおうということで、Ruby で。

要件

  • 名前と本文を投稿できる。
  • Ruby で動く。
  • データベースに MySQL を使う。
  • ビューに ERB を使う。

mod_ruby を入れる

まずは RubyCGI で動かすために mod_ruby を入れる (参考)。

MySQL のドライバを入れる

先日入れた

データベースを作る

次にデータベースを作る。フィールドは id、名前、本文、日付の 4 つ。

CREATE DATABASE rubybbs;
GRANT ALL ON rubybbs.* to hogehoge IDENTIFIED BY 'hogefoobar';
USE rubybbs;
CREATE TABLE posts (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
date DATETIME NOT NULL,
body TEXT NOT NULL
);

プログラム本体を書く

次にプログラム本体を書く。

prepare statement と html_escape で、ある程度のインジェクションを回避できるようだ。
また、ERB に外部ファイルを使うときは untaint しないと、eval のセキュリティエラーになる。

#!/usr/bin/ruby
require 'rubygems'
require 'cgi'
require 'mysql'
require 'erb'
#config
DB_HOST = "localhost"
DB_USER = "hogehoge"
DB_PASSWORD = "hogefoobar"
TEMPLATE_FILE = "template.rhtml"
NAME_MAX_CHARS = 20
BODY_MAX_CHARS = 100
cgi = CGI.new
puts cgi.header({"content-type" => "text/html", "charset" => "utf-8"})
db = Mysql::new(DB_HOST, DB_USER, DB_PASSWORD, "rubybbs")
db.query("set character set utf8")
#add
begin
if (!cgi["name"].strip.empty? && !cgi["body"].strip.empty? && cgi["name"].length < NAME_MAX_CHARS && cgi["body"].length < BODY_MAX_CHARS)
statement = db.prepare("INSERT INTO posts (name, body, date) VALUES (?, ?, ?)")
statement.execute(cgi["name"], cgi["body"], Time.now)
end
rescue
statement.close
db.close
puts "error"
exit
end
#fetch
begin
statement = db.prepare("select name, body, date from posts")
statement.execute
posts = []
statement.each do |col|
posts << {"name" => ERB::Util.html_escape(col[0]),
"body" => ERB::Util.html_escape(col[1]).gsub(/¥n/, "<br>"),
"date" => ERB::Util.html_escape(col[2])}
end
posts.reverse!
rescue
statement.close
db.close
puts "error"
exit
end
#render
begin
puts ERB.new(File.read(TEMPLATE_FILE).untaint, 1).result(binding)
rescue
puts "error"
exit
end
#end
statement.close
db.close

ビューのテンプレートを書く

最後にビュー の rhtml を書く。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="content-language" content="ja">
<meta http-equiv="content-style-type" content="text/css">
<title>RubyBBS</title>
<style type="text/css">
div.item {
margin-bottom: 1em;
background-color: #eee;
}
div.item p {
margin: 0;
}
div.item p.name {
font-size: 1.5em;
}
div.item p.body {
font-size: 2em;
}
</style>
</head>
<body>
<h1>RubyBBS</h1>
<form method="post" action="rubybbs.rb">
<p>name:<br><input type="text" name="name"></p>
<p>body:<br><textarea name="body" rows="2" cols="40"></textarea></p>
<p><input type="submit" value="submit"></p>
</form>
<% posts.each do |item| %>
<div class="item">
<p class="name"><%= item["name"] %></p>
<p class="date"><%= item["date"] %></p>
<p class="body"><%= item["body"] %> </p>
</div>
<% end %>
</body>
</html>

課題

とりあえず動くものはできたが、公開サーバで動かせるレベルではない。
気がついた課題は、

  • 全体的にコードが汚い。無駄が多い。
  • バリデートが甘い。get で投稿できてしまう。
  • 設定ファイルを作るべき。
  • テンプレートファイルは Apache が読めないようにすべき。

とりあえずこんなところ。