CGI::Applicationモジュール v.1.2

by Hippo2000(2000/9/3)

CGI::ApplicationモジュールはHTML::Template、CGI.pmをベースとしたCGIアプリケーションを作成するためのフレームワークを提供します。

作者はJesse Erlbaumさんです。メールで許可をいただきました。

内容等が間違っていたら修正します。ご連絡ください。


目次


名前

CGI::Application - 再利用可能なWebアプリケーションを構築するためのフレームワーク


概要

  # WebApp.pm
  package WebApp;
  use base 'CGI::Application';
  sub setup {
        my $self = shift;
        $self->start_mode('mode1');
        $self->mode_param('rm');
        $self->run_modes(
                'mode1' => \&do_stuff,
                'mode2' => \&do_more_stuff
        );
  }
  sub teardown {
        my $self = shift;
        # Post-response shutdown functions
  }
  sub do_stuff { ... }
  sub do_more_stuff { ... }
 
 
  # webapp.cgi
  use WebApp;
  my $webapp = WebApp->new();
  $webapp->run();

使用例

CGI::Applicationは洗練された、再利用可能なWebベースのアプリケーションの作成を簡単にすることを目指しています。このモジュールは、もし従えばWebソフトウェアをデザインしやすく、ドキュメントしやすく、書きやすく、発展させやすくする方法論を実装しています。

CGI::ApplicationはCommon Gateway InterfaceそしてLincoln D. SteinsのすばらしいCGI.pmモジュールといった標準的で、独占されていない技術と技法で構築されています。CGI::Applicationは分別をもって、開発者を特定のツール、オペレーティング・システムまたはWebサーバに結び付けるような技術や技法を使うのを避けています。

CGI::Applicationの典型的な使い方の例を以下に示します。

あなたはウィジットのデータベースを検索するアプリケーションを書かなければならないとします。そのアプリケーションは3つの画面を持ちます:

   1. 検索フォーム(Search form)
   2. 結果のリスト(List of results)
   3. 1つのレコードの詳細(Detail of a single record)

CGI::Applicationを使って、このアプリケーションを書くためには2つのファイルを作らなければなりません:

   1. WidgetView.pm -- あなたの"アプリケーション・モジュール(Application Module)"
   2. widgetview.cgi -- あなたの "インスタンス・スクリプト(Instance Script)"

アプリケーション・モジュール(Application Module)には、あなたのアプリケーションの機能に関するすべてのコードが入ります。そしてWebサーバのドキュメント・ルートの外で、Perlライブラリの検索パスのどこかにあります。

インスタンス・スクリプト(Instance Script)が実際にWebサーバから呼ばれます。それはとても小さく、単純なファイルで、それは単にあなたのアプリケーションのインスタンスを作成し、継承されたメソッドrun()を呼び出します。以下が"widgetview.cgi"の全体です。

   #!/usr/bin/perl -w
   use WidgetView;
   my $webapp = WidgetView->new();
   $webapp->run();

ご覧の通り、widgetview.cgi は単にアプリケーション・モジュール(それは"WidgetView"というPerlバッケージを実装しています)を"use"しているだけです。アプリケーション・モジュール"WidgetView.pm"は、より長いものになります:

   package WidgetView;
   use base 'CGI::Application';
   use strict;
   # データベース接続のために必要
   use DBI;
   sub setup {
        my $self = shift;
        $self->start_mode('mode1');
        $self->run_modes(
                'mode1' => \&showform,
                'mode2' => \&showlist,
                'mode3' => \&showdetail
        );
        # DBIデータベースに接続
        $self->param('mydbh' => DBI->connect());
   }
   sub teardown {
        my $self = shift;
        # 終ったら切断
        $self->param('mydbh')->disconnect();
   }
   sub showform {
        my $self = shift;
        # CGI query オブジェクトを取得
        my $q = $self->query();
        my $output = '';
        $output .= $q->start_html(-title => 'Widget Search Form');
        $output .= $q->start_form();
        $output .= $q->textfield(-name => 'widgetcode');
        $output .= $q->hidden(-name => 'rm', -value => 'mode2');
        $output .= $q->submit();
        $output .= $q->end_form();
        $output .= $q->end_html();
        return $output;
   }
   sub showlist {
        my $self = shift;
        # データベース接続を取得
        my $dbh = $self->param('mydbh');
        # CGI query オブジェクトを取得
        my $q = $self->query();
        my $widgetcode = $q->param("widgetcode");
        my $output = '';
        $output .= $q->start_html(-title => 'List of Matching Widgets');
        ## DBI接続されたデータベースから、CGI.pm queryオブジェクトを介して
        ## 前のHTMLフォームから提供される、ユーザが指定した"widgetcode"の
        ## 値に対応する"widgets"を取得するための処理をおこなう。
        ## 
        ## 各行には以下のようにアンカー・タグを提供する"Widget Detail"へ
        ## リンクが入ります。
        ##
        ##   "widgetview.cgi?rm=mode3&widgetid=XXX"
        ##
        ##  ..."XXX"はユーザがクリックした"widget"に対するIDを表す
        ## ユニークな値になります。
        $output .= $q->end_html();
        return $output;
   }
   sub showdetail {
        my $self = shift;
        # データベース接続を取得
        my $dbh = $self->param('mydbh');
        # CGI query オブジェクトを取得
        my $q = $self->query();
        my $widgetid = $q->param("widgetid");
        my $output = '';
        $output .= $q->start_html(-title => 'Widget Detail');
        ## ユーザがクリックした"widget"のプロパティをすべて取得するための
        ## 処理を行います。このwidgetのキー IDの値は、CGI.pm query
        ## オブジェクトの"widgetid"プロパティを通して与えられます。
        
        $output .= $q->end_html();
        return $output;
   }

