レベル: 中級 Teodor Zlatanov, Programmer, Gold Software Systems
2009年 6月 14日 この 5 回からなる連載では、Amazon の S3 (Simple Storage Service) と SimpleDB を利用した単純な写真共有 Web サイトを、Perl と Apache を使用して構築します。この第 4 回目では、mod_perl を利用したサイト全体のコード・ベースを検証し、最上位レベルの構成方法、それぞれのハンドラーで行っている内容、外部依存関係の設定方法などについて説明します。
この記事では、mod_perl を利用したサイト全体を検証します (今回はコードのみを検証し、テンプレートについては次回に検証します)。そのため説明は、これまでの (ぶらぶら歩き、あるいはゆるい駆け足程度の) ゆったりしたペースから全速力に変わります。
 |
この連載を最大限に活用するために
この連載を読むためには、HTTP と HTML に関する初心者レベルの知識と、JavaScript と Perl (Apache の mod_perl プロセスの内部で使われます) に関する中級レベルの知識が必要です。また、リレーショナル・データベースやディスク・ストレージ、ネットワークなどの知識があると役に立ちます。この連載は回を追うごとに技術的なレベルが高くなるため、そうしたトピックの知識が必要な場合には「参考文献」セクションを参照してください。
|
|
この連載で説明するサイトは実際に動作しますが、ほとんどの詳細部分については十分な説明をしませんので、ソース・コードを読むことを強くお勧めします。それというのも、皆さんが詳細部分を理解することができる、あるいは理解できなかったことを自分で学ぶことができるものと思うからです。どこかの書店、あるいは検索エンジンが皆さんを助けてくれるはずです。
特に、mod_perl を利用したサイトを完全にセットアップする方法や Template Toolkit を使用する方法は、広範にわたるトピックであり、これまで既に何度も説明されてきているので、この連載ではすべてを説明することはしません。それらについて学ぶために最も効果的な方法は、このサイトが動作するようになるまで、あらゆる疑問と障害に取り組むことです。この連載ではエンジンと車輪、ボディー等々を提供しますが、燃料を入手し、車を走らせる仕事は皆さんにかかっています。
これまでと同じように、この連載ではドメイン名として share.lifelogs.com を使います。皆さん自身の環境に合うように、必要に応じてドメイン名を変更することを忘れないでください。
最上位レベルの構成
ここでは、mod_perl をサポートして動作する Apache サーバーが必要です (そこで、そのように Apache サーバーをセットアップしてください)。リスト 1 に示すセクションを Apache の httpd.conf ファイルに挿入します。
リスト 1. Apache の構成ファイル share.httpd.conf に mod_perl のサポートを追加する
<VirtualHost 1.2.3.4:80>
ServerName share.lifelogs.com
DocumentRoot /var/www/html
ErrorLog /var/log/apache/error-share.log
PerlRequire /home/tzz/mod_perl_require_share.pl
<Location />
SetHandler perl-script
PerlHandler SharePerlHandler
</Location>
SetEnv AWS_KEY 'my-AWS-key'
SetEnv AWS_SECRET_KEY 'my-secret-AWS-key'
</VirtualHost>
|
これを見るとわかるように、すべてのものは /home/tzz の配下にあります。
ここで重要な点は以下の 2 点です。
- 専用のエラー・ログがあります (そのため、このサイトのエラーを他とは切り離して調べることができます)。
- Amazon の開発者キーを処理環境の中で渡します。こうすることで、何らかの原因で Perl のソース・コードが漏洩した場合にも、開発者キーはソース・コードの中に含まれません。(通常、Web サーバーの構成の方がソース・コードよりもセキュアです。)
すべてのものが SharePerlHandler によって処理され、すべてのリクエストが share.lifelogs.com で処理されることに注目してください。これは、本番環境ではおそらく望ましくありません。
PerlRequire ディレクティブは単に環境のセットアップのみを行い、何も特別なことはしません。この場合も、すべてのものは /home/tzz の配下にあります。
リスト 2 に、mod_perl_require_share.pl ファイルを示します。
リスト 2. mod_perl_require_share.pl ファイル
#!/usr/bin/perl -w
use strict;
use lib '/home/tzz';
use SharePerlHandler;
1;
|
mod_perl ハンドラー
mod_perl ハンドラーはすべて SharePerlHandler.pm ファイルの中にあります。広い意味で、このハンドラーにはいくつかのセクションがあります (セットアップ、メイン・ハンドラー、コメントと写真用のハンドラー、汎用ユーティリティー、そして SimpleDB ユーティリティー)。
汎用ユーティリティーと SimpleDB ユーティリティーをそれぞれ独自のモジュール内に配置することもできますが、ここでは単純にするために、すべてを 1 ヶ所にまとめています。コメントと写真用のハンドラー、そして SimpleDB ユーティリティー関数は、大部分を simple_go.pl スクリプト (「ダウンロード」を参照) から引用していますが、いくつか小さな変更を加えてあります。
まずは、セットアップ・セクションから始めましょう。各セクションを説明しながら、私が下した判断について説明します (ある特定の方法で私が行った理由として、「単純さ」という説明を何度も聞くことになるでしょう)。優れた Web サイトの構築は簡単ではありません。そのため、ここで紹介するものはすべて概略のテンプレートだと思ってください。これらを最終的な設計として本番に使用したりせず、皆さんの要求と予算に応じて取捨選択してください。これらが動作すること自体が気になる人もいるかもしれませんが、私は何とかして動作させようという誘惑に勝てなかったのです。
外部依存関係のセットアップ
リスト 3 は SharePerlHandler.pm ファイルのセットアップの概要を示しています。
リスト 3. SharePerlHandler.pm ファイルのセットアップの概要
package SharePerlHandler;
use Apache::Constants qw(:common REDIRECT);
use strict;
use Carp qw/verbose cluck carp croak confess/;
use Data::Dumper;
use Apache::Request;
use Template;
use POSIX;
use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
use MIME::Base64;
use Data::UUID; # generates unique IDs
use lib '/home/tzz/amazon-simpledb-2007-11-07-perl-library/src/';
use Amazon::SimpleDB::Client;
|
SharePerlHandler.pm は多くのモジュールに依存しています。何よりもまず、優れた Perl プログラミングをするには欠かせない strict モジュールを使用しています。私は use strict を使って実行しなかったものを本番には使用しません。また、SharePerlHandler.pm で使用するモジュールの説明は以下のとおりです。
Carp モジュールは、より適切なエラー情報を提供することができます。
Data::Dumper は汎用のデバッグ用です。
POSIX は私が頻繁に使用する多くの関数で必要になります。
Digest::HMAC_SHA1 と MIME::Base64 は Amazon S3 のアップロード・ポリシーのためのモジュールです。
Template モジュールは Template Toolkit です。このモジュールを使うと、何らかの動的コンテンツを持つ HTML ページを素早く作成することができます。
Data::UUID は一意の ID を生成するためのものです。
Apache::Request と Apache::Constants は mod_perl が Apache サーバーとやり取りするためのモジュールです。
Amazon::SimpleDB::Client は Amazon のモジュールであり、このモジュールを使うと SimpleDB とやり取りすることができます。
これらのモジュールを CPAN からインストールする方法がわからない人のために言うと、cpan -e 'install MODULE' を実行してインストールします (これを知らないとしたら、その人にはこの記事は少し難しすぎるかもしれません)。
Net::Amazon::S3 モジュールを使うこともできますが (「第 1 回」では、Net::Amazon::S3 は優れた選択肢であると説明しました)、ここでは使いません。簡単にするためにアーキテクチャーに関していくつか決断を下した結果、これらのモジュールが必要なくなったのです。これについてはアップロードについて説明する際に詳しく説明します。
リスト 4 は SharePerlHandler.pm のグローバル設定を示しています。
リスト 4. SharePerlHandler.pm のグローバル設定
use constant IMAGE_MODE => 0;
use constant COMMENT_MODE => 1;
use constant VERBOSE => 1; # can also be done through the environment or some other way
my $template = Template->new( {
INCLUDE_PATH => '/home/tzz/templates/share',
RECURSION => 1,
}
);
my $uuid = Data::UUID->new();
|
基本的な SimpleDB 操作用に、画像モードなのかコメント・モードなのかを表す定数が必要なので、それらの定数をここで定義します。VERBOSE 定数を他の任意のメソッドで置き換え、サーバーの冗長性を制御することができます。ただし冗長性の制御が動的であればあるほど (サーバーは冗長性を毎回チェックする必要があるため)、その処理のコストは高くなることを忘れないでください。
次に、まったく新しい $templates オブジェクトを作成します (このオブジェクトは /home/tzz/templates/share からテンプレートをロードして再帰処理を行います)。最後に、至る所で使用する UUID を生成します。
メイン・ハンドラー
さて、メイン・ハンドラーは大きなセクションです。魔法のようなことが起こるので、よく注意していてください (眠くなってないですか? なら OK です)。まず、リスト 5 をじっくりと見てください。
リスト 5. さらにグローバルな設定
sub handler
{
my $r = shift @_;
my $q = Apache::Request->new($r,
POST_MAX => 4096,
DISABLE_UPLOADS => 1);
my $user = (rand 2 > 1) ? 'bob' : 'ted';
# pick a user randomly between bob and ted
# (50% chance each)
handle_photo($q);
# always try to delete, add, or edit a URL
# if it's passed as a parameter
handle_comment($q);
# always try to delete, add, or edit a comment
# if it's passed as a parameter
my $uri = $q->uri();
my $tfile = $uri;
$tfile =~ s,^/,,; # remove the starting "/" in the name if it exists
$tfile =~ s,/$,,; # remove the ending "/" in the name if it exists
$tfile =~ s,/,_,g; # "/" in the file name becomes "_" so all the
# templates can be in one directory
$tfile = 'index' unless length $tfile;
# make the URI "index" if it's
# empty (e.g. someone hit the / URI)
if ($tfile =~ m/\.html$/)
{
$tfile =~ s/html$/tmpl/; # map ANYTHING.html to ANYTHING.tmpl
}
else
{
$tfile .= '.tmpl'; # map ANYTHING to ANYTHING.tmpl
}
my $policy = '';
my $signature = '';
if ($tfile eq 'upload.tmpl')
{
$template->process('policy.tmpl',
{
username => $user,
},
\$policy) || croak($template->error());
my $key = $ENV{AWS_SECRET_KEY};
$policy = encode_base64($policy);
$policy =~ s/\n//g;
$signature = encode_base64(hmac_sha1($policy, $key));
$signature =~ s/\n//g;
}
$q->send_http_header('text/html; charset=utf-8');
my $output = '';
$template->process($tfile,
{
request => $q,
username => $user,
policy => $policy,
signature => $signature,
env => \%ENV,
params => \%{$q->param()},
fimages => sub { return list_simpledb(sprintf('SELECT * from `%s`',
simpledb_image_domain_name())) },
fcomments => \&get_comments,
},
\$output) || croak($template->error());
print $output;
return OK;
}
|
これは長い関数です。少し長すぎるほどです。さらにもう少しロジックを追加すれば、いくつか独立した部分を抽出できる (最近のクールな人達の言葉では「リファクタリング」できる) はずです。しかしメイン・ハンドラーがどのようなものかを示すためには、これで充分です。
まず、この関数はリクエスト・オブジェクトを取得します。そしてランダムなユーザー名 (「bob」や「ted」など) を設定します。通常、こうした処理には皆さん独自の方法があるはずです (例えばクッキーを使う、あるいは Apache に認証と許可を処理させる、など)。
「第 1 回」では SimpleDB にユーザーのテーブルを作ると説明しましたが、そうするとサイトが複雑になりすぎるため、そのテーブルをやめることにしました。SimpleDB でユーザーを参照することは簡単ではありません。サインアップする方法を用意する必要があり、またユーザー情報を管理する必要があるからです。そうするとコードが大規模になりすぎてしまうため、ここではユーザーのテーブルを省略することにしました (ただし SimpleDB でユーザーのテーブルを保持することは可能です)。
次に、写真またはコメント用のパラメーターを処理する必要があります。例えば deletecommentid パラメーターがある場合には、そのコメント ID を削除する必要があります。写真とコメント用のパラメーター・ハンドラーの詳細は後ほど説明します。
続いて、実際のリクエストを処理する必要があります。このためには単純なマッピングを使用して、例えば any/request/here.html を any_request_here.tmpl に変換した上で、このテンプレートを要求します。「/」をリクエストされた場合には必ず index.tmpl を提供します。
対応するテンプレートがない URI の場合には、データが生成されず、実際にエラーがスローされます。この手法を本番に使用してはいけませんが、この手法を使うと数行で Web サイトをセットアップすることができます。そのため、デモとして簡潔さと単純さが目標の場合には非常に便利なのです。
テンプレート・ファイルが upload.tmpl の場合には、S3 用のアップロード・ポリシーを生成する必要があります。そこで、policy.tmpl ファイルを使ってアップロード・ポリシーを生成します。ユーザー名は、このテンプレートに渡されます (このテンプレートは、この連載の「第 2 回」で説明したテンプレートと非常によく似ています)。リスト 6 はポリシーのテンプレートを示しています。
リスト 6. コード・リストのサンプル (最大の表示幅になっています)
{"expiration": "3000-01-01T00:00:00Z",
"conditions": [
{"bucket": "images.share.lifelogs.com"},
{"acl": "public-read"},
["starts-with", "$key", ""],
["starts-with", "$Content-Type", ""],
["starts-with", "$success_action_redirect",
"http://share.lifelogs.com/s3uploaded?user=[% username %]"],
["content-length-range", 0, 1048576]
]
}
|
第 2 回の場合との大きな違いは、ユーザー名をアップロードが成功した URLの一部に含める代わりに、パラメーターにしていることです。こうすることで画像パラメーター・ハンドラーが大幅に単純になります。これについては後で詳しく説明します。
ここで、ポリシーに署名し、HTTP ヘッダーの送信に進みます (Apache が活躍します)。そして、必要な出力を生成しますが、この出力には以下のように大量のパラメーターがあります。
request: Apache リクエスト
username: ランダムなユーザー名
policy: S3 のアップロード・ポリシー (空白の場合もあります)
signature: S3 のポリシーの署名 (空白の場合もあります)
env: 処理環境 (この環境を本番にしてはいけません)
params: 例えば POST リクエストや GET リクエストのパラメーター
fimages: すべての画像を返す関数
fcomments: (画像の ID をキーとして) すべてのコメントを返す関数
これは汎用のハンドラー用のパラメーターです。ここに記載した以外のすべての魔法は、コメントと画像のパラメーター・ハンドラーの中と、テンプレート自体の中で行われます。ではサイト全体のコード・ベースを検証する旅を続けましょう。
画像とコメントのハンドラーは、リクエストごとに呼び出されます。これらのハンドラーは、適用対象と思えるパラメーターを見つけると、画像またはコメントに対して、追加、変更、削除のいずれかの操作を行います。これらのパラメーターはテンプレートの POST フォームの中に含まれていますが、テンプレートについては SharePerlHandler.pm について学んでから説明します。
しかし大急ぎで先に進む必要はありません。ここには気になるものがたくさんあります。手抜きのコードに、不安定なアーキテクチャー、そして Twitter に投稿する価値のあるバグ (@tzlatanov まったく最低だよ。テンプレートなくてもマッピングしてるし、テンプレート壊れてるし。どう見てもホンモノのハッカーじゃないよな。mod_perl なんて今さらありえないし(笑)) などです。
コメント・パラメーター・ハンドラー
リスト 7 はコメント・パラメーター・ハンドラーを示しています。
リスト 7. コメント・パラメーター・ハンドラー
sub handle_comment
{
my $q = shift @_;
my $user = $q->param('user');
my $imageid = $q->param('refimageid');
my $comment = $q->param('comment');
my $refcommentid = $q->param('refcommentid');
my $commentid = $q->param('commentid');
my $deleteid = $q->param('deletecommentid');
my $result;
if (defined $deleteid) # delete
{
$result = delete_simpledb($deleteid, COMMENT_MODE);
}
elsif (defined $commentid && defined $comment) # edit
{
my %q = (
comment => $comment,
);
put_simpledb($commentid, COMMENT_MODE, %q);
$result = get_simpledb($commentid, COMMENT_MODE);
}
elsif (defined $imageid && defined $comment) # new comment
{
my %q = (
image_id => $imageid,
comment => $comment,
);
$q{reply_to} = $refcommentid if defined $refcommentid;
$q{user} = $user if defined $user;
my $id = new_uuid();
put_simpledb($id, COMMENT_MODE, %q);
$result = get_simpledb($id, COMMENT_MODE);
}
$q->param()->{'result'} = $result;
}
|
コメント・パラメーター・ハンドラーには、渡されるパラメーターに応じて 3 つのモードがあります。これらのモードは相互排他的です。どのモードでも、クエリー・パラメーター result が定義されている場合、この result を適切に設定して処理が成功したかどうかを示します。こうすることで、テンプレートは result が設定されているかどうかを後でチェックし、それに応じて適切な動作を実行することができます。サイトが成長してくると、おそらく他のパラメーター (last_operation や error_message) を追加する必要があるでしょう。
deletecommentid パラメーターが設定されると、コメント・パラメーター・ハンドラーは適切な値を使って delete_simpledb を呼び出します。これは最も単純で条件の無いモードです。
次のモードではコメントを変更します。このモードでは、コメントが既に存在しているかどうかはチェックしません。そのため、ここで不正な ID を使うと新しいコメントが作成されてしまいます。コメントが既に存在しているかどうかのチェックは簡単ですが、コストがかかります (SimpleDB の呼び出しを追加する必要がありますが、そうすると HTTP コマンドを送信して受信するという往復時間に加えて Amazon 側での処理時間が加わるため、動作が遅くなります)。
それぞれのコメントには独自の ID があるため、編集は容易であることに注意してください。個々のコメントにレコードがない場合には、コメントを画像の属性としてまとめる (ストリングの配列にして、各要素が 1 つのコメントになるようにする) こともできるかもしれませんが、そうすると個々のコメントの編集や削除は、はるかに難しくなります。どうしても、コメントの中に独自のレコード構造を実装して ID や投稿ユーザー等を表現しなければならなくなるからです。
この編集モードは、commentid と comment というクエリー・パラメーターがあるとトリガーされます (commentid は編集ターゲットの UUIDを、comment は新しいコメント内容を表現します)。このモードでは、同じ UUID を SimpleDB から取得します。そのため、得られたコメントのフィールドが想定どおりのフィールドであったかどうかを、ここでチェックすることができます (想定どおりではない場合には、何かがおかしかったということです)。ここでは単純にするために、そうしたチェックはしていません。
最後のモードでは新しいコメントを作成します。このモードは、imageid パラメーターと comment パラメーターによってトリガーされます。このモードではオプションとして、参照 UUID (そのコメントが別のコメントへの応答の場合) とユーザー名 (そのコメントが匿名ではない場合) を引数に取ることができます。編集モードとまったく同じように、put_simpledb を使って SimpleDB に属性を格納し、それらの属性を、フィールドが適切に変更されたかどうかのチェックは行わずに再度取得します。
画像パラメーター・ハンドラー
このハンドラーはコメント・パラメーター・ハンドラーと非常によく似ています。そのため、居眠りをしていた人は元に戻ってコメント・パラメーター・ハンドラーの説明を読んでください。
画像の URL が渡されなかった場合、画像の URL は S3 のキーとバケットから作成されます。こうすることで、S3 へのアップロードが成功した場合のリダイレクト処理を一貫性のあるインターフェースで行うことができます。リスト 8 は画像パラメーター・ハンドラーを示しています。
リスト 8. 画像パラメーター・ハンドラー
sub handle_photo
{
my $q = shift @_;
my $user = $q->param('user');
my $name = $q->param('name');
my $bucket = $q->param('bucket');
my $key = $q->param('key');
my $url = $q->param('url');
my $editid = $q->param('imageid');
my $deleteid = $q->param('deleteimageid');
# set the URL from the S3 key and bucket if necessary
if (!defined $url && defined $key && defined $bucket)
{
$url = sprintf("http://%s.s3.amazonaws.com/%s", $bucket, $key)
}
my $result;
if (defined $deleteid) # delete
{
$result = delete_simpledb($deleteid, IMAGE_MODE);
}
elsif (defined $name && defined $editid) # editing an image name
{
my %q = (
name => $name,
);
put_simpledb($editid, IMAGE_MODE, %q);
$result = get_simpledb($editid, IMAGE_MODE);
}
elsif (defined $url && defined $name && defined $user) # adding a new one
{
my %q = (
url => $url,
name => $name,
user => $user,
);
$q{bucket} = $bucket if defined $bucket;
my $id = new_uuid();
put_simpledb($id, IMAGE_MODE, %q);
$result = get_simpledb($id, IMAGE_MODE);
}
$q->param()->{'result'} = $result;
}
|
画像の削除は deleteimageid パラメーターによってトリガーされます。画像の名前を編集するためには、name パラメーターと imageid パラメーターを使います。新しい画像を作成するためには、URL とユーザー名、そして画像の名前を使います。画像のバケットはオプションであり、(S3 へのアップロードが成功し、このサイトにリダイレクトされた後で) 画像パラメーター・ハンドラーが呼び出された場合以外には表示されないはずです。
ポリシーによる要求から、S3 へのアップロードが成功してリダイレクトされる場合には、パラメーターとして URL の一部にユーザー名が含まれることを思い出してください。またこの場合、URL にはキーとバケットも含まれています。そのため、それらを使って画像を作成すればよいだけです。
ユーティリティー関数
ユーティリティー関数は、まさに種々雑多です。これらの関数について調べてみましょう。
リスト 9. 種々雑多な関数
sub qlog
{
printf STDERR @_;
print STDERR "\n";
}
sub new_uuid
{
return $uuid->to_string($uuid->create());
}
sub simpledb_image_domain_name
{
return simpledb_domain_name(IMAGE_MODE);
}
sub simpledb_comment_domain_name
{
return simpledb_domain_name(COMMENT_MODE);
}
sub simpledb_domain_name
{
return sprintf "%s.share.lifelogs.com",
(shift == IMAGE_MODE) ? 'share_photos' : 'share_comments';
}
|
qlog は毎回 printf STDERR と書くのを避けたい場合に便利です。また qlog は改行も出力してくれます。種々雑多な出力を Apache のエラー・ログに記録するかどうかは皆さん次第です。また、
new_uuid は新しい UUID を生成するための関数です。
simpledb_domain_name、simpledb_image_domain_name、simpledb_comment_domain_name は、モード・パラメーター (IMAGE_MODE または COMMENT_MODE) を使って SimpleDB のドメインを提供します。
SimpleDB のユーティリティー関数
SimpleDB のユーティリティー関数は「第 3 回」で紹介したユーティリティー関数 (simple_go.pl) とほとんど同じです。これらの関数を下記の「ダウンロード」セクションからダウンロードしてください。simple_go.pl との違いは以下のとおりです。
get_comments はすべてのコメントを取得するための新しい関数です。キーとして、まず画像 ID を指定し、次に親のコメント ID または noparent を指定します。
print を使う代わりに qlog を使い、また $verbose の代わりに VERBOSE 定数を使います。
- サービスは、リクエストごとに
AWS_KEY と AWS_SECRET_KEY という環境キーを使って初期化されます。
- モードはグローバル変数ではなく、
$mode として関数の間で渡されます。
- エラーが発生した場合には、
die() を使う代わりに、可能な限り丁寧にエラーを処理します。
- ドメイン名の取得には、グローバル変数ではなく
simpledb_domain_name を使います。
- 関数はリネームされています。これは、(目的が 1 つという前回の simple_go.pl スクリプトとは異なり) 目的が 1 つではない名前空間では「get」や「put」などが名前として不適切だからです。
再度注意しておきますが、このコードでは 1 つの属性に対して 1 つの値しか想定していません。いずれかの属性がストリングの配列である場合には、それらのストリングのうちの 1 つしか返されません。属性を書き込む場合、書き込む前には値の配列であったとしても、書き込まれた後には 1 つの値しか残りません。こうすることでコードは大幅に単純になっていますが、SimpleDB を汎用的に使用するには適したコードにはなっていません。
まとめ
今回は、この連載の「第 2 回」と「第 3 回」で作成した部分を使用して mod_perl を利用したサイトを完成させるためのコードについて一通り説明しました。(下記の「ダウンロード」セクションから、第 2 回と第 3 回で作成した部分もダウンロードすることができます)。完成したサイトでは、Template Toolkit、S3、SimpleDB を使って、画像のアップロード、(スレッド化された状態での) 閲覧、編集とコメント追加 (匿名の場合とそうでない場合)、そして削除を行うことができます。
第 5 回では、このサイトのテンプレート・バージョンについて説明します。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| SimpleDB utility functions | simpledb_utility.zip | 3KB | HTTP |
|---|
| Sample script (from Part 2) | s3form.zip | 2KB | HTTP |
|---|
| Sample script (from Part 3) | simple_go.zip | 4KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
- My developerWorks community に参加してください。個人プロファイルとカスタムのホームページを利用することで、皆さんの関心事項に合わせて developerWorks を調整することができ、また他の developerWorks ユーザーとやり取りすることができます。
著者について  | 
|  | Teodor Zlatanov は 1999年にボストン大学 (Boston University) でコンピューター工学の修士号を取得しています。彼は 1992年からプログラマーとして働いており、Perl、Java、C、C++ を使ってきています。彼が関心を持っている領域は、オープンソースによるテキスト構文解析、データベース・アーキテクチャー、ユーザー・インターフェース、UNIX システム管理などです。 |
記事の評価
|