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

developerWorks Japan  >  Linux  >

洗練されたPerl: Perlによるアプリケーションの設定 第2回

CPAN AppConfigモジュールの高度な使い方

developerWorks
ページオプション

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

原文はこちら

原文はこちら


レベル: 初級

Teodor Zlatanov (tzz-at-iglou.com), Programmer, Gold Software Systems

2002年 7月 01日

ファイルを基盤とする設定は、手作業で行おうとしたのではすぐに行き詰まってしまいます。本稿では、TedがAppConfigモジュールによるローカル構成記憶域の操作方法を紹介してくれます。妥当性のチェック、自動処理、ハッシュ変数や配列変数の変更、および「ラジオ・ボタン」スタイルの相互に排他的なオプションといったAppConfigモジュールの高度な機能を解説してくれます。

本稿では、CPAN AppConfigモジュールを使った、Perlプログラムの、ファイルを基盤とした高度な設定手法を紹介したいと思います。AppConfigの初歩的なこと、および構成管理モジュールを使う理由については、私が「洗練されたPerl」の連載の中でこのテーマをとりあげたときの記事を参照してください。

今回の第2回目の記事に示したコードには、第1回目の記事のコードを流用している部分があり、その詳細については説明していません。今回扱うテーマを理解するには、第1回目の記事を読んでおく必要があります。

読み始めるにあたっては、まず、Perl 5.005以上およびCPAN AppConfigモジュールをシステムにインストールしておいてください (必要なら、本稿末の参考文献に掲げたこれらのパッケージへのリンクをご利用ください)。

妥当性のチェック

開発のあわただしさの中でしばしば忘れ去られがちですが、後でプログラムがこけたり、死んだりしないよう、プログラムにとってのワクチン接種の意味で、先にデータの妥当性をチェックしておく必要があります。ユーザーは、自分の用意した入力をプログラムが処理してくれるものと考えますので、みなさんは、予期されないことが起こるものと考えたほうがよいでしょう。

幸い、みなさんは、AppConfigのデータ妥当性チェック・ルーチンを利用できます。不適当なデータが検出された場合に、好ましくないデータを弾 (はじ) くか、プログラム全体をアボートするかは、AppConfigを初期化する際に指定するPEDANTICの設定値で選択することができます。


リスト1. 構成値の妥当性のチェック
                