CGI::Applicationはnew()とrun()メソッドの実装の面倒を見ます。STDOUTに出力をおくるためにprint()を呼んでいないことに注意してください。代わりにすべての出力はスカラで返されます。

CGI::Applicationのもっとも大きな貢献はアプリケーションの状態を管理することです。アプリケーションを前に進めるために必要なことは、HTMLフォーム・パラメータ''rm'の値を、フォーム・サブミッションを扱いたい実行モード("run mode")の値に設定することだけです。これがCGI::Applicationの鍵です。


概略

CGI::Applicationの裏にある哲学をガイドするとWebベースのアプリケーションは特定の"実行モード(Run-Mode)"の集まりにまとめることができるということです。各実行モードは大まかに1つの画面(フォーム、ある種の出力等)に対応します。実行モードはPerlモジュールである1つの"アプリケーション・モジュール(Application Module)"によって管理されます。Webサーバのドキュメント空間には"インスタンス・スクリプト(Instance Script)"があり、それはWebサーバによってCGI(あるいはApache+mod_perlを使っていればApache::Registryスクリプト)として呼び出されます。

この方法論は"埋め込み(Embedded)"式(ASP,JSP、EmbPerl、Masonなど)の反対です。それらではアプリケーションのそれぞれの状態のための"ページ"があり、ページが機能を起動します。CGI::Applicationではフォームは機能に従います - アプリケーション・モジュールがページを起動し、1つのアプリケーションのコードは1ヶ所にあります;複数の"ページ"に散らばったりしません。もし埋め込みアーキテチャがわかりにくい、まとまっていない、設計しにくい、管理しにくいと感じるのであれば、CGI::Applicationは、そんなあなたのための方法なのです!

CGI::ApplicatinにApacheは必須ではありません。CGI::ApplicationをベースとしたWebアプリケーションはNT/IISや他の全てのCGI互換性のある環境でも同じように動きます。しかしながら、CGI::ApplicationをベースとしたアプリケーションはApache/mod_perlサーバにとても適しています。というのも自然に正しいプログラミングの実践を勧めるからです。常にuse strictすることによって!


説明

CGI::Applicationはオブジェクト指向のPerlモジュールで、抽象クラスを実装します。このパッケージを直接インスタンス化するようにはなっていません。代わりにあなたのアプリケーション・モジュールがCGI::Applicationのサブ・クラスとして実装されるようになっています。

CGI::Applicationから継承するためは、以下のコードがパッケージ宣言の後で、アプリケーション・モジュールのはじめになければなりません。

    use base 'CGI::Application';

表記法と規約

このドキュメントのために、以下の表記法を利用します:

  WebApp.pm   アプリケーション・モジュール・クラスを実装するPerlモジュール
  WebApp      アプリケーション・モジュール・クラス:CGI::Applicationのサブクラス.
  webapp.cgi  インスタンス・スクリプト。アプリケーション・モジュールをを実装します
  $webapp     アプリケーション・モジュール・クラスのインスタンス(オブジェクト)
  $self       $webappと同じ。インスタンス・メソッドで現在のオブジェクトを表します
              (標準のPerlオブジェクト指向のテクニック)

インスタンス・スクリプト・メソッド

