<!-- -*- mode:text coding:euc-jp -*-
   $FML: io_abstraction.sgml,v 1.6 2008/08/18 20:52:59 fukachan Exp $
-->


<chapter id="io.abstraction">
	<title>
	IO インターフェイス
	</title>

<para>
将来の移植性/拡張性を見据えるなら、ＭＬサーバの設計においても
UNIX における Vnode/VFS interface (vnode(9)参照)
のような構造をあらかじめ導入しておくべきでしょう。
</para>

<para>
例: NetBSD の vnode 構造体。
<screen>
struct vnode {
		...
        voff_t          v_size;                 /* size of file */
        int             v_numoutput;            /* num pending writes */
        long            v_writecount;           /* ref count of writers */
		...
        int             (**v_op)(void *);       /* vnode ops vector */
		...
        void            *v_data;                /* private data for fs */
};
</screen>
v_op の先に、
vop_open()
vop_read()
vop_getattr()
などが定義されます。
</para>

<para>
つまり struct vnode の **v_op (vnode operation vector) にあたるものが
IO に使われるクラスの各メソッドです。
</para>

<para>
&fml8; では IO::Adapter クラスという抽象化されたインターフェイスを使います。
このクラスはユーザリストというオブジェクトに対する 
IO インターフェイスを抽象化したものです。
</para>


<sect1 id="io.abstraction.overview">
	<title>
	IO::Adapter の基本形
	</title>

<para>
&fml8; の設計上の手本となるクラスは 
<link linkend="module.io.adapter">
IO::Adapter
</link>
といえるでしょう。実装もすでに完成形であり、primitive なメソッドは何か？
などについても十分考えぬかれています。
</para>

<para>
<link linkend="module.io.adapter">
IO::Adapter
</link>
クラスは
<screen>
KEY => VALUE
</screen>
もしくは
<screen>
KEY => [ VALUE, VALUE2, VALUE3 ]
</screen>
のいづれかの型のデータ構造を抽象化していると考えられます。
つまり、これは RDBMS の基礎理論同様の表型データ構造です。
</para>
<screen>
KEY1 VALUE1-1 ""     ""
KEY2 VALUE2-1 VALUE2-2 VALUE2-3
KEY3 VALUE3-1 VALUE3-2 VALUE3-3
KEY4 VALUE4-1 VALUE4-2 VALUE4-3
</screen>

<para>
ユーザリストを管理する上で必要最低限の基本メソッド群は、
IO::Adapter の
<screen>
open()
close()
</screen>
および、そのオブジェクトへの IO である
<screen>
add(KEY, ARGV) (ARGV はクラス依存のデータ渡しのためにある引数)
delete(KEY)
find(KEY or REGEXP)
get_next_key()
</screen>
があれば十分のようです。
少なくとも、ユーザ管理は、これらのメソッドだけで十分書けます。
</para>

</sect1>


<sect1 id="io.abstraction.ops">
	<title>
	メソッド
	</title>

<para>
前述のように IO::Adapter の基本メソッドは次の通りです。
<screen>
open()
close()
add(KEY, ARGV) (ARGV はクラス依存のデータ渡しのためにある引数)
delete(KEY)
find(KEY or REGEXP)
get_next_key()
</screen>
</para>

<para>
もちろん、オブジェクトを生成するのは new() ですので、
これら以外に new() だけは必要です:)
必要なら適宜、ディストラクタも定義して下さい。
</para>


<sect2>
	<title>
	new()
	</title>

<para>
オブジェクトを生成するのは new() です。
たとえば IO::Adapter であれば、
<screen>
$obj = new IO::Adapter マップ;
もしくは
$obj = new IO::Adapter マップ, パラメータ(ハッシュリファレンス);
</screen>
などとオブジェクトタイプを引数(マップ)で指定するため、
マップに応じた初期化を行ないます。
</para>

<para>
呼び出しの基本形は引数の多い方に合わせるので、次のようになります。
<screen>
my $config = $curproc->config();
my $obj    = new IO::Adapter $map, $config;
</screen>
HOOK なので利用する際にも、つねに、この形で書いておくべきです。
</para>

</sect2>


<sect2>
	<title>
	open()
	</title>

