IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  Linux | Open source  >

洗練されたPerl: Perl と Amazon クラウド、第 5 回

コードを理解したら、今度は mod_perl を利用したサイト全体のテンプレートについて調べます

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません

ダウンロード

原文はこちら

原文はこちら


レベル: 中級

Teodor Zlatanov, Programmer, Gold Software Systems

2009年 6月 23日

この 5 回からなる連載では、Amazon の S3 (Simple Storage Service) と SimpleDB を利用した単純な写真共有 Web サイトを、Perl と Apache を使用して構築します。この第 5 回目では、mod_perl を利用したサイト全体のテンプレートを検証します。検証する対象は、索引用のテンプレート、アップロード用の 3 つのテンプレート (汎用、S3 フォーム用、URL 追加用)、画像とコメントの閲覧用のテンプレート、そしてある画像へのコメントを再帰的に閲覧する (つまりスレッドを順に追っていく) ためのテンプレートです。

今回は連載の最終回として、mod_perl を利用したサイト全体を検証します (第 4 回ではコード・ベースを検証したので、今回はテンプレートを検証します)。繰り返しますが、ソースコードを読むことを強くお勧めします。このサイトは実際に動作しますが、この連載ではその詳細について完全には説明しません。それは皆さんが詳細部分を理解することができる、あるいは理解できなかったことを自分で学ぶことができるものと思うからです。どこかの書店、あるいは検索エンジンが皆さんを助けてくれるはずです。

この連載を最大限に活用するために

この連載を読むためには、HTTP と HTML に関する初心者レベルの知識と、JavaScript と Perl (Apache の mod_perl プロセスの内部で使われます) に関する中級レベルの知識が必要です。また、リレーショナル・データベースやディスク・ストレージ、ネットワークなどの知識があると役に立ちます。この連載はかなり専門的な内容であるため、各連載のトピックに関する知識が必要な場合には「参考文献」セクションを参照してください。

特に、mod_perl を利用したサイトを完全にセットアップする方法や Template Toolkit を使用する方法は、広範にわたるトピックであるため、この連載ではすべてを説明することはしません。それらについて学ぶために最も効果的な方法は、このサイトが動作するようになるまで、あらゆる疑問と障害に取り組むことです。この連載ではエンジンと車輪、ボディー等々を提供したことを忘れないでください。今度は皆さんが燃料を入手し、車を走らせる必要があります。

この連載ではドメイン名として share.lifelogs.com を使います。皆さん自身の環境に合うように、必要に応じてドメイン名を変更することを忘れないでください。

index.tmpl

それでは、上から順にテンプレートを説明しましょう (policy.tmpl については第 4 回で説明しました)。Template Toolkit の構文の説明に関しては、「参考文献」セクションを参照してください。ここでは、理解しにくい部分を説明するようにします。

index.tmpl は単純な HTML ページです。1 つだけ、ここで注意が必要な点は、すべての URI が相対 URI であるという点です。そのため、このテンプレートも他のすべてのテンプレートも、任意のドメインで動作します。


リスト 1. index.tmpl は単純な HTML です

<html>
  <head>
<title>Share Pictures</title>
</head>
<body>
<h1>Share Pictures</h1>

You can
<a href="/upload">upload or add images</a>
or
<a href="/browse">browse images and comments</a>.

<address>
Contact <a href="mailto:tzz@bu.edu">Ted Zlatanov</a> if you have lots
of money you're trying to get out of Nigeria.  The breath
is <strike>baited</strike>bated.
</address>
</body>
</html>




上に戻る


upload.tmpl

さて、これから面白い部分に入ります。ここには、JavaScript、HTML、そして Template Toolkit 言語が 1 ヶ所にまとまっています。これが Web デザイナーを喜ばせないとしたら、何が彼らを喜ばせるのか、私にはわかりません。


リスト 2. upload.tmpl は Web デザイナーを喜ばせるのに十分です

