OBAQシェルとは「オブジェクト指向に基づく汎用の構成定義ファイルパーサ」です。
例えば、Apacheの構成定義ファイル(httpd.conf)を見てみましょう。
# サーバ全体に対する指定
ServerType standalone
ServerRoot /etc/httpd
LockFile /var/lock/httpd.lock
PidFile /var/run/httpd.pid
# ディレクトリに対する指定
<Directory /home/httpd/icons>
Options Indexes MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
# VirtualHostに対する指定
<VirtualHost ip.address.of.host.some_domain.com>
ServerAdmin webmaster@host.some_domain.com
DocumentRoot /www/docs/host.some_domain.com
ServerName host.some_domain.com
ErrorLog logs/host.some_domain.com-error_log
CustomLog logs/host.some_domain.com-access_log common
<Directory /home/httpd/icons>
....
</Directory>
</VirtualHost>
一般にapacheのように複雑なソフトでは、このように構成定義ファイルも複雑になります。 OBAQシェルは、このような複雑な構造を持つ構成定義ファイルを読んで、 プログラムの初期設定を行なう処理を記述するためのフレームワークです。
OBAQシェルでは「構成定義=モデルオブジェクトの生成」という考え方で、 次のようなプログラミングモデルを想定しています。
つまりOBAQシェルは、外界(処理対象)の「モノ」に対応する(シミュレーションする)オブジェクトの生成を行なうライブラリです。 これを利用することで、以下のようなメリットがあります。
もし、上記のhttpd.confをOBAQで記述するとしたら、以下のようになります。
# サーバ全体に対する指定
Server
type=standalone
root=/etc/httpd
lockfile=/var/lock/httpd.lock
pidFile=/var/run/httpd.pid
# ディレクトリに対する指定
Directory /home/httpd/icons
.set_ptions Indexes MultiViews
allowOverride=false
.set_order allow,deny
.allow from all
# VirtualHostに対する指定
VirtualHost ip.address.of.host.some_domain.com
admin=webmaster@host.some_domain.com
root=/www/docs/host.some_domain.com
serverName=host.some_domain.com
errorLog=logs/host.some_domain.com-error_log
customLog=logs/host.some_domain.com-access_log common
Directory /home/httpd/icons
....
このテキストファイルをOBAQファイルに渡すだけで、 ServerとかDirctoryとかVirtualHostというオブジェクトが生成され、 それぞれ必要な属性が設定され、各オブジェクト間の関係が設定されます。
もちろん、Server, Directory, VirtualHost というクラスとそれが実際に動作するためのメソッドは記述する必要がありますが、 上記の構成ファイルからオブジェクトモデルを生成する処理はOBAQシェルが行ないます。
これによって、プログラマーは実際に処理に専念でき、ユーザは個別のフォーマットを学習する必要がなくなります。 また、両者にとって最も少い負担で機能を拡張することができます。
| 種類 | 判別方法 | 処理内容 | 例 |
|---|---|---|---|
| オブジェクト生成行 | 先頭文字が大文字 | 指定したクラスのオブジェクトを生成する | Folder xxxx |
| オプション設定行 | 先頭文字が小文字 | 直前のオブジェクトに対するオプション設定 | recuresive=true |
| メソッド行 | 先頭文字が.(ピリオド) | 直前のオブジェクトに対するメソッド呼び出し | .add_subfolder yyy |
| 内部コマンド行 | 先頭文字が% | OBAQシェルに対する処理指示 | |
| コメント行 | 先頭文字が#l | コメント |
Folder # オブジェクト生成行
title="Walrus sample(navigation_bar)" # オプション設定
target_dir=target # オプション設定
.def_acq header_title ' parent.title.to_s + "--" + title.to_s '
# ↑メソッド呼びだし行
HtmlDocument index.html # オブジェクト生成行
prettyprint # オプション設定
role=index # オプション設定
HtmlDocument java.html # オブジェクト生成行
title=javaについて
HtmlDocument perl.html # オブジェクト生成行
title=perlの特徴
OBAQフォーマットでは、単にクラス名を書くとそのクラスのオブジェクトを生成します。 生成時に渡すパラメータは、クラス名の後に空白を置いて記述します。 生成時のパラメータの種類や指定方法は各クラスの仕様を参照してください。
オブジェクト生成行に--があると、その後はオプション設定行と見なします。
HtmlDocument java.html title=javaについて
上と下は同じ意味になります。
HtmlDocument java.html -- title=javaについて
クラス名の前にある空白の数(インデント)によってオジェクト間の親子関係が決定します。
Folder aaa
HtmlDocument xxx.html
Folder bbb
HtmlDocument yyy.html
HtmlDocument zzz.html
この例では、xxx.htmlはaaaの子供、 yyy.htmlとzzz.htmlはbbbの子供になります。
オプション設定行は、○○=××という形式で記述して○○というオプションに××という値を設定します。
○○としてどのようなオプションが許されるのかは、各クラスの仕様を参照してください。
=の前後にはスペースがあってもいいしなくてもかまいません。××は"や'で挟むことも可能です。
イコールと値が省略されていた場合は,○○ == trueが省略さてているものと します。
メソッド呼び出し行には、メソッド名と(必要ならば)引数を記述します。 OBAQシェルから呼び出すメソッドは、全ての引数を文字列として受けつけるようにします。
OBAQシェルは、bashやRubyと同様のヒアドキュメントを理解します。 オプションに複数行にわたる内容を設定する時に使用します。
HtmlDocument title = Rubyについて .description = <<END このドキュメントにはRubyについての説明が記述してあります。 Ruby言語についてよく知っている人は飛ばしてください。 END
| 開始マーク | 終了マーク | 備考 |
|---|---|---|
| >>END | 行の先頭のEND | ENDの所には任意の文字列を指定可能 |
| >>-END | ENDだけの行 | -があると終了マークをインデントできる |
| >* | *だけの行 | *のかわりに任意の文字を指定可能 |
| { | }だけの行 | [] <> ()も同様 |
| {{ | }}だけの行 | [[]] <<>> (())も同様 |
.def メソッド名 メソッド本体という形式でオブジェクトにメソッドを定義できます。 メソッド本体にはヒアドキュメント形式を使用することができます。
SomeObject xxxx
.def method1 "puts hello"
.def method2 {
puts "hello"
}
.def method3 <*
puts "hello"
*
同様に、 .def_acq メソッド名 メソッド本体でACQメソッドを定義できます。 ACQメソッドについては「環境獲得」を参照してください。
| コマンド | 引数 | 処理内容 |
|---|---|---|
| %load | ファイル名 | Rubyソースを読みこむ |
| %require | ファイル名 | Rubyソースを読みこむ(既に読まれていれば処理しない) |
| %eval | Rubyスクリプト | Rubyの命令として評価する |
| %include | モジュール名 | Rubyのモジュールを利用可能にする |
| %import | ファイル名 | 別のOBAQフォーマットのソースを読みこむ |
| %get_object | クラス名 オブジェクト名 | OBAQオブジェクトを呼び出す |
| %model_class | クラス名 Rubyスクリプト | クラスを定義する |
| %comment | コメント | コメント |
%load, %require, %evalはほぼRubyの同名の命令に対応しています。
%get_objectは、OBAQオブジェクトを分割して記述した場合に、 他のファイルで定義されたオブジェクトを修正するために使用します。
a.walの中でa.htmlというオブジェクトが定義されていて、 b.walからa.walを引用したとします。
(a.wal) HtmlDocument a.html (b.wal) %import a.wal %get_object HtmlDocument a.html title=yyy
この時、上記のように%get_objectjを使用すると、 あたかも a.htmlに対してtitle=yyyというオプションが指定されていたのと同じ意味になります。
OBAQフォマーットの中でRubyのクラスを定義する略記法です。 下記の2つは同じ意味になります。
%eval {{
class AAA < ObaqModelObj
def_option :xxx
end
}}
%model_class lAAA ObaqModelObj {{
def_option :xxx
}}
OBAQ構成定義ファイルからオブジェクトを生成できるクラスは次の3種類に分かれています。
モデルクラス
Obaq::ObaqModelObjをインクルードしたクラスです。 ユーザから対象システムの機能(What)を記述するクラスです。
ジョブクラス
Obaq::JobObjをインクルードしたクラスです。 モデルクラスに対して行う指示とその順番(How)を記述するクラス
コマンドセットクラス
Obaq::CommandSetを継承したクラスです。 モデルクラスとジョブクラスの組合せを決めて、 実際に行う作業を指定するために使用します。
構成定義ファイルの読みこみ
メインの構成定義ファイルを読みこみます。 この中から、%require等で指定されたRubyソースも読みこみその中でクラス定義が行なわれます。 そして、OBAQオブジェクトを生成します。
上記の一連の処理は、Obaq::ObaqApp.main に記述してあります。 アプリケーションから、このメソッドを呼びだすこともできますが、 これを実行する obaq というコマンドも用意してあります。
下記の内容を $ obaq sample.obaq として実行したとします。
# (1)
%model_class HelloMsg {
def_option :message
}
# (2)
HelloMsg hello1
message="Hello, world"
# (3)
CommandSet
ObaqJob
.def doit {
hello = app.get_object(HelloMsg, "hello1")
puts hello.message
}
この時、以下のような順番で処理が実行されます。
class HelloMsgの定義(%model_class)
OBAQオブジェクトを生成するためのクラス定義が行なわれます。 この例では、%model_classによって構成定義ファイルの中にクラスを定義していますが、 クラスの内容が複雑になる場合には、別ファイルに記述して %requireで読みこみます。
HelloMsgオブジェクトの生成
HelloMsgクラスのオブジェクトが生成されます。 このクラスには messageというオプションスロットがありますが、 このオプションには"Hello, world"という文字列が設定されます。
コマンドセットオブジェクトとジョブオブジェクトの生成
CommandSetとObaqJobは、組み込みのコマンドセットとジョブクラスです。 簡単な処理ではこのように組み込みのクラスを使用しますが、 複雑な処理では、ジョブオブジェクトやコマンドセットオブジェクトも個別に記述します。
そして、このObaqJobオブジェクトに doit というメソッドを定義しています。
コマンドセットの選択
この構成定義にはコマンドセットがひとつしかないので、自動的にそれが選択されます。 複数のコマンドセットがある場合は、obaqコマンド --target xxx というオプションを与えて 実行すべきコマンドセットを指定します。
ジョブの実行
このコマンドセットにはジョブオブジェクトがひとつだけ付属していますので、 そのオブジェクトのdoitメソッドを実行します。
doitメソッドの内容は、構成定義ファイル内にインラインで記述されています。
モデルオブジェクトの機能を呼出す
doitの中から app.get_object(クラス名、オブジェクト名)で、HelloMsgオブジェクトを呼び出します。 そして、おのオブジェクトに出力すべきメッセージの内容を問合せます。
このようにジョブは実行に際して、モデルオブジェクトに指示したり、問い合わせたりして、 ユーザの指定した構成に従った処理を行います。
OBAQシェルによってオブジェクトを生成するためには、 classが以下の条件を満たしている必要があります。
initialize(app, name, ...)
最初の引数は必ずapp(アプリケーションオブジェクト)になります。 アプリケーションオブジェクトは、OBAQオブジェクト全体を管理するシステムに一つだけ存在するオブジェクトです。 多くのOBAQの機能は、このオブジェクト経由で実行されます。 initializeの中ではinit_modelを呼び出して下さい。
init_modelのひとつめの引数は、initializeで渡されたappになります。 ふたつ目の引数は、そのOBAQモデルオブジェクトの名前になります。 このオブジェクトが別のOBAQオブジェクトから参照される場合は名前をつけるようにして下さい。 ただし、そのオブジェクトがsingleton(あるクラスのオブジェクトがひとつしか生成されない場合)の時はクラス名で参照を得ることができるので名前は不要です。
build_model
OBAQシェルが構成定義ファイルを読み終わった直後に、build_modelというメソッドが呼ばれます。 他のモデルオブジェクトとの関連づけ(を伴う初期化)を行う場合には、このメソッドの中で実行してください。
構成定義ファイルから呼ばれるメソッド
.メソッド名の形式で、構成定義ファイルからOBAQモデルオブジェクトのメソッドを呼ぶことができます。 このようなメソッドを提供する場合は、引数を全部文字列としてください。
他のオブジェクトから呼ばれるメソッド
Rubyの通常のメソッドとして記述できます。
app.get_object(クラス名、名前)
他のオブジェクトへの参照を得る場合に使用します。
app.each_object(クラス名) { ブロック }
特定クラスのオブジェクト全てに対してブロックを繰り返すイテレータです。