IO Interface And Operations We need IO abstraction layer for porting and extension. See Vnode/VFS interface (vnode(9)) on IO abstraction. 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 */ }; vop_open(), vop_read(), vop_getattr(), ... are defined over v_op. **v_op (vnode operation vector) corresponds to a method of object oriented programming. &fml8; provides IO::Adapter class as IO abstraction layer. Fundamentals Of IO::Adapter IO::Adapter is most fundamental in &fml8; architecture. It is well considered and implemented. It provides enough primitive methods. IO::Adapter class abstracts KEY => VALUE or KEY => [ VALUE, VALUE2, VALUE3 ] type data structure. It is similar to RDBMS theory like this. KEY1 VALUE1-1 "" "" KEY2 VALUE2-1 VALUE2-2 VALUE2-3 KEY3 VALUE3-1 VALUE3-2 VALUE3-3 KEY4 VALUE4-1 VALUE4-2 VALUE4-3 To maintain address list, least fundamental methods of IO::Adapter are open() close() and IO operations to the object add(KEY, ARGV) (ARGV is class dependent) delete(KEY) find(KEY or REGEXP) get_next_key() methods. At least, enough to write user management codes. Methods / Operation Vector Mentioned above, the fundamental methods of IO::Adapter are as follows: open() close() add(KEY, ARGV) (ARGV = class dependent) delete(KEY) find(KEY or REGEXP) get_next_key() Other than these methods, new() as a constructor and destructor() are needed. The constructor is new() methond. For example, use like this; $obj = new IO::Adapter MAP; The argument is a map which calls map dependent constructor. open() For a file, call open(2), for RDBMS, connect to the specified SQL server. close() Reverse of open(). add(KEY, ARGV) add KEY (primary key) or KEY and the associated VALUE into the object. ARGV is class dependent. We assume an object is composed of a form. It is similar to RDBMS. The primary key is a mail address. This assumption is correct in the case of mailing list driver. This is basic data structure. delete(KEY) Delete KEY and the associated VALUE. find(KEY) / find(REGEXP) Search data corresponding with the primary key within an object. The target is specified as regular expression. Regular expression search is useful. The return value is STR of ARRAY_REF (KEY => [ VALUE, VALUE2, VALUE3 ]). get_next_key() Return the list of primary keys like this: while ($obj->get_next_key()) { ... } This operation corresponds to FIRST_KEY() and NEXT_KEY() of perl hash. Discussion What ARRAY_REF return ? What expected in using ARRAY_REF as the return value ? A list of PRIMARY KEY ? How To Get All Primary Keys ? We need to call get_next_keys() again and again to retrieve all keys. It may be useful to implement special method to return a list of keys but it is not implemented. get_primary_keys() ? find('*', { all => 1 }) is used for this purpose. It is of no use to implement specific method for this. HASH_REF As Return Value ? What expected ? RETURN VALUE = { KEY_1 => VALUE_1, KEY_2 => VALUE_2, } If a mail address has several attributes, this method is useful. For example, consider digest delivery: ADDRESS => { internal => 3 hours, compression => no, format => mime/multipart, }; Abstracted Cache IO layer is this type such as: FML::Error -> FML::Error::Cache -> Tie::JournaledDir But ... dependent on Tie::* hmm ..