<html> 
  <head>
    <title>Upload Page For [% username %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js"
                 type="text/javascript"></script>
  </head>

  <body> 
  <script language="JavaScript">
function OnSubmitForm()
{
 var form = $('uploader');
 var file = form['file'];
 var ct   = form['Content-Type'];
 var name = form['name'].value;

 if (!name || name.length < 1)
 {
  alert("Sorry, you can't upload without a name.");
  return false;
 }

 var filename = ''+$F(file);
 var f = filename.toLowerCase(); // always compare against the lowercase version

 if (!navigator['mimeTypes'])
 {
  alert("Sorry, your browser can't tell us what type of file you're uploading.");
  return false;
 }

 var type = $A(navigator.mimeTypes).detect(function(m)
 {
  // does any of the suffixes match?
  return m.type.length > 3 && m.type.match('/') &&
   $A(m.suffixes.split(',')).detect(function(suffix)
  {
    return f.match('\.' + suffix.toLowerCase() + '$');
  });
 });

 if (!type || !type['type'])
 {
  type = { type : prompt("Enter your own MIME type, we couldn't find one through
                          the browser", "image/jpeg") };
 }

 if (type && type['type'])
 {
  ct.value = type.type;

  // fix up the redirect if we're about to submit
  var sar  = form['success_action_redirect'];

  sar.value = sar.value + escape(name);
  return true;
 }

 alert("Sorry, we don't know the type for file " + filename);
 return false;
}
</script>
<h1>Hi, [% username %]</h1>
    <form id="uploader" action="https://images.share.lifelogs.com.s3.amazonaws.com/"
          method="post" enctype="multipart/form-data" onSubmit="return OnSubmitForm();">
      <input type="hidden" name="key" value="${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="[% env.AWS_KEY %]"> 
      <input type="hidden" name="acl" value="public-read">
      <input type="hidden" name="success_action_redirect"
             value="http://share.lifelogs.com/s3uploaded?user=[% username %]&name=">
      <input type="hidden" name="policy" value="[% policy %]">
      <input type="hidden" name="Content-Type" value="image/jpeg">
      <input type="hidden" name="signature" value="[% signature %]">
      Select File to upload to S3:
      <input name="file" type="file"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
    <form id="adder" action="/urluploaded" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      Enter a URL:
      <input name="url" type="text"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br>
      <input type="submit" value="Add URL"> 
    </form> 
  </body>
</html>

アップロード・ページには 2 つのアップロード・ダイアログが表示されます。どちらのダイアログを使っても画像を追加することができますが、2 番目の方がはるかに簡単です。2 番目のダイアログでは、ユーザーが画像の URL と名前を入力すると、その画像が /urluploaded に POST されますが、/urluploadedurluploaded.tmpl にすぎません。このテンプレートが表示されると、画像パラメーター・ハンドラーが自動的に呼び出されます。ユーザー名は、サーバーから取得され、フォームには表示されない POST パラメーターです。

最初のフォームは非常に複雑です。幸い、この連載の「第 2 回」では S3 へのアップロードのすべてを説明しているので、皆さんはこのフォームを見てもまったく驚かないはずです。

第 2 回の s3form.pl に加えた主な変更は以下のとおりです (この記事の「ダウンロード」セクションからも s3form.pl を入手することができます)。

  • success_action_redirect はユーザー名と画像の名前をパラメーターとして渡します。ポリシーは、このストリングの一部のみ (ユーザー名までを含みますが、画像の名前は含みません) を要求するように調整されています。
  • ポリシーと署名はサーバーから渡されます。
  • AWS アクセス・キーと秘密鍵はサーバーからの env ハッシュで渡されます。
  • OnSubmitForm 関数は画像の名前を要求し、その画像の名前をパラメーターとして、エスケープして success_action_redirect フォーム・フィールドに追加します (画像の名前が要求されるのは MIME タイプが判断されるよりも前ですが、URL に追加されるのはフォームが POST される直前であることに注意してください)。
  • OnSubmitForm 関数は MIME タイプが見つからない場合はグレースフルに失敗するため、ユーザーが独自の MIME タイプを指定できるようになっています。



上に戻る


s3uploaded.tmpl

このテンプレートは S3 にアップロードする際に使われます。


リスト 3. s3uploaded.tmpl を使ってアップロードに成功する場合と失敗する場合

[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              Upload Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       uploaded [% params.key %] to S3 bucket [% params.bucket %]
       named [% params.name %].<p>
      (etag is [% params.etag %] but I doubt you care.)
    <p>
[% IF success %]
      <a href="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
   Your new upload is probably here.  Let's see if it displays already.
   <img src="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a> or
      <a href="/">the main page</a>.
  </body>
</html>

Template Toolkit のあまり美しくない IF-ELSE 構成体と result パラメーターにより、このページはアップロードの成功と失敗を処理します。成功した場合には SimpleDB への追加を行います。この段階では、画像は必ず S3 にアップロードされています。SimpleDB への追加に失敗した場合に S3 にアップロード済みの画像を取り消す方法については、読者の演習として残しておきます。




上に戻る


urluploaded.tmpl

このテンプレートは URL を追加する場合に使われます。


リスト 4. urluploaded.tmpl (同じコードを再び利用するという夢の世界)

[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              URL add Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       added [% params.url %] named [% params.name %].<p>
    <p>
[% IF success %]
      <a href="[% params.url %]">
   The URL you added is, perhaps, visible here.
   <img src="[% params.url %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a>
      or <a href="/">the main page</a>.
  </body>
</html>

このコードは明らかに不適切な HTML ですが、それに加え、このテンプレートは s3uploaded.tmpl と似ています。この HTML の作成者の夢の世界では、同じコードを再び利用していることはまったく問題にしていません。




上に戻る


browse.tmpl

このテンプレートは、画像とコメントを閲覧する際に使用します。


リスト 5. browse.tmpl、画像とコメントを 1 つのテンプレートで処理する

[% SET images = fimages() %]
[% SET comments = fcomments() %]
<html> 
  <head>
    <title>Browse</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    <ul>
      [% FOR ik IN images.keys %]
      <li>
         [% SET image = images.$ik %]
         [% image.name %]<br>
         <img src="[% image.url %]"><br>
         [% IF image.bucket %](in S3)[% END %]<br>
         uploaded by [% image.user %]<br>
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="deleteimageid" value="[% ik %]">
           <input type="submit" value="Delete"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="imageid" value="[% ik %]">
           Change Image Name:
           <input name="name" type="text" value="[% image.name|html %]"> 
           <input type="submit" value="Rename"> 
         </form>
         [% INCLUDE comments.tmpl ik=ik comments=comments %]
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="user" value="[% username %]">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter a Comment (as user [% username %]):
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter Anonymous Comment:
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
      </li>
      [% END %]
    </ul>
  </body>
</html>

このテンプレートを使うと、すべての画像とコメントを 1 度に取得することができます。ただし、数千もの画像とコメントを含むような実際の Web サイトでは、このテンプレートを使うのは効率的ではなく、もし使ったとすると動作に問題を生じるのはほぼ確実で、大変なことになります。SimpleDB の便利な NextToken によるページネーション・メカニズムを使って、あるいは皆さん独自のメカニズムを使って、画像に対するページネーションを設定し、表示しようとしている画像のコメントのみを取得できるようにする必要があります (この記事で紹介している SimpleDB ユーティリティー関数では、NextToken をあまり効果的に使っていません)。

必要ない限り、コメントを取得しないようにする必要があります。SimpleDB へのリクエストのコストは安くありません。このテンプレートが毎回 comments.tmpl テンプレートにコメントを渡しているのは、そのためです。

このテンプレートは Template Toolkit の FOR ループを使って、ソートをせずに画像に対して繰り返し処理を行っています (ソートが必要な場合には、Template Toolkit 環境の外で Perl コードにソートさせた方が適切です)。ここでは画像に対応するコメントを選択するために画像キーが必要です。各画像に対して、画像の名前、URL、S3 のステータス、所有者を表示します。次にフォームを表示します。このフォームでは、画像の削除、画像の名前の変更、または匿名あるいは 1 人のユーザーとしてコメントを投稿することができます。非常に単純です。

最後に (実は中ほどに、なのですが、私達は直線的に考える人間ではありません)、INCLUDE コマンドによって、いくつかのパラメーター (ik は画像のキーであり、comments はコメント・リストです) と共に comments.tmpl テンプレートが組み込まれます。これはつまり、comments.tmpl テンプレートによって生成されるものはすべて、各画像の画像リストの中に挿入されるということです。




上に戻る


comments.tmpl

このテンプレートは、ある画像に対するコメントを再帰的に閲覧します (スレッドを追っていきます)。


リスト 6. comments.tmpl によってスレッドを追っていく

[% IF parent %]
 [% SET thread = comments.$ik.$parent %]
[% ELSE %]
 [% SET thread = comments.$ik.noparent %]
[% END %]

<ul>
[% FOR ck IN thread.keys %]
 [% SET comment = thread.$ck %]
  <li>[% comment.comment %] (by [% IF comment.user %][% comment.user %]
      [% ELSE %]Anonymous[% END %])<br>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="deletecommentid" value="[% ck %]">
      <input type="submit" value="Delete"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="commentid" value="[% ck %]">
      Edit Comment:
      <input name="comment" type="text" value="[% comment.comment|html %]"> 
      <input type="submit" value="Edit"> 
    </form>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter a Comment (as user [% username %]):
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter Anonymous Comment:
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    [% INCLUDE comments.tmpl ik=ik comments=comments parent=ck %]
  </li>
[% END %]
</ul>

そして私はついに、最高であると同時に最も扱いにくいコードを保存しました。

画像キーを指定すると、このテンプレートは、親が特定のコメント・キーと等価である画像に対するすべてのコメントを見つけます。

関心対象の各コメント (指定の親を持つ場合もあれば、親がない場合もあります) に対して、このテンプレートはコメント自体を表示し、それに続いて、コメントの削除、編集、または (あるユーザー名での、または匿名での) 新しいコメントの入力のための HTML フォームを表示します。これは browse.tmpl のフォームとほとんど同じですが、refcommentid パラメーターを含む点が異なります。

そして、ここが巧妙な部分、あるいはひどい部分ですが (コンピューター・サイエンスを研究する人と教える人とで受け取り方が異なります)、このテンプレートはコメントを見つけるごとに、テンプレート自身の中にさらにテンプレートを含め、その都度 parent をコメント・キーの値に設定しています。つまり、疑似言語 (Template Toolkit) で再帰を行ない、Perl で実行するレイアウト言語 (HTML) の中に再帰を生成しています。




上に戻る


まとめ

この 5 回の連載はこれで終わりです。今回は、第 2 回と第 3 回で作成した部分、そして第 4 回で生成したコードを使用して、mod_perl を利用したサイト全体に対するさまざまなテンプレートを調べました。このサイトでは、Template Toolkit、S3、SimpleDB を使って画像のアップロード、閲覧、編集、削除を行うことができ、またコメントの追加 (匿名の場合とそうでない場合)、(スレッド化された状態での) 閲覧、そして削除を行うことができます。





上に戻る


ダウンロード

内容ファイル名サイズダウンロード形式
SimpleDB utility functionssimpledb_utility.zip3KBHTTP
Sample script (from Part 2)s3form.zip2KBHTTP
Sample script (from Part 3)simple_go.zip4KBHTTP
ダウンロード形式について


参考文献

学ぶために
  • 最初に「洗練されたPerl: Perl と Amazon クラウド、第 1 回」(developerWorks、2009年3月) を読み、Web サイトの構築に Amazon の S3 と SimpleDB を使用する場合の利点と欠点を理解してください。次に「第 2 回」(2009年4月) に進み、Web ページから HTML フォームを利用してファイルを S3 にアップロードし、サーバーの負荷を最小限にとどめる方法を学んでください。そして「第 3 回」(2009年6月) を読み、テーブルの中の URL のリストを使って画像をアップロードする方法、そして画像とコメントを管理する方法を学んでください。「第 4 回」では、このサイト全体のコード・ベースを検証しています。

  • 以下のサービスの詳細と開発者リソースが Amazon.com に提供されています。
  • navigator オブジェクトのプロパティーである JavaScript MimeType の驚くべき機能について学んでください。navigator オブジェクトは最上位レベルのオブジェクトであり、使用されるクライアント・インターネット・ブラウザーまたは Web ナビゲーター・プログラムをオブジェクトとして表現したものです。

  • mod_perl を利用すると Perl と Apache HTTP サーバーの強力さを完全に活用することができます。

  • Prototype は JavaScript フレームワークです。このフレームワークでは、クラス駆動開発用のツールキットと地球上で「最も優れた」Ajax ライブラリーを利用して、動的な Web アプリケーションを容易に作成することができます。Prototype の使い方を説明した素晴らしい記事として、Sergio Pereira による「Developer Notes for prototype.js」があります。

  • S3 とSimpleDB は Web ベースのサービスであり、使用できなくなる場合があるため、使用できない場合への対応は重要な検討要素です。

  • Amazon のサービスを使ったクラウド・コンピューティングがホットな話題となっています。この連載では Perl を使って Amazon のサービスにアクセスする方法を説明していますが、このサービス全体を幅広く概説した記事として、別の連載記事「Amazon Web サービスを利用したクラウド・コンピューティング」(developerWorks、2008年7月から2009年2月) を読んでください。

  • developerWorks の Linux ゾーンには Linux 開発者のための資料が豊富に用意されています。最も人気のあった記事やチュートリアルの一覧もご覧ください。

  • developerWorks に掲載されているすべての「Linux のヒント」シリーズの記事と Linux チュートリアルを参照してください。

  • developerWorks の Technical events and webcasts で最新情報を入手してください。


製品や技術を入手するために
  • CPAN には、迅速にテンプレートを作成するためのキットである Template-Toolkit-2.20 のための、ダウンロード、モジュール・リスト、そしてドキュメントが用意されています。この技術を開発したチームの中核メンバーによる著書、『Perl Template Toolkit』が O'Reilly から出版されています。

  • CPAN (Comprehensive Perl Archive Network) サイトには膨大な数のモジュールとモジュールのドキュメントが用意されています。

  • S3Fox (Amazon S3 Firefox Organizer) は S3 を管理するための Firefox 用アドオンであり、S3 のための使いやすいフロントエンドとして機能します。

  • developerWorks から直接ダウンロードできる IBM ソフトウェアの試用版を使用して皆さんの次期 Linux 開発プロジェクトを構築してください。


議論するために
  • My developerWorks community に参加してください。個人プロファイルとカスタムのホームページを利用することで、皆さんの関心事項に合わせて developerWorks を調整することができ、また他の developerWorks ユーザーとやり取りすることができます。


著者について

photo- teodor zlatanov

Teodor Zlatanov は 1999年にボストン大学 (Boston University) でコンピューター工学の修士号を取得しています。彼は 1992年からプログラマーとして働いており、Perl、Java、C、C++ を使ってきています。彼が関心を持っている領域は、オープンソースによるテキスト構文解析、データベース・アーキテクチャー、ユーザー・インターフェース、UNIX システム管理などです。




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