.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Alzabo::RDBMSRules 3pm" .TH Alzabo::RDBMSRules 3pm "2015-05-24" "perl v5.20.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh sub reverse_engineer { my \f(CW$self\fR = shift; my \f(CW$schema\fR = shift; .PP .Vb 1 \& my $dbh = $schema\->driver\->handle; \& \& foreach my $table ( $dbh\->tables ) \& { \& my $t = $schema\->make_table( name => $table ); \& \& $self\->reverse_engineer_table($t); \& } \&} .Ve .PP sub reverse_engineer_table { my \f(CW$self\fR = shift; my \f(CW$table\fR = shift; .PP .Vb 1 \& my $dbh = $table\->schema\->driver\->handle; \& \& my $sth = $dbh\->column_info( undef, $table\->schema\->name, $table\->name, undef ); \& \& while ( my $col_info = $sth\->fetchrow_hashref ) \& { \& use Data::Dumper; warn Dumper $col_info; \& my %attr = ( name => $col_info\->{COLUMN_NAME}, \& type => $col_info\->{TYPE_NAME}, \& nullable => $col_info\->{NULLABLE} ? 1 : 0, \& ); \& \& $attr{size} = \& $col_info\->{COLUMN_SIZE} if $col_info\->{COLUMN_SIZE}; \& \& $attr{precision} = \& $col_info\->{DECIMAL_DIGITS} if $col_info\->{DECIMAL_DIGITS}; \& \& $attr{default} = \& $col_info\->{COLUMN_DEF} if defined $col_info\->{COLUMN_DEF}; \& \& $attr{comment} = \& $col_info\->{REMARKS} if defined $col_info\->{REMARKS}; \& \& $table\->make_column(%attr); \& } \& \& $self\->reverse_engineer_table_primary_key($table); \&} .Ve .PP sub reverse_engineer_table_primary_key { my \f(CW$self\fR = shift; my \f(CW$table\fR = shift; .PP .Vb 1 \& my $dbh = $table\->schema\->driver\->handle; \& \& my $sth = $dbh\->column_info( undef, $table\->schema\->name, $table\->name ); \& \& while ( my $pk_info = $sth\->fetchrow_hashref ) \& { \& $table\->add_primary_key( $table\->column( $pk_info\->{COLUMN_NAME} ) ); \& } \&} .Ve .SH "NAME" Alzabo::RDBMSRules \- Base class for Alzabo RDBMS rulesets .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Alzabo::RDBMSRules; \& \& my $rules = Alzabo::RDBMSRules( rules => \*(AqMySQL\*(Aq ); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This class is the base class for all \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR modules. To instantiate a subclass call this class's \f(CW\*(C`new()\*(C'\fR method. See the \&\*(L"\s-1SUBCLASSING\s0 Alzabo::RDBMSRules\*(R" section for information on how to make a ruleset for the \s-1RDBMS\s0 of your choice. .SH "METHODS" .IX Header "METHODS" .SS "available" .IX Subsection "available" A list of names representing the available \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR subclasses. Any one of these names would be appropriate as the \&\*(L"rdbms\*(R" parameter for the \f(CW\*(C`Alzabo::RDBMSRules\->new()\*(C'\fR method. .SS "new" .IX Subsection "new" The constructor always accepts one parameter, \*(L"rdbms\*(R", which is the name of the \s-1RDBMS\s0 to be used. .PP Some subclasses may accept additional values. .PP The constructor returns a new \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR object of the appropriate subclass. .PP Throws: \f(CW\*(C`Alzabo::Exception::Eval\*(C'\fR .ie n .SS "schema_sql (""Alzabo::Create::Schema"" object)" .el .SS "schema_sql (\f(CWAlzabo::Create::Schema\fP object)" .IX Subsection "schema_sql (Alzabo::Create::Schema object)" Returns a list of \s-1SQL\s0 statements which would create the given schema. .ie n .SS "index_sql (""Alzabo::Create::Index"" object)" .el .SS "index_sql (\f(CWAlzabo::Create::Index\fP object)" .IX Subsection "index_sql (Alzabo::Create::Index object)" Returns a list of \s-1SQL\s0 statements to create the specified index. .ie n .SS "drop_table_sql (""Alzabo::Create::Table"" object)" .el .SS "drop_table_sql (\f(CWAlzabo::Create::Table\fP object)" .IX Subsection "drop_table_sql (Alzabo::Create::Table object)" Returns a list of \s-1SQL\s0 statements to drop the specified table. .ie n .SS "drop_index_sql (""Alzabo::Create::Index"" object)" .el .SS "drop_index_sql (\f(CWAlzabo::Create::Index\fP object)" .IX Subsection "drop_index_sql (Alzabo::Create::Index object)" Returns a list of \s-1SQL\s0 statements to drop the specified index. .SS "schema_sql_diff" .IX Subsection "schema_sql_diff" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Schema\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Schema\*(C'\fR object .PP This method compares the two schema objects and returns an array of \&\s-1SQL\s0 statements which turn the \*(L"old\*(R" schema into the \*(L"new\*(R" one. .SS "table_sql_diff" .IX Subsection "table_sql_diff" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .PP This method compares the two table objects and returns an array of \&\s-1SQL\s0 statements which turn the \*(L"old\*(R" table into the \*(L"new\*(R" one. .ie n .SS "type_is_numeric (""Alzabo::Column"" object)" .el .SS "type_is_numeric (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_numeric (Alzabo::Column object)" Returns a boolean indicating whether or not the column is numeric (integer or floating point). .SS "quote_identifiers" .IX Subsection "quote_identifiers" Returns true or false to indicate whether or not the generated \s-1DDL SQL\s0 statements should have their identifiers quoted or not. This may be overridden by subclasses. It defaults to false. .SS "can_alter_table_name" .IX Subsection "can_alter_table_name" If this is true, then when syncing a schema, the object will call \&\f(CW\*(C`alter_table_name_sql()\*(C'\fR to change the table's name. Otherwise it will call \f(CW\*(C`recreate_table_sql()\*(C'\fR. .SS "can_alter_column_name" .IX Subsection "can_alter_column_name" If this is true, then when syncing a schema, the object will call \&\f(CW\*(C`alter_column_name_sql()\*(C'\fR to change the table's name. Otherwise it will call \f(CW\*(C`recreate_table_sql()\*(C'\fR. .SS "Virtual Methods" .IX Subsection "Virtual Methods" The following methods are not implemented in the \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR class itself and must be implemented in its subclasses. .SS "column_types" .IX Subsection "column_types" Returns a list of valid column types. .SS "feature ($feature)" .IX Subsection "feature ($feature)" Given a string defining a feature, this method indicates whether or not the given \s-1RDBMS\s0 supports that feature. By default, this method always returns false unless overridden in the subclass. .PP Features that may be asked for: .IP "\(bu" 4 extended_column_types .Sp Column types that must be input directly from a user, as opposed to being chosen from a list. MySQL's \s-1ENUM\s0 and \s-1SET\s0 types are examples of such types. .IP "\(bu" 4 index_column_prefixes .Sp MySQL supports the notion of column prefixes in indexes, allowing you to index only a portion of a large text column. .IP "\(bu" 4 fulltext_indexes .Sp This should be self-explanatory. .IP "\(bu" 4 functional_indexes .Sp Indexes on functions, as supported by PostgreSQL. .ie n .SS "validate_schema_name (""Alzabo::Schema"" object)" .el .SS "validate_schema_name (\f(CWAlzabo::Schema\fP object)" .IX Subsection "validate_schema_name (Alzabo::Schema object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the schema's name is not valid. .ie n .SS "validate_table_name (""Alzabo::Create::Table"" object)" .el .SS "validate_table_name (\f(CWAlzabo::Create::Table\fP object)" .IX Subsection "validate_table_name (Alzabo::Create::Table object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the table's name is not valid. .ie n .SS "validate_column_name (""Alzabo::Create::Column"" object)" .el .SS "validate_column_name (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "validate_column_name (Alzabo::Create::Column object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the column's name is not valid. .SS "validate_column_type ($type_as_string)" .IX Subsection "validate_column_type ($type_as_string)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the type is not valid. .PP This method returns a canonized version of the type. .ie n .SS "validate_column_length (""Alzabo::Create::Column"" object)" .el .SS "validate_column_length (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "validate_column_length (Alzabo::Create::Column object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the length or precision is not valid for the given column. .SS "validate_column_attribute" .IX Subsection "validate_column_attribute" This method takes two parameters: .IP "\(bu" 4 column => \f(CW\*(C`Alzabo::Create::Column\*(C'\fR object .IP "\(bu" 4 attribute => \f(CW$attribute\fR .PP This method is a bit different from the others in that it takes an existing column object and a \fBpotential\fR attribute. .PP It throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the attribute is is not valid for the column. .ie n .SS "validate_primary_key (""Alzabo::Create::Column"" object)" .el .SS "validate_primary_key (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "validate_primary_key (Alzabo::Create::Column object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the column is not a valid primary key for its table. .ie n .SS "validate_sequenced_attribute (""Alzabo::Create::Column"" object)" .el .SS "validate_sequenced_attribute (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "validate_sequenced_attribute (Alzabo::Create::Column object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the column cannot be sequenced. .ie n .SS "validate_index (""Alzabo::Create::Index"" object)" .el .SS "validate_index (\f(CWAlzabo::Create::Index\fP object)" .IX Subsection "validate_index (Alzabo::Create::Index object)" Throws an \f(CW\*(C`Alzabo::Exception::RDBMSRules\*(C'\fR if the index is not valid. .ie n .SS "table_sql (""Alzabo::Create::Table"" object)" .el .SS "table_sql (\f(CWAlzabo::Create::Table\fP object)" .IX Subsection "table_sql (Alzabo::Create::Table object)" Returns an array of \s-1SQL\s0 statements to create the specified table. .ie n .SS "column_sql (""Alzabo::Create::Column"" object)" .el .SS "column_sql (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "column_sql (Alzabo::Create::Column object)" Returns an array of \s-1SQL\s0 statements to create the specified column. .ie n .SS "foreign_key_sql (""Alzabo::Create::ForeignKey"" object)" .el .SS "foreign_key_sql (\f(CWAlzabo::Create::ForeignKey\fP object)" .IX Subsection "foreign_key_sql (Alzabo::Create::ForeignKey object)" Returns an array of \s-1SQL\s0 statements to create the specified foreign key. .ie n .SS "drop_column_sql (""Alzabo::Create::Column"" object)" .el .SS "drop_column_sql (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "drop_column_sql (Alzabo::Create::Column object)" Returns an array of \s-1SQL\s0 statements to drop the specified column. .ie n .SS "drop_foreign_key_sql (""Alzabo::Create::ForeignKey"" object)" .el .SS "drop_foreign_key_sql (\f(CWAlzabo::Create::ForeignKey\fP object)" .IX Subsection "drop_foreign_key_sql (Alzabo::Create::ForeignKey object)" Returns an array of \s-1SQL\s0 statements to drop the specified foreign key. .ie n .SS "column_sql_add (""Alzabo::Create::Column"" object)" .el .SS "column_sql_add (\f(CWAlzabo::Create::Column\fP object)" .IX Subsection "column_sql_add (Alzabo::Create::Column object)" Returns an array of \s-1SQL\s0 statements to add the specified column. .SS "column_sql_diff" .IX Subsection "column_sql_diff" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Column\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Column\*(C'\fR object .PP This method compares the two table objects and returns an array of \&\s-1SQL\s0 statements which turn the \*(L"old\*(R" table into the \*(L"new\*(R" one. .SS "index_sql_diff" .IX Subsection "index_sql_diff" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Index\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Index\*(C'\fR object .PP This method compares the two index objects and returns an array of \&\s-1SQL\s0 statements which turn the \*(L"old\*(R" index into the \*(L"new\*(R" one. .SS "alter_primary_key_sql" .IX Subsection "alter_primary_key_sql" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .PP This method compares the two table objects and returns an array of \s-1SQL\s0 statements which alter the \*(L"old\*(R" one's primary key to match the \*(L"new\*(R" one's. .ie n .SS "alter_table_name_sql (""Alzabo::Create::Table"" object)" .el .SS "alter_table_name_sql (\f(CWAlzabo::Create::Table\fP object)" .IX Subsection "alter_table_name_sql (Alzabo::Create::Table object)" Given a table, this method is expected to change the table's name from \&\f(CW\*(C`$table\->former_name\*(C'\fR to \f(CW\*(C`$table\->name\*(C'\fR. This will only be called if the rules object returns true for \f(CW\*(C`can_alter_table_name()\*(C'\fR. .ie n .SS "alter_column_name_sql (""Alzabo::Create::Table"" object)" .el .SS "alter_column_name_sql (\f(CWAlzabo::Create::Table\fP object)" .IX Subsection "alter_column_name_sql (Alzabo::Create::Table object)" Given a column, this method is expected to change the table's name from \f(CW\*(C`$column\->former_name\*(C'\fR to \f(CW\*(C`$column\->name\*(C'\fR. This will only be called if the rules object returns true for \&\f(CW\*(C`can_alter_column_name()\*(C'\fR. .SS "recreate_table_sql" .IX Subsection "recreate_table_sql" This method takes two parameters: .IP "\(bu" 4 new => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .IP "\(bu" 4 old => \f(CW\*(C`Alzabo::Create::Table\*(C'\fR object .PP This method is expected to drop the old table and create the new one. .PP However, it \fBmust\fR preserve all the data stored in the old table, excluding data in columns that are being dropped. Additionally, if there are sequences associated with columns in the old table, they should not be dropped. .PP This method will only be called if either \f(CW\*(C`can_alter_table_name()\*(C'\fR or \&\f(CW\*(C`can_alter_column_name()\*(C'\fR return false. .ie n .SS "reverse_engineer (""Alzabo::Create::Schema"" object)" .el .SS "reverse_engineer (\f(CWAlzabo::Create::Schema\fP object)" .IX Subsection "reverse_engineer (Alzabo::Create::Schema object)" Given a schema object (which presumably has no tables), this method uses the schema's \f(CW\*(C`Alzabo::Driver\*(C'\fR object to connect to an existing database and reverse engineer it into the appropriate Alzabo objects. .ie n .SS "type_is_integer (""Alzabo::Column"" object)" .el .SS "type_is_integer (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_integer (Alzabo::Column object)" Returns a boolean indicating whether or not the column is an integer type. .ie n .SS "type_is_floating_point (""Alzabo::Column"" object)" .el .SS "type_is_floating_point (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_floating_point (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a floating point type. .ie n .SS "type_is_character (""Alzabo::Column"" object)" .el .SS "type_is_character (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_character (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a character type. This is defined as any type which is defined to store text, regardless of length. .ie n .SS "type_is_date (""Alzabo::Column"" object)" .el .SS "type_is_date (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_date (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a date type. This is \fBnot\fR true for datetime types. .ie n .SS "type_is_datetime (""Alzabo::Column"" object)" .el .SS "type_is_datetime (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_datetime (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a datetime type. This is \fBnot\fR true for date types. .ie n .SS "type_is_time (""Alzabo::Column"" object)" .el .SS "type_is_time (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_time (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a time type. This is \fBnot\fR true for datetime types. .ie n .SS "type_is_time_interval (""Alzabo::Column"" object)" .el .SS "type_is_time_interval (\f(CWAlzabo::Column\fP object)" .IX Subsection "type_is_time_interval (Alzabo::Column object)" Returns a boolean indicating whether or not the column is a time interval type. .SH "SUBCLASSING Alzabo::RDBMSRules" .IX Header "SUBCLASSING Alzabo::RDBMSRules" To create a subclass of \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR for your particular \&\s-1RDBMS\s0 is fairly simple. .PP Here's a sample header to the module using a fictional \s-1RDBMS\s0 called FooDB: .PP .Vb 1 \& package Alzabo::RDBMSRules::FooDB; \& \& use strict; \& use vars qw($VERSION); \& \& use Alzabo::RDBMSRules; \& \& use base qw(Alzabo::RDBMSRules); .Ve .PP The next step is to implement a \f(CW\*(C`new()\*(C'\fR method and the methods listed under the section \*(L"Virtual Methods\*(R". The new method should look a bit like this: .PP .Vb 10 \& 1: sub new \& 2: { \& 3: my $proto = shift; \& 4: my $class = ref $proto || $proto; \& 5: my %p = @_; \& 6: \& 7: my $self = bless {}, $self; \& 8: \& 9: return $self; \& 10: } .Ve .PP The hash \f(CW%p\fR contains any values passed to the \&\f(CW\*(C`Alzabo::RDBMSRules\->new\*(C'\fR method by its caller. .PP Lines 1\-7 should probably be copied verbatim into your own \f(CW\*(C`new\*(C'\fR method. Line 5 can be deleted if you don't need to look at the parameters. .PP The rest of your module should simply implement the methods listed under the \*(L"Virtual Methods\*(R" section of this documentation. .PP Look at the included \f(CW\*(C`Alzabo::RDBMSRules\*(C'\fR subclasses for examples. Feel free to contact me for further help if you get stuck. Please tell me what database you're attempting to implement, and include the code you've written so far. .SH "AUTHOR" .IX Header "AUTHOR" Dave Rolsky,