<para>
ファイルであれば open(2)、
RDBMS であれば SQL サーバへの接続を確立する、
といった具合です。
</para>

</sect2>


<sect2>
	<title>
	close()
	</title>

<para>
すなおに open() の逆の動作をします。
</para>

</sect2>


<sect2>
	<title>
	add(KEY, ARGV)
	</title>

<para>
KEY (プライマリキー)もしくは
KEY および KEY に付随するデータをオブジェクトに書き込みます。
なお、ARGV はクラス依存のデータ渡しのためにある引数で、
この引数が使われないこともあります。
</para>

<para>
UNIX と異なり、オブジェクトの構造に一定の型があります。
型とは RDBMS のようなテーブルの形です。
</para>

<para>
また、プライマリキーとなるのは通常メールアドレスです。
この前提が多くの場面で正しいため、メールアドレスをプライマリキーにした
テーブル型が基本的なデータ構造といえます。
</para>

</sect2>


<sect2>
	<title>
	delete(KEY)
	</title>

<para>
KEY および KEY に付随するデータ構造を削除します。
</para>

</sect2>


<sect2>
	<title>
	find(KEY) / find(REGEXP)
	</title>

<para>
オブジェクト内からプライマリキーに該当するデータを探します。
</para>

<para>
探す対象を正規表現で指定できるように作る方が便利です。
正規表現検索が使えると、ユーザ検索などで重宝します。
</para>

<para>
返り値は STR か ARRAY_REF (KEY に対する [ VALUE, VALUE2, VALUE3 ])です。
</para>

</sect2>


<sect2>
	<title>
	get_next_key()
	</title>

<para>
プライマリキーの一覧を取り出したい場合が多々あります。
そこで
<screen>
while ($obj->get_next_key()) { ... }
</screen>
のような表現を可能とするために、このメソッドが実装されています。
</para>

<para>
これはハッシュの FIRST_KEY() と NEXT_KEY() にあたるといえます。
しかしながら、我々の場合は open() などのメソッドが別途用意されているため、
FIRST_KEY() と NEXT_KEY() のように２つに分ける必要はありません。
</para>

<para>
XXX すなおに、key() もしくは get_key() でも良い気がする？
</para>

</sect2>

</sect1>


<sect1 id="io.abstraction.discussion">
	<title>
	議論
	</title>


<sect2>
	<title>
	返り値を ARRAY_REF 型で欲しい場合？
	</title>

<para>
うーん、どういうデータ構造が欲しいんでしょうねぇ？
PRIMARY KEY の一覧くらいか？
</para>

</sect2>


<sect2>
	<title>
	プライマリキーを全部取り出したい場合は？
	</title>

<para>
定石は全部読んでみること、つまり get_next_key() を呼びまくるコードです。
専用のメソッドがあった方が便利でしょうけど、
めったにそんなコードは書かないので、
専用メソッドがなくても問題はないと考えます。
</para>

<para>
もし、あるとしたら get_primary_keys() みたいな名前のメソッド？で、
ARRAY_REF で返すのでしょうかね？でも、find('*', { all => 1 }) などとす
ると全部の KEY の値がARRAY_REF で返りますんで、
別のメソッドも不要な気がします。
<screen>
(KEY1 KEY2)
</screen>
</para>

</sect2>


<sect2>
	<title>
	返り値が HASH_REF の場合？
	</title>

<para>
これこそ、どういうデータ構造が欲しい場合なんでしょうか？
返り値が HASH_REF とは、次のような構造が返り値になる場合です。
<screen>
返り値 = {
	変数１ => 値１、 
	変数２ => 値２、 
}
</screen>
</para>

<para>
メールアドレスに属性をつける場合、こういった型での返り値が欲しいでしょうか。
たとえば、まとめ送り(&fml4; の例)。
<screen>
メールアドレス => {
	送り間隔	=> 	３時間、
	ファイル圧縮	=>	しない
	フォーマット	=>	mime/multipart
};
</screen>
注意: 
&fml8; では、まとめ送りの複雑なオプションはサポートしていませんので、
この操作は不要です。
</para>

<para>
結局、実装する必要がないというのが現状。
</para>

</sect2>

</sect1>


</chapter>
