IO Abstraction Layer (IO::Adapter Class) IO::Adapter Overview All IO of &fmldevel; should use IO::Adapter class like vfs/vnode framework. For example, read/write member list, add/remove a user. The usage is like this: use IO::Adapter; $obj = new IO::Adapter $map, $map_params; $obj->open || croak("cannot open $map"); while ($x = $obj->get_next_key()) { ... } $obj->close; $map is map:identifier. file: can be omitted. Currently available maps follow: file:/var/spool/ml/elena/recipients unix.group:root nis.group:root mysql:id postgresql:id ldap:id "file:" map is a normal file (text file). "unix.group:root" map is to read root entry in /etc/group file. "nis.group:root" map is to read root entry in NIS (YP). "mysql:id" map implies the use of MySQL. Parameters for MySQL access is defined in "mysql:id" entry. These paraemeters should be specified before calling "new IO::Adapter". IO::Adapter Methods Official methods IO::Adapter provides currently follow: new() open() close() get_next_key() add(KEY) delete(KEY) getpos() setpos(NUM) eof() touch() find(REGEXP, $args) KEY is a primary key to handle database access. In almost cases, the primary key is a mail address. REGEXP is a regular expression (regexp), this is usually also a mail address. Unification of all types of IO needs that we should implement least methods. The currently implemented methods are selected by test and our operations. If could, we refer SQL IO more than file IO as an abstraction base model of IO::Adapter. It introduces difference between &fml4; and &fml8; but it is mandatory for further abstraction. Argument Type Of Methods The argument needs nothing, STR or ARRAY_REF as the argument and the return value is STR. get_next_key() is typical as the no argument case. This method is used to list up the content of files or retrieve the specific address in the file. In other case, the return value may be a pair of strings. KEY_STR => [ VALUE_STR_1 VALUE_STR_2 VALUE_STR_3 ] This is used as the return value to represent ARRAY_REF. For example, "actives" file of &fml4; consists of lines which have plural space separeted entries. So it can be representated as a type of array. rudo@nuinui.net s=skip m=xxx.yyy.z # commnet rudo@nuinui.net => [ s=skip m=xxx.yyy.z # comment ] If retrieval value could be represented as ARRAY_REF, argument of store operations alsot needs ARRAY_REF. It is summarized as follows. The argument is one of "nothing", "STR" or ARRAY_REF. The return value is either of STR of ARRAY_REF (array reference). argument return value --------------------------------------- none => STR STR => STR none => [STR, STR, ... ] STR => [STR, STR, ... ] File Map "file:/some/where/file/name" or file name "/some/where/file/name" map is abstraction of IO to/from a text file. The format of text file is space separated. Unixgroup Map Abstraction of /etc/group. IO is read only. For example, the access to wheel:*:0:root,rudo,kenken in /etc/group is "unixgroup:wheel" map in IO::Adapter. $obj = new IO::Adapter "unixgroup:wheel"; If you call get_next_key() method for this object, you will get the member of wheel group sequentially. In other words the wheel group is regarded as the following file by IO::Adapter. root rudo kenken Nis Map It is same as one of /etc/group but the data is retrieved from NIS/YP. MySQL Map For easy maintenance, it is better to write all mysql configurations in one file. For example, it is good that we have only to write SQL configurations in config.cf. But we identify plural mysql conditions. So, we use the tag [mysql:members] to declare one region between a tag to the next tag or =cut. It is similar to .ini file format used on Microsoft OS. We use the tags like this: config.cf example member_maps = mysql:members recipient_maps = mysql:recipients [mysql:members] sql_server = localhost sql_user = fml sql_password = uja sql_database = fml sql_table = ml sql_find = select * from ... ... In calling IO::Adapter, use new IO::Adapter "mysql:members", $config; where $config is a hash reference holding some paremeters like this: $config => { [mysql:members] => { sql_sever => localhost ... } } FML::Config prepares this $config by reading .cf files. Hence, we usually use FML::Config object as an argument of IO::Adapter::new() method. Discussion: How To Write SQL Statements In config.cf ? (fml-devel 204) How about lexical scope ? The .cf files cannot contain all variables since lexical scope variables exist. We use &varname syntax for such lexical scope variables. For example, use different member and recipient maps. In SQL statements, the difference is a flag (fml_recipient) in a table. For example, consider the case that "where" statement has different value but it is determined lexically in calling MySQL. member_maps = mysql:members recipient_maps = mysql:recipients [mysql:members] sql_server = localhost sql_user = fml sql_password = uja sql_database = fml sql_table = ml sql_get_next_key = select fml_address from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' sql_getline = select * from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' sql_add = insert into $sql_table values ('$ml_name', '$ml_domain', '&address', 1, 1) sql_delete = delete from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_address = '&address' sql_find = select * from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_address like '&regexp' [mysql:recipients] sql_server = localhost sql_user = fml sql_password = uja sql_database = fml sql_table = ml sql_get_next_key = select fml_address from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_recipient = '1' sql_getline = select * from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_recipient = '1' sql_add = update ml set recipient = 1 where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_address = '&address' sql_delete = update ml set recipient = 0 where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_address = '&address' sql_find = select * from $sql_table where fml_ml = '$ml_name' and fml_domain = '$ml_domain' and fml_recipient = '1' and fml_address like '&regexp'