CGI::Applicationから継承することにより、多くの組み込みメソッドにアクセスできます。以下はインスタンス・スクリプトから呼ばれるであろうと考えられるものです。

new()
new()メソッドはCGI::Applicationのためのコンストラクタです。ブレスされたアプリケーション・モジュール・パッケージ(クラス)へのリファレンスを返します。new()はオプションでキー => 値の組をとることもできます:
    my $webapp = App->new(
                TMPL_PATH => 'App/',
                PARAMS => {
                        'custom_thing_1' => 'some val',
                        'another_custom_thing' => [qw/123 456/]
                }
    );

このメソッドはいくつかの特定のパラメータをとることができます:

TMPL_PATH - これはオプションのパラメータで、load_tmpl()メソッド(以下で定義)へ値を追加します。これはHTML::Templateオブジェクトを取得するようにload_tmpl()を呼び出したとき、指定されたすべてのファイル名に付けられるパスを設定します。この実行時パラメータにより、テプレートのインスタンス作成をさらにカプセル化し、再利用のさらなる可能性を提供します。

PARAMS - このパラメータは、もし使われると、実行時にいくつかのカスタム・パラメータを設定することができます。異なる値を同じアプリケーション・モジュールを使う異なるインスタンス・スクリプトで渡すことにより、さらに高いレベルの再利用性を実現できます。例えば、"MailForm.pm"というアプリケーション・モジュールがあるとします。アプリケーションはHTMLフォームの内容を取得し、指定された宛先にメールを送ります。すべてこの"MailForm.pm"モジュールを使いながらも、異なる宛先、異なるフォームを設定する複数のインスタンス・スクリプトをサイトのあちこちに置くことができます。

run()
run()メソッドはインスタンス・スクリプトからアプリケーション・モジュール・オブジェクトに対して呼ばれます。呼ばれると、アプリケーション・モジュールの機能を実行します。
    my $webapp = WebApp->new();
    $webapp->run();

このメソッドは、まずmode_param()(デフォルトは"Run Mode"を表す"rm")によって指定されるCGIパラメータの値を検索することにより、アプリケーションの状態を判定します、それには操作のモードの名前が入っていることが期待されます。指定されなければ、状態はstart_mode()の値がデフォルトになります。

 
モードが判定されたら、run()はrun_modes()に格納されたディスパッチ・テーブルを検索し、モード名がキーになっている関数ポインタを見つけます。もしあれば関数が呼ばれ、返されたデータがSTDOUT、そしてブラウザにprint()されます。指定されたモードがrun_modes()テーブルになければ、run()はcroak()します。

サブクラス化とメソッドの上書き

CGI::Applicationは、サブクラス・モジュールで実装されることによりオーバーライドされることが期待されるいくつかのメソッドを実装します。これらのメソッドは以下の通りです:

setup()
このメソッドは継承されたnew()コンストラクタ・メソッドにより呼び出されます。setup()メソッドは以下のプロパティ/メソッドを定義するために呼ばれなければなりません:
    mode_param() - 実行モードCGIパラメータの名前を設定する。
    start_mode() - デフォルトの実行モードが入ったテキスト・スカラ。
    run_modes() - モード => 関数の対応が入ったハッシュ・テーブル。
    tmpl_path() - テンプレート・ファイルへのパスが入ったテキスト・スカラ。

setup()メソッドはアプリケーションのすべてのインスタンス・メソッドを呼ぶ子とができます。この関数は$webapp->param()メソッドを通してアプリケーション独特のプロパティを定義するのに適した場所です。

setup()メソッドは以下のように実装されるでしょう:

        sub setup {
                my $self = shift;
                $self->tmpl_path('/path/to/my/templates/');
                $self->start_mode('putform');
                $self->run_modes({
                        'putform' => \&my_putform_func,
                        'postdata' => \&my_data_func
                });
                $self->param('myprop1');
                $self->param('myprop2', 'prop2value');
                $self->param('myprop3', ['p3v1', 'p3v2', 'p3v3']);
        }
teardown()
このメソッドはアプリケーションが実行した後、自動的に呼ばれます。操作の後の掃除のためにつかうことができます。teardown()関数の典型的な使い方は、setup()関数で確立されたデータベース接続の切断です。サーバにアプリケーションについての情報を状態情報を格納するためにteardown()を使うこともできます。

アプリケーション・モジュール・メソッド

以下のメソッドはCGI::Applicationから継承され、アプリケーション・モジュールの中でアプリケーションに呼ぶことができます。これらの関数をアルファベット順に並べます。

