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 '®exp'
[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 '®exp'