レベル: 中級 Teodor Zlatanov (tzz@bu.edu), Programmer, Gold Software Systems
2000年 10月 01日 ファイル・ベースの構成は、手作業の構築方式を使用したのではすぐに行き詰まってしまいます。Teodor Zlatanovは、AppConfigモジュールを使用することによって、Perlプログラム用のローカル構成記憶域をどのように扱うことができるのか、また、そのような構成をデータベースに保管して、ネットワーク上の任意のマシンからそのデータベースにどのようにアクセスすることができるのかを示します。
小さなディレクトリー・リスナーからWebブラウザーまで、プログラムにとってまず最初の要件の1つとして、構成可能であることが挙げられます。ファイル・ベースの構成可能性とコマンド行オプションの組み合わせは、構成可能性のニーズを満たす、長寿命で柔軟なソリューションとして定評があります。Perlプログラムは、構成ファイルとコマンド行の構文解析ルーチンを含む傾向がありますが、一般にこの方法を利用します。
この記事で使用するコマンド行構文解析は、やや複雑になります。したがって、単純な引数よりも高位 のオーダーを構文解析する場合には、混乱を避けるためにParse::RecDescent (またはそれと同等の構文解析モジュール) を使用することをお勧めします。複雑なコマンド行構文解析の説明については、英語を話すPerlプログラムに関する私の前回の記事を参照してください。
最初に、Perl 5.005またはそれ以降およびCPAN AppConfigモジュールがユーザーのシステムにインストールされていることを確認してください。また、特定のデータベースのためにPersistent::MySQLまたは適切なPersistentクラスも必要になります。これらもCPANにあります (この記事の後の部分の参考文献を参照)。
単純な方法: DIY
理論上は (そして適切なツールがあれば) 誰でも構成パーサーを構築することができます。その一例として、Perl Cookbook で紹介されている迅速な実装を使用すると、うまく始めることができます。この種の実装で開始した場合、構成ファイル・パーサーを書くのはどれほど厄介なことでしょう?
実際、この手のプロジェクトでは次のような複雑な問題がさらにいくつか持ち上がるため、かなり骨が折れます。
- 構成ファイル内のブランク行およびコメント
- 誤った行 (つづりが誤っているキーワードなど)、および重大な誤りと無視できる誤りの判別
- 多種多様のデータ構造 (ブール、スカラー、配列、ハッシュなど) が必要になる可能性が高いため、独自のパーサーを書かなければならない可能性
- 複数の構成ファイル
- 変数のデフォルト
- コマンド行オプションとファイル構成の統合、およびそれらによる相互作用の制御
- 別のDIY構成ファイル形式を使用しているユーザーの教育
(通常は次のように行われます。「'=' だけの行がない限り、これで大丈夫だ。コメントは '#' で始まっているな。だけど、これだけで1行にしないとまずいぞ。キーワードは大文字にして、値は小文字にすることを忘れないように。戻ってこいよ。まだ行くんじゃない! 必須キーワードの話をするのを忘れていたよ!」)
- モジュールを再利用する代わりとしての、バグが疑われる構成コードの書き直しまたはコピー
- 通常のDIYによる無計画なキーワードのハッシュの代わりに、一貫性のあるインターフェースを備えたオブジェクトに構成をすること
腰が引けてしまいましたか? そのためにAppConfigがあるのです。これを使用すると、こうした問題をすべて扱うことができます。DIYを使用すべきでないことは、ほぼ断言できます。
困ったときのAppConfig
Andy WardleyのAppConfig CPANモジュールは、上記のすべての問題の解決に役立ちますが、万能薬ではありません。プログラムをたちどころに改善するわけではありません。AppConfigを使用するためには書き直しが必要になることもあります。(また、わずかですが学習曲線もあります。これについては、この記事でさらに軽減させるように試みます。)
DIYとAppConfigのどちらを使用してよいのか、はっきり決められない場合には、ユーザー自身の経験と、書こうとしているプログラムに基づいて決定する必要があります。しかし、DIY手法と同程度あるいはそれ以上のことがAppConfigによってできないような状況は、きわめて少ないと考えられます。
以下で、AppConfigを使用して行えることを (前のセクションで挙げた問題に従って)、1つずつ説明します。
- 構成ファイル内のブランク行およびコメント:
AppConfigはブランク行とコメントを認識したうえで、それらを無視します。
- 誤った行 (キーワードのつづりの誤りなど) における、重大な誤りと無視できる誤りの判別 :
AppConfigの感度を設定して、正しくない設定値を無視したり、プログラムを打ち切ったりすることができます。キーワードで代替つづりが許される場合には別名を使用することができます (国際的な設定値など)。
- 別のデータ構造 (ブール、スカラー、配列) およびハッシュが必要なために、独自のパーサーを作成する:
AppConfigはこれらすべてのデータ構造を処理することができますが、ネストを使用しないで処理します。ネストされたハッシュまたは配列が必要な場合には、ユーザー自身で行うか、あるいはAppConfigを支援する必要があります。
- 複数の構成ファイル:
AppConfigは、必要に応じていくつでも構成ファイルを処理することができ、それらの間で設定値をロードし合います。AppConfigによる配列およびハッシュのリセットを支援して、スタックの最下部に挿入された値が先頭に現れないようにすることもできます。
- 変数のデフォルト:
AppConfigは変数のデフォルトを提供します。"-variable" 構文を使用すると、構成ファイル内の変数がデフォルト状態にリセットされます。
- ファイル構成によるコマンド行オプションの制御と統合:
AppConfigは、Getopt::StdとGetopt::Longの両方のコマンド行オプション構文解析を行います。構文解析は、構成ファイルを読み取る前に行うことも、読み取った後で行うこともできます。
- 別のDIY構成ファイル形式を使用しているユーザーの教育:
AppConfigは、標準的で柔軟な形式を使用します。"KEYWORD value" も "KEYWORD=value" も、スカラーとして受け入れられます。配列は合計されるため、"ARR=1" の後に "ARR=2" を続けると要素が1と2の配列ARRとなります。また、ブール・オプションは、"bool"、"nobool"、"!bool"、"bool on"、"bool off"、"bool yes" (ただし、"bool no" を使用すると不適切な操作が行われます)、"bool=1"、"bool=0" の形式で指定することができます。(悪名高い記号論理の考案者George Boole博士 --参考文献 を参照 -- は、きっと安心することでしょう。) ハッシュ・オプションは "KEYWORD PARAMETER = value" の形式で指定します。このvalueは、PARAMETERというキーが割り当てられたハッシュ項目になります。
- モジュールを再利用する代わりとしての、バグが疑われる構成コードの書き直しまたはコピー:
AppConfigはこの点が非常に安定していて、モジュールへのインターフェースは変更されにくくなっています。また、すでに数千人ものプログラマーによってテスト済みですので、使わない手はありません。
- 通常のDIYによる無計画なハッシュの代わりに、構成を、一貫性のあるインターフェースを備えたオブジェクトにすること:
一貫性のあるAPIが、メイン・プログラムから構成ハンドラーを抽出し、ハンドラーとのインターフェース (私たちの例ではAppConfig) を単純化します。この方法では、データ構造を直接操作する代わりにメソッドだけを使用するため、バグの発生件数も少なくて済みます。
以上のように、AppConfigの使用を選択すべき理由はたくさんありますので、完全な注釈の付いたAppConfigの使用例を見てみましょう。ここでは、より進んだ機能は省略することにします (これらについては、次のセクションで検討します)。変数は、スカラー、ブール、および配列の場合にはコマンド行から "-varname value" と入力することにより、またハッシュの場合には "-varname key=value" と入力することにより、設定できます。ここで使用する構成ファイルはconfig.pl であり、次にその例を示します。
# blank lines are ignored
# set a boolean
debug
# set a scalar
name=E.T.
# set an array
hosts = dbhost
hosts = backuphost
# reset the hosts array
-hosts
# add new values to hosts
hosts = firewall
hosts = farewell
# set a hash
phone joe = 222-333-4444
phone marge = 555-666-7777 |
AppConfigの進んだ使用法
AppConfigは、EXPANDの設定値に応じて、いくつかのレベルで変数拡張を行うことができます。詳細については、AppConfigのドキュメントを参照してください。
# expand all variables, globally
my $config = AppConfig->new({ GLOBAL => { EXPAND => EXPAND_ALL } });
# expand just HOME_DIR as UID, so "~username" will work as in the shell
$config->define('HOME_DIR => { ARGCOUNT => ARGCOUNT_ONE, EXPAND => EXPAND_UID });
|
INIスタイル・セクションも、便利なAppConfig機能の1つです。構成ファイルの行で [セクション] を単独で使用すると、そのファイルの終わりまで、または次のセクションが登場するまでの間に使用されるすべてのキーワードを、そのセクション名および下線 '_' 付きであらかじめ示しておくことができます。たとえば、
[file]
location = /tmp
type = txt
name = accounts.txt
[database]
host = wyrm
user = slayer
password = amethyst
|
は、下記の表現と同じです。
file_location = /tmp
file_type = txt
file_name = accounts.txt
database_host = wyrm
database_user = slayer
database_password = amethyst |
AppConfig構成オブジェクトはvarlist() 関数で検査することができます。下記のコードは、AppConfigオブジェクト内の各変数の内容を出力するためのものです。このvarlist() 関数は、正規表現を使用しなければならないため、扱いに注意を必要とする場合があります (空ストリングは無効です)。
use Data::Dumper; # for hash and array references
my %varlist = $config->varlist('.*');
foreach my $varname (keys %varlist)
{
print "Variable name $varname, value = ", Dumper $config->get($varname), "\n";
} |
AppConfigには、Getopt::Longモジュールの能力をフルに利用できるようにする、Getopt::Longインターフェースがあります。下記のコードは、Getopt::Long用の変数パラメーターを定義しています。このGetopt::Longは、コマンド行から入力されたパラメーターを構文解析するために呼び出されるものです。無効な値が入力されると、エラーが発生します。
$config->define("help|h|!"); # define a boolean
$config->define("code|c|=i"); # define a scalar integer
$config->define("list|l|=f@"); # define a array of floating point values only
$config->define("uids|u|=f%"); # define a hash of floating point values only
$config->getopt(); # instead of args(), to use the Getopt::Long options |
AppConfigでは、変数の妥当性検査も行うことができます。つまり、変数は、正規表現 (あるいはコードの一部) を参照することにより、その変数自体に不当な値または単に無意味な値が設定されるのを拒否することがあります。
# the username validation succeeds only when it is exactly "joe"
# the password validation succeeds when it contains "joe" or "joE"
$config->define(
'USERNAME' => { ARGCOUNT => ARGCOUNT_ONE,
VALIDATE => sub # subroutine validation
{
my $varname = shift @_;
my $value = shift @_;
print "$varname = $value\n";
return ($value eq "joe");
}
},
'PASSWORD' => { ARGCOUNT => ARGCOUNT_ONE,
VALIDATE => "jo[Ee]" # regex validation
}
); |
AppConfigは、アクションの自動的な起動を可能にして、ある変数の値が変更されるたびにそのアクションが実行されるようにすることができます。AppConfigへの参照はサブルーチンに渡されるため、たった1つの変更によって他のいくつかの変数の変更を起動することができます。
$config->define(
'USERNAME' => { ARGCOUNT => ARGCOUNT_ONE,
ACTION => sub # autoaction
{
my $config = shift @_;
my $varname = shift @_;
my $value = shift @_;
print "$varname = $value\n";
}
}
);
|
AppConfigの制約事項
AppConfigは、変数内に組み込まれたコードを処理しません。私の意見では、構成ファイルにはコードが収まるのにふさわしい場所がなく、ユーザーに任意のコードを実行させることはお勧めできません。ただし、AppConfigは変数の自動計算を行いませんが、変数に関連した妥当性検査および自動アクション・サブルーチンを調整して、このタスクを行うことができると思われます。どうしても必要な場合には、問題の変数を選択し、(下記の方法により) 読者自身の手でそれらの変数に対してeval() を実行してください。言うまでもありませんが、これは、ユーザーにプログラムをそのレベルで制御させたいと思わない限りは、お薦めしません。
foreach my $varname ('username', 'password')
{
$config->set($varname, eval $config->get($varname));
}
|
AppConfigのINIスタイル・セクションはどこにもありません。このセクションはコード・セクションを定義しますが、コード・セクションはあらかじめ知られていなければ役に立ちません。親オブジェクトの内部でネストされた新規AppConfigオブジェクトを作成するように、セクションを設計しておいたほうが良かったと思われますが、これはささいな問題です。
単純なテストでは、ロードおよび実行の速度はAppConfigの使用による影響を受けていないようです。これはかなり小さなモジュールであるため、サイズや速度への悪影響は、通常は無視することができます。もちろん、プログラムにとってタイミングが重要である場合には、AppConfigを使用した場合と使用しない場合とで時間を計測し、その不利を補うだけの価値がこのモジュールにあるかどうかを、読者自身で判断してください。
主として良好なAPIのおかげで、他の手段を使用した場合に比べ、AppConfigの複雑度ははるかに低く、また学習曲線はなだらかになっています。特に新人プログラマーにとっては、分かりにくい部分もありますが、概して、これまでPerlの経験を積んでいる人にとっては、たいした問題ではありません。
AppConfigの構文解析の制約事項は、使いやすさを優先した結果です。コマンド行オプションについて高機能の構文解析を行う必要がある場合には、英語を話すPerlプログラムに関する私の前回の記事を参照してください。(AppConfigがコンテキストに依存した構文解析を行えないことに関し詳述しています。)
AppConfigおよびPersistent::DBIによるデータベースへの構成のアップロード
このセクションに取り組む前に、Persistentモジュールによるデータの保管に関する私の記事を読むことをお勧めします。また、Perlの参照およびSQLデータベースについてある程度理解していることも必要です。私の特定のコード例では、MySQLデータベースおよびそれに対応するPersistentモジュールを使用します。別のデータベース (たとえば、PostgresまたはOracle) を使用する場合には、別のPersistentモジュールを探す必要があります。
データベース・コンテキストで構成を使用できるようにするためには、データベース・スキーマがすでに設計されていなければなりません。つまり、データを保管および復元するコードを書き始める前に、保管したいデータを決める必要があります。この例では、ブール、スカラー、配列、およびハッシュを別個のテーブルに保管します。
これは必ずしも最善の方法ではありません。1つのテーブルをすべてのデータ・タイプに使用することも、目的別にテーブルを分けることもできます。私の例は、永続的な構成を実装するための多くの方法の1つにすぎません。これだけが唯一の方法でないことは確かです。
ここで示すスキーマは、おそらく、ほとんどの目的に十分使用できると思われます。値とキーの長さがある程度制限されていますが、これらは、コードの側で簡単に対応できます。ただし、配列およびハッシュの要素IDの作成方法が原因で問題が発生する可能性はあります。このようなケースでの完全な解決策は存在せず、問題解決のためにさまざまな解決策があるだけです。任意の構造のデータをリレーショナル・データベースに保管する際には、常に慎重を要します。
AppConfig::Stateでは_argcount() メソッドを使用する予定です。このメソッドの詳細については、AppConfig::Stateのマニュアル・ページを参照してください。ごく簡単に言うと、このメソッドは、変数の名前が分かっている場合に、私たちが扱っている変数の種類を教えてくれます。
MySQLデータベースおよびそれに対応するPersistentモジュールを使用している、私のコード例を次に示します:persistent-config.pl
結論
わずかな作業を行うことにより、AppConfigおよびPersistentクラスを上手に併用することができます。前のセクションで示した永続構成スクリプト(persistent-config.pl)は、短いキーおよび変数名を使用したほとんどの構成を処理することができますが、まだ改善の余地があります。このスクリプトを、ユーザーの好みに合わせて改良すると、ネットワークのどこからでも このスクリプトを開始して、中央ホストから現行構成をロードさせることができます。少なくとも、改良を行うことにより、読者はデータベース構成を知ることができ、アプリケーションをネットワーク中心の視点から見ることができるようになります。
一般に、コードの再利用はモジュールの最大の利点の1つですが、AppConfigでは特にそれが顕著です。DIY方式でバグや遅延が発生するような場合、AppConfigは有効な解決策であり、ほとんどの構成の要求を満たすことが期待できます。
「AppConfigの制約事項」で示した制約事項は、きわめてまれです。もちろん、AppConfigを使用する前に、自分のプロジェクトにとって何が最適であるのかを決める必要があります。ここで示した情報が特定のプロジェクトにどの程度当てはまるのかを判断して、AppConfigのマニュアル・ページを調べることが大切です。
AppConfigからは必要なものだけを得るようにしてください。何でもやらせようとは考えないことです。プログラムの半分を構成ファイルに入れてみたいと思うかもしれませんが、そのようなことをすると、ユーザーからの苦情が殺到するでしょう。構成ファイルは論理的でしかも単純なものにしてください。AppConfigが周到に用意しているコマンド行オプションを含め、プログラムによって受け入れられる構成構文の詳しい説明を書いてください。
参考文献
- developerWorks にあるTeodoreの記事洗練されたPerl: 平易な英語による Perl プログラムの作成 をご覧ください。
- developerWorks にあるTeodoreの洗練されたPerl: Persistentモジュールによるデータの保管 に関する記事をご覧ください。
- 必要なすべてのPerlモジュールについては、CPAN.org をご覧ください。
- Perl情報および関連資料については、Perl.com をご覧ください。
- Andy WardleyによるAppConfigモジュールは下記サイトから入手してください。
- そのほかの構成管理CPANモジュールとして、次のものがあります。
-
Programming Perl Third Edition、Larry Wall、Tom Christiansen、およびJon Orwant著 (O'Reilly & Associates, 2000) は、Perlに関する現在最高のガイドで、最新版は5.005および5.6.0に合わせて更新されたものです。
-
Perl Cookbook、Tom ChristiansenおよびNathan Torkington著 (O'Reilly & Associates, 1998) は、すべてのPerlの問題に関するハウツー本の決定版です。5.6.0がリリースされた今では、内容がやや古くなっていますが、それでも買うだけの価値はあります。DIY手法による構成がレシピ8.16に示されています。
著者について  | 
|  | Teodor Zlatanov 氏は、1999 年にボストン大学を卒業し、コンピューター・エンジニアリングの分野で理学修士号を取得しました。1992 年以来、Perl、Java、C、および C++ を使用して、プログラマーとして働いています。興味の対象は、オープン・ソースのテキスト分析処理、3 層クライアント・サーバー・データベース・アーキテクチャー、UNIX システム管理、CORBA、およびプロジェクト管理です。メール・アドレスは
tzz@bu.edu です。 |
記事の評価
|