dump()
    print STDERR $webapp->dump();

dump()メソッドはデバッグ用の関数で、リクエストの環境変数とCGIフォームデータのすべてが入った、人間に読みやすくフォーマットされたテキストを返します。 STDERRに出力するのに便利です。

dump_html()
    my $output = $webapp->dump_html();

dump_html()メソッドはデバッグ用関数でリクエストの環境変数とCGIフォームデータのすべてが入った、ブラウザを通して人間が読みやすくフォーマットされたテキストを返します。ブラウザに出力するのに便利です。

header_props()
    $webapp->header_props(-type=>'image/gif',-expires=>'+3d');

header_props()メソッドはCGI.pmに互換性のあるHTTPプロパティのハッシュを期待します。これらのプロパティはCGI.pmのheader()またはredirect()メソッド直接渡されます。使い方の詳細についてはCGIをご覧ください。

HTTPヘッダについての重要な注意

header_props()メソッドを通して、出力されるHTTPヘッダを変更することができます。クッキーを設定したり、"text/html"以外のMIMEタイプを設定したり、リダイレクトを実行するためにこれは必要です。header_props()メソッドはheader_type()と一緒に機能します。header_type()に入っている値が、CGI::header()かCGI::redirect()のどちらを使うのかを決定します。header_props()の内容は、呼び出されるどちらかのCGI.pm関数に引数として渡されます。

この関係を理解することは、HTTPヘッダを正しく操作したいのであれば重要です。

header_type([<'header' || 'redirect'>])
    $webapp->header_type('redirect');

header_type()メソッドは'header'または'redirect'を渡されることを期待します。このメソッドはブラウザに送信されるHTTPヘッダのタイプを指定します。指定されなければデフォルトは'header'です。詳細についてはCGIのheaderセクションをご覧ください。

load_tmpl()
    my $tmpl_obj = $webapp->load_tmpl('some.tmpl');

このメソッドはテンプレート・ファイルの名前を取り、HTML::Templateオブジェクトを返します。そのオブジェクトの作成のためにHTML::Template->new_file()コンストラクタが使われます。HTML::Templateの特別な使い方についてはHTML::Templateをご覧ください。

tmpl_path()が設定されていると、load_tmpl()はtmpl_path()プロパティに与えられたファイル名を付けます。これはさらにテンプレート使用のカブセル化を進めます。

 

load_tmpl()メソッドは、それに渡されたすべての余分なパラメータをHTML::Template->new_file()に直接渡します。これによりHTML::Templateオブジェクトをさらにカスタマイズ化することができます。

    my $tmpl_obj = $webapp->load_tmpl('some_other.tmpl', 
         die_on_bad_params => 0,
         cache => 1
    );

アプリケーションがこれよりもさらに特殊化する必要があれば、CGI::Applicationサブクラス・アプリケション・モジュールで独自のload_tmpl()を実装することによりload_tmpl()をオーバーライドすることを推奨されます。

mode_param()
    $webapp->mode_param('rm');

このアクセサ/ミューテータ・メソッドは一般的にはsetup()メソッドで呼ばれます。mode_param()メソッドはアプリケーションの実行モード(run mode)が入ったCGIフォーム・パラメ−タの名前を設定します。指定されなければ、デフォルトの値は'rm'です。このCGIパラメータは、プログラムを正しいモードにするために、run()メソッドによって問い合わせられます。

param()
    $webapp->param('pname', $somevalue);

param()メソッドは、これを通してアプリケーション全体でアクセス可能なアプリケーション・インスタンス・プロパティを設定することができる機能を供します。

param()メソッドは2つの基本的な方法で利用されます。まずパラメータの値を取得したり、設定するために使用できます:

    $webapp->param('scalar_param', '123');
    my $scalar_param_values = $webapp->param('some_param');

つぎに配列コンテキストで呼ばれ、パラメータなしで呼ばれると、param()は現在設定されている全てのパラメータが入った配列を返します:

    my @all_params = $webapp->param();

param()メソッドはインスタンス毎にアプリケーションをカスタマイズするためにとても価値のあるシステムを可能にします。あるアプリケーション・モジュールは異なるインスタンス・スクリプトによりインスタンス化されるかもしれません。各インスタンス・スクリプトはパラメータに異なる値を設定するかもしれません。これにより似ているアプリケーションが共通のコード・ベースを共有しながらも、違った動きをすることを可能にします。例えば、1つのアプリケーション・モジュールと複数のインスタンス・スクリプトを持つメール・アプリケーションがあるとします。各インスタンス・スクリプトは異なる宛先を指定するでしょう。もう1つの例はWeb掲示板システムです。複数の掲示板があり、それぞれには異なる話題と異なる管理者がいるでしょう。