# SSN: must be a string with exactly 9 digits
# LIMITED: must be a string key existing in the hash %limited_hash
$config->define(
# more complex subroutine validation
                'SSN'      => { ARGCOUNT => ARGCOUNT_ONE,
				VALIDATE => sub	
				{
				 my $varname = shift @_;
				 my $value = shift @_;
				 # only succeed with 9 digits in $value
				 return (9 == ($value =~ tr/0-9//));
				}
			      },
# limited values, must be 'alpha' or 'beta'
                'LIMITED'  => { ARGCOUNT => ARGCOUNT_ONE,
				VALIDATE => sub 
				{
				 my $varname = shift @_;
				 my $value = shift @_;
				 my %limited_hash = ( alpha => 1, beta => 2 );
				 return exists $limited_hash{$value};
				}
			      },
               );

VALIDATEサブルーチンは、ACTIONサブルーチン (次のセクションで説明) よりも、ずっと機能が制限されていることに注意してください。AppConfig::StateインスタンスはVALIDATEサブルーチンには渡されませんので、このような構成にすると、簡単には他の変数にアクセスできません。これは、たぶん、意図的に行ったことでしょうから、大胆なプログラマーも、AppConfig::Stateを不確定な状態に置くことはありません。




上に戻る


自動処理

AppConfigモジュールでは、変数に値がセットされたときに関数を起動するといったことを簡単に行うことができます。ACTIONサブルーチンは、値がセットされた後に呼び出されます。これに対して、VALIDATEサブルーチンは、値がセットされる前に起動されます。現在の変数に値をセットするためにACTIONを使ってはなりません。注意しないと、無限ループを引き起こしてしまうことになります。たとえば、以下のコードを実行してみてください。


リスト2. 無限の自動処理ループ -- こんなことを行ってはいけない
                
$config->define(
                'TRIGGER' => { ARGCOUNT => ARGCOUNT_ONE,
                                ACTION => sub # autoaction
                                          {
                                           my $config = shift @_;
                                           my $varname = shift @_;
                                           my $value = shift @_;
                                           print "$varname = $value\n";
                                           $value++;
                                           $config->TRIGGER($value);
                                          }
                              }
               );
                    

リスト2のコードは、ずっと走り続けることになります。外部変数を使って必要な箇所に安全装置を設けることもできますが、そんなことをすればすぐに混乱を招くことになります。やるなら、データを全部読み込んだ後に、データの後処理を行うようにします。

自動処理については、以下のラジオ・ボタン相互依存関係にあるオプションのセクションでもさらに研究したいと思います。そこでは、変数の値によって、他の変数に対する処理を開始する方法を扱います。また、相互に排他的な変数については、変数の値を、それ自身のトリガーの中から安全にセットする方法を紹介したいと思います。




上に戻る


AppConfigのハッシュ変数、配列変数の変更

AppConfigオブジェクト中のスカラー値の変更方法は、いたって簡単です。$config->VARIABLE(value) を呼び出せばよいだけのことです。

しかし、配列とハッシュになると、話は複雑になってきます。変数の値を調べようとすると、AppConfigオブジェクトは、配列またはハッシュの参照を渡してきます。変更しなければならないのは、オブジェクト中のデータであり、渡されてきたデータではありません。要素1個だけを削除することはできません。要素全部を削除した後、必要な要素を挿入し直す必要があります。

配列を変更するには、以下のようにします。


リスト3. 配列の変更
                
$config->define('HOSTS' => { ARGCOUNT => ARGCOUNT_LIST });
# add a value to the array named HOSTS
$config->HOSTS("jove");
# list the values in the HOSTS array
print "$_\n" foreach @{$config->HOSTS};
# reset the array to 0 entries
$config->_default("HOSTS");

配列の値に直接アクセスするためのAPIはありません。配列の値にアクセスするには、内部的なAppConfig::Stateデータにアクセスするか、配列変数の値として返されてきた配列参照にアクセスするかですが、そういうやり方はお薦めできません。

ハッシュのエントリーも、配列と同様、セットするのは簡単ですが、削除はやっかいです。また、ハッシュのエントリーは、値にスカラー文字列かundefしかセットできません。


リスト4. ハッシュの変更
                
$config->define('PHONE' => { ARGCOUNT => ARGCOUNT_HASH });
# add values to the hash named PHONE
$config->PHONE("jimmy=1-800-453-2211");
# unusual cases
$config->PHONE("equals=a=b"); # the value will be "a=b"
$config->PHONE("harry=");     # the value will be a string of length 0 ('')
$config->PHONE("harry");      # the value will be undef
# list the sorted keys and their values in the PHONE hash
print "$_ => ", $config->PHONE()->{$_}, "\n" foreach sort keys %{$config->PHONE};
# you can also do it with Data::Dumper
print Dumper $config->PHONE;
# reset the hash to 0 entries
$config->_default("PHONE");
                    

配列と同様、ハッシュ値に直接アクセスするためのAPIはありません。ハッシュの値にアクセスするには、内部的なAppConfig::Stateデータにアクセスするか、ハッシュ変数の値として返されてきたハッシュの参照にアクセスするかですが、そういうやり方はお薦めできません。




上に戻る


ラジオ・ボタン

ラジオ・ボタンは、GUIユーザーなら誰でも知っているものです。いくつかの相互に排他的なオプションを定義するものです。たとえば、ファイルの種類は、「実行可能」か「ディレクトリー」か「特殊」(その他のすべてのもの) などと分類できます。これを処理するときには、数値を使用し、たとえば、実行可能は1、ディレクトリーは2、特殊は3などとして扱うことができます。これに対して、ラジオ・ボタンの場合、これら3つのオプションのいずれかがセットされると、他の2つは自動的に0にセットされます。

実装には、オプションの2進数の状態 (オンかオフ) が使用されます。オプションがオフにされた場合、とくに何も行う必要はありません。オプションがオンにされた場合、他の2つのオプションは0にされます。

3つのオプションを全部0にするようにすることも可能です。これは、不定状態として扱うことができます (したがって、状態は、実行可能、ディレクトリー、特殊、および不定の4つがありうることになります)。


リスト5. ラジオ・ボタン
                
my $config = AppConfig->new();
$config->define(
     # mutually exclusive options
    'FILE'       => { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ACTION =>\&toggle_type },
    'DIRECTORY'  => { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ACTION =>\&toggle_type },
    'SPECIAL'    => { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 1, ACTION =>\&toggle_type },
              );
# {{{ toggle_type: do the right thing when file type is specified (3 mutually exclusive options)
sub toggle_type
{
 my $self    = shift @_;
 my $varname = shift @_;
 my $value   = shift @_;
 return unless $value;                  # do nothing if we're being turned off
 # turn off all options except the one that invoked this action
 $self->set($_, 0) foreach grep !/^$varname$/i, qw/FILE DIRECTORY SPECIAL/;
}

このコードで重要なことは、これが、AppConfigの他のすべての機能と並行して使用でき、また、新しいモジュールを必要としない、ということです。みなさんのコードにそのまま組み込むことができます。AppConfig::State内部のものにアクセスする (したがって、たとえばset() を呼び出す必要がない) のであれば、別のやり方で実装することもできたでしょうが、相互に排他的な3つの設定値を扱うこの例では、そうする必要もありませんでした。




上に戻る


相互依存関係にあるオプション

以下は、オプションがもっと複雑な場合に、AppConfig::State内部のものを変更するコードです。重要な点は、AppConfig::Stateの内部で変数の値を直接変更するやり方です。すなわち、通常、やってはならないことです。

この例は、TIMESハッシュのエントリーが2個以上になると、自動的にWINNER変数、LOSER変数をセットするというものです。これは、実際に動かすことのできる完全なコードです。

また、変数間の相互作用がどの程度複雑になりうるかに注意してください。WINNERとLOSERを安全にセットできるのは、どのタイミングでしょうか。WINNERとLOSERを直接セットする場合、TIMESハッシュを使用すべきなのでしょうか、それともAppConfig::Stateの内部で直接値を変更すべきなのでしょうか。これは、変数を3つしか使わない場合です。小さなプログラムでも、変数間に相互依存関係をもたせたい場合には、これがどれだけ複雑なものになるか想像してみてください。オプションがたくさんあって、それが他のオプションや動作を起動する場合には、有限ステート・マシンにすることを検討したほうがよいでしょう。そのような複雑な環境には、FSM (有限ステート・マシン) が最も適しています。


リスト6. 相互依存関係にあるオプション
                
#!/usr/bin/perl -w
use strict;
use AppConfig qw/:argcount/;
my $config = AppConfig->new();
$config->define(
                'TIMES'    => { ARGCOUNT => ARGCOUNT_HASH, ACTION =>\&toggle_option },
                'WINNER'   => { ARGCOUNT => ARGCOUNT_ONE,  ACTION =>\&cheat_action  },
                'LOSER'    => { ARGCOUNT => ARGCOUNT_ONE,  ACTION =>\&cheat_action  },
                'CHEATERS' => { ARGCOUNT => ARGCOUNT_NONE },
              );
$config->TIMES("Johnny Boy = .444");
$config->TIMES("Johnny Appleseed = 2.45");
$config->TIMES("Johnny Be Good = 8.002");
print "Winner: ", $config->WINNER, "\n";
print "Loser: ", $config->LOSER, "\n";
$config->WINNER("Johnny Be Good");
print "Winner: ", $config->WINNER, "\n";
print "Loser: ", $config->LOSER, "\n";
$config->LOSER("Johnny Be Good");
print "Winner: ", $config->WINNER, "\n";
print "Loser: ", $config->LOSER, "\n";
# {{{ toggle_option: toggle interdependent options 
sub toggle_option
{
 my $self    = shift @_;
 my $varname = shift @_;
 my $value   = shift @_; my @sorted =  sort { $self->TIMES()->{$a} <=> $self->TIMES()->{$b} } keys %{$self->TIMES};
 if (scalar @sorted > 1)                # we need more than 1 member to have a winner and a loser
 {
  # we have to set winner and loser directly to avoid the cheat_action
  $self->{VARIABLE}->{winner} = $sorted[0];
  $self->{VARIABLE}->{loser} = $sorted[-1];
 }
}
# }}}
# {{{ cheat_action: set TIMES hash value for a given key so it's always the winner or the loser
sub cheat_action
{
 my $self    = shift @_;
 my $varname = shift @_;
 my $value   = shift @_; my $time;
 $varname = uc $varname;                # make sure we match the variable name
 if ($varname eq 'WINNER')
 {
  # set the time to 0 unconditionally
  $time = 0;
 }
 elsif ($varname eq 'LOSER')
 {
  # set the time to the sum of all the times (it will always be the worst time, then)
  $time += $_ foreach values %{$config->TIMES()};
 }
 # trigger a resorting by inserting the newly wanted time
 $self->TIMES("$value=$time");
}
# }}}




上に戻る


さらにいろいろと試してみる

今回紹介した高度な技法は、みなさん自身のプログラムの設定手法を改善するのに役立つことと思います。恐れずに、みなさん自身でAppConfigの拡張を試してみてください。



参考文献



著者について

Teodor Zlatanov

Teodor Zlatanov氏は、1999年、ボストン大学でコンピューター・エンジニアリング理学修士号を取得しました。1992年以来、プログラマーとしての業務に従事しており、使用してきた言語は、Perl、Java、C、C++です。テキスト構文解析におけるオープン・ソースによる作業、3層のクライアント/サーバー・データベース・アーキテクチャー、UNIXのシステム管理、CORBA、プロジェクト管理に関心を持っています。彼のメール・アドレスはtzz@iglou.com です。




記事の評価


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



 


 


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


この記事を共有する

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について プライバシー お問い合わせ