new()メソッドは一度に複数の実行時パラメータを指定するためのショートカットを提供します。内部的にはCGI::Applicationが、これらのプロパティを設定するためにparam()メソッドを呼び出します。param()メソッドはアプリケーションの再利用性を高めるのにとても強力なツールです。

query()
    my $q = $webapp->query();
    my $remote_user = $q->remote_user();

このメソッドはアプリケーション・モジュールをインスタンス化することにより作成されるCGI.pmのqueryオブジェクトを取り出します。問い合わせ(query)オブジェクトの使い方の詳細については、CGIをご覧ください。CGI::ApplicatinはCGImジュールの上に組み立てられています。フォーム・データに関連したいときにはいつでもqueryオブジェクトを使うので、一般的には、CGI.pmにとても慣れたいと思うでしょう。

new()メソッドが呼ばれると、CGI queryオブジェクトは自動的に作成されます。いくつかの理由で、独自のCGI queryオブジェクトを使いたければ、new()メソッドは既存のqueryオブジェクトを作成時に渡すこともサポートしています。

run_modes()
    $webapp->run_modes('mode1' => \&some_sub, 'mode2' => \&some_other_sub);

このアクセサ/ミューテータは異なるCGIステータスのためのディスパッチ・テーブルを指定するハッシュを期待します。runメソッドは、mode_param()により指定された(デフォルトは'Run Mode'を表す'rm')CGIパラメータを読み込むことによりCGIを正しい関数に送るために、このテーブルのなかのデータを使います。

このメソッドにより設定されるハッシュ・テーブルは、モード名をキーとして持つことが期待されます。値はCGIが指定されたモードに入ったときに、呼び出されて欲しい関数へのポインタでなければなりません:

    'mode_name' => \&mode_function

参照される関数は、最終的にWebブラウザに送信されるテキストを返すことが期待されます。

RUN MODE関数についての重要な注意

アプリケーションはSTDOUTへのprint()をしてはいけません。STDOUTへ出力を送るためにprint()することは(HTTPヘッダを含めて)、独占的に継承されたrun()メソッドの領域です。このルールを破ることは、よくあるエラーの原因です。プログラムが間違ってHTTPヘッダの前に内容を送信するのであれば、たぶんこのルールを破っているのでしょう。

start_mode()
    $webapp->start_mode('mode1');

start_modeはrun_mode()テーブルで指定されるモードの名前を持ちます。デフォルト・モードは"start"です。ここで指定されるモード・キーは、mode_param()によって指定されるCGIフォーム・パラメータの値が定義されていないときに使われます。一般的に、これはアプリーケーションが実行される最初のときです。

tmpl_path()
    $webapp->tmpl_path('/path/to/some/templates/');

このアクセサ/ミューテータ・メソッドはテンプレートが格納されているディレクトリへのファイル・パスです。これはテンプレート・ファイルを見つけるためにload_tmpl()により使われます。

tmpl_path()がオペテーティング・システムのディレクトリ・デリミタ(UNIXなら'/'、Windowsなら'\'、Macintoshなら':'等)で終っていることを確認することは重要です。load_tmpl()メソッドはさまざまなOSに特有なことを判定しようとはしません− 単にtmpl_path()をload_tmpl()渡されたファイル名に付けるだけです。


参考資料

CGI, HTML::Template, perl(1)


作者(AUTHOR)

Jesse Erlbaum <jesse@vm.com>

サポート・メーリング・リスト

疑問、コメント、バグ報告または機能の提案がありましたら、サポート・メーリング・リストに送ってください!そのメーリング・リストに参加するには、空のメッセージを"cgiapp-subscribe@lists.vm.com"に送るだけです。


クレジット CREDITS

(原文のまま)

Thanks go to my place of work, Vanguard Media (http://www.vm.com), for funding the development of this library, and encouraging me to release it to the world. If you need a web-application for your business, do check us out!

Many thanks to Sam Tregar (author of the most excellent HTML::Template module!) for his innumerable contributions to this module over the past year, and most of all for getting me off my ass to finally get this thing up on CPAN!


ライセンス(LICENSE)

(原文のまま)

Copyright (c) 2000, Jesse Erlbaum <jesse@vm.com> and Vanguard Media (http://www.vm.com). All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


ホーム Perlの小技

ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。