From: <mjd...@us...> - 2009-11-25 05:59:46
|
Revision: 324 http://treebase.svn.sourceforge.net/treebase/?rev=324&view=rev Author: mjdominus Date: 2009-11-25 05:58:49 +0000 (Wed, 25 Nov 2009) Log Message: ----------- lots more documentation Modified Paths: -------------- trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/TreeBaseObjects.pm trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm Modified: trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/TreeBaseObjects.pm =================================================================== --- trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/TreeBaseObjects.pm 2009-11-25 04:21:49 UTC (rev 323) +++ trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/TreeBaseObjects.pm 2009-11-25 05:58:49 UTC (rev 324) @@ -16,77 +16,6 @@ on. -=head1 OBJECT ATTRIBUTES - -In general, if a database object, represented by C<$X>, has an attribute named C<foo>, then -C< $X->foo > retrieves the value of the attribute. If the attribute is a scalar, the value is -returned as a Perl scalar; if the attribute is a reference to another database object, a -Perl object is returned. - -Each object is assumed to correspond to a single table in the database. If the object class is -C<ObjectClass>, the corresponding table name is assumed to be C<objectclass>. This can be -overridden by defining the C<ObjectClass::table> method, which should return the correct table -name. - -Each object from class C<ObjectClass> is assumed to have a unique ID attribute which is stored -in the table in a field whose name is returned by C<ObjectClass::id_attr>. This defaults to -C<objectclass_id> if the method is not overridden. - -C<ObjectClass::new(C<$id>)> will create an object with the specified ID number. Objects are -created lazily: the database is not consulted until some - -We will consider a running example, a database which contains studies, trees, and matrices. -Each tree and each matrix is contained in exactly one study, but each study may have multiple -trees and matrices. - -Each tree contains zero or more treenodes, and each matrix contains zero or more matrixrows. - -Suppose the C<tree> table contains a field, C<study_id>, which contains the foreign key of the - -=over - -=item %r_attr - -This hash specifies which other classes refer to the specifying class in a many-to-one relationship. -For example: - - %Analysis::r_attr = ( 'analysissteps' => 'AnalysisStep' ); - -Specifies that multiple AnalysisStep objects may refer to an invocant Analysis object, and that -these AnalysisStep objects can be instantiated by calling the Analysis::analysissteps() method. - -=item %r2_attr - -This hash specifies associated objects which are linked to instances of the specifying class -through an intersection table. For example: - - %Citation::r2_attr = ( - 'authors' => ['citation_author', 'Person', 'authors_person_id'] - ); - -Specifies that associated instances of the Person class can be fetched by calling the -Citation::authors() method which will look in the citation_author table and instantiate -Person objects passing the values in the authors_person_id column to the Person -constructor. - -=item %subobject - -This hash specifies associated classes which are identified by foreign keys in the table that is -mapped onto the specifying class. For example: - - %PhyloTree::subobject = ( - 'rootnode' => 'PhyloTreeNode', - 'treetype' => 'TreeType' - ); - -Specifies that an associated instance (one-to-one) of the PhyloTree's root node (instantiated -as a PhyloTreeNode) and of the tree type (instantiates as a TreeType object) can be -created by calling the rootnode() and treetype() methods, respectively. Calls to those methods -will look in phylotree.rootnode_id and phylotree.treetype_id in the row of the invocant object -and create the right associated objects. - -=back - =head1 PACKAGE METHODS =over Modified: trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm =================================================================== --- trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm 2009-11-25 04:21:49 UTC (rev 323) +++ trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm 2009-11-25 05:58:49 UTC (rev 324) @@ -20,22 +20,117 @@ This module maps relations in a relational database to objects in Perl. It avoids all difficult implementation problems by providing only read-only access. -=head1 PACKAGE VARIABLES +=head1 OBJECT ATTRIBUTES -=over +In general, if a database object, represented by C<$X>, has an attribute named C<foo>, +then C< $X->foo > or C< $x->get('foo') > retrieves the value of the attribute. If the +attribute is a scalar, the value is returned as a Perl scalar; if the attribute is a +reference to another database object, a Perl object is returned. -=item %dbh +Each object is assumed to correspond to a single table in the database. If the object +class is C<ObjectClass>, the corresponding table name is assumed to be C<objectclass>. +This can be overridden by defining the C<ObjectClass::table> method, which should return +the correct table name. -This hash holds cached database handles keyed on class names. +Each object from class C<ObjectClass> is assumed to have a unique ID attribute which is stored +in the table in a field whose name is returned by C<ObjectClass::id_attr>. This defaults to +C<objectclass_id> if the method is not overridden. -=item $DBH +C<ObjectClass::new(C<$id>)> will create an object with the specified ID number. Objects +are created lazily: the database is not consulted until some other attribute of the object +is read. -Holds a singleton database handle +Attributes are of four types. The call C< $Object->foo > will be resolved in the +following order: +=over 4 + +=item 1. Direct attributes. + +If the object's table has a column whose name is exactly C<foo>, the value from that +column will be returned directly. SQL C<NULL> values are returned as Perl C<undef>. + +Technical details: Whether an attribute name is considered a direct attribute is +determined by the return value of the C<has_attr> method. If C<has_attr> returns true, +C<get_no_check> is called to produce the attribute value. See the descriptions of those +methods below for details. + +=item 2. Subobjects. + +If the object's table has a column whose name is C<foo_id>, its value is taken to be a +foreign key, joining to a table named C<foo>. The corresponding record is looked up in +the joined table, and an object of class C<Foo> will be constructed and returned. + +Technical details: Whether an attribute is considered to be a subobject name is determined +by the return value of the C<has_subobject> method. The foreign key column name may be +overridden by redefining the C<foreign_key> method. The name of the instantiated class +may be overridden by redefining the C<subobject_class> method. The name of the joined +table may be overridden by redefining the C<table> method for the instantiated class. See +the descriptions of those methods below for details. + =back -=head1 PACKAGE METHODS +=item 3. Reverse subobjects. +If the object's package contains an C<%r_attr> hash with key C<foo>, the value is taken to +be a class that contains a foreign key linking to the invocant's table. The tables are +joined and all foreign objects linking to the invocant are returned. + +Technical details: An attribute name is considered to designate a reverse subobject when +the C<has_r_attr> method returns true. If so, the C<r_class> method is called to +determine the class from which the subobjects will be instantiated, that class's C<table> +method will determine the table joined, and that class's C<foreign_key> method will be +called to determine the foreign key column for the join. See the descriptions of those +methods below for details. + +=item 4. Linked objects. + +If the invocant's package contains an C<%r2_attr> hash with key C<foo>, the value should be +an array whose first element is the name of a link table that contains keys for both the +invocant's table and a foreign table. The three tables are joined, and the objects from +the foreign table that link through to the invocant are returned. The value +C<$r2_attr{foo}> has the following format: + + [ link table name, + class in which foreign objects are instantiated, + column of link table with foreign keys ] + +The third of these is optional; if omitted, the name returned by the foreign class's +C<id_attr> method is used. + +Technical details: An attribute name is considered to designate a linked object when the +C<has_r2_attr> method returns true. The C<r2_table> method is called to determine the +name of the link table. The invocant's ID is looked up in the column of the link table +named by the invocant's C<id_field> method, and the corresponding values of the column +named by the C<r2_id_attr> method are gathered. An object is allocated for each resulting +foreign id, in the class named by the C<r2_class> method. See the descriptions of those +methods below for details. + +For example, consider the following tables: + + study study_author person + + study_id study_id person_id person_id + 1 1 100 100 + 2 2 101 101 + 3 2 102 102 + 4 2 103 103 + 3 102 + 4 101 + 4 103 + +Suppose that C<%study::r2_attr> contains: + + authors => [ 'study_author', 'Person', 'person_id' ] + +The link table is C<study_author>, and the foreign table is C<person>. Then +C< Study->new(4)->authors > will return C<Person> objects 101 and 103. + + +=back + +=head1 CLASS METHODS + =over =item set_db_connection() @@ -54,30 +149,19 @@ sub get_db_connection { my $class = shift; return $dbh{$class}; } -=back - -=head1 INSTANCE METHODS - -=over - -=item prepare_cached() - -Prepares a query on the singleton database handle, returns statement handler. - -=cut - -sub prepare_cached { +# Private; should remain undocumented +sub _prepare_cached { my ($self, $q) = @_; return $DBH->prepare_cached($q); } +=head1 INSTANCE METHODS + =item new() -Instantiates an instance of one of the classes defined in TreeBaseObjects. This constructor -requires that the singleton database handle $CIPRES::TreeBase::VeryBadORM::DBH has been defined -and that a valid ID is supplied as argument. Instantiated objects are cached in the private -%cache hash as $cache{$class}{$id}. Returned objects are simply blessed hash references that -contain the ID as { 'id' => $id } +Instantiates an instance of one of the classes defined in TreeBaseObjects. This +constructor requires that the singleton database handle $CIPRES::TreeBase::VeryBadORM::DBH +has been defined and that a valid ID is supplied as argument. =cut @@ -97,20 +181,9 @@ return $obj; } -=item AUTOLOAD - -Provides the magical methods available in the child classes. It does this by checking which of -has_attr(), has_subobject(), has_r_attr() or has_r2_attr() applies and invokes one of -get_no_check(), get_subobject_no_check(), get_r_subobject_no_check() or get_r2_subobject_no_check() -respectively. Croaks otherwise. - -=cut - # Maybe add some caching here at some point # -# This should dispatch off to ->get because the code in the -# two places is almost the same and we've already had one bug -# occur when they didn't stay in sync. mjd 20091123 +# Do not document this; it's private sub AUTOLOAD { our $AUTOLOAD; my ($package, $method) = $AUTOLOAD =~ /(.*)::(.*)/; @@ -121,9 +194,12 @@ =item has_attr() -Checks to see if the invocant's class defines the supplied attribute. It does this by calling -attr_hash() and doing a lookup for the supplied attribute in the returned hash. +Given an attribute name, check to see if the invocant's class has a direct attribute with +that name, and return true or false. +By default, it looks for the attribute name as a key in the hash returned by the +C<attr_hash()> method. + =cut sub has_attr { @@ -134,10 +210,12 @@ =item has_r_attr() -Checks to see if the invocant's class defines the supplied "reverse attribute" (see -L<TreeBaseObjects> for the description of the %r_attr hash). It does this by returning whatever -is returned by r_class() whilst passing it the supplied "reverse attribute"'s name. +Given an attribute name, check to see if the invocant's class has a reverse attribute with +that name, and return true or false. +By default, this just passes the attribute name to the C<r_class> method to see if an +instantiating class is known for the reverse attribute. + =cut sub has_r_attr { @@ -148,11 +226,12 @@ =item has_r2_attr() -Checks to see if the invocant's class defines the supplied "reverse attribute through -intersection table" (see L<TreeBaseObjects> for the description of the %r2_attr hash). It does this -by returning whatever is returned by r2_class() whilst passing it the supplied "reverse -attribute through intersection table"'s name. +Given an attribute name, check to see if the invocant's class has a link attribute with +that name, and return true or false. +By default, this just passes the attribute name to the C<r2_class> method to see if an +instantiating class is known for the link attribute. + =cut sub has_r2_attr { @@ -163,10 +242,12 @@ =item has_subobject() -Checks to see if the invocant is associated with the supplied subobject. It does this by -first turning the subobject's name into a foreign key column (by calling foreign_key()) and then -checking whether that column is available as an attribute (by calling has_attr()). +Given an attribute name, check to see if the invocant's class has a subobject attribute with +that name, and return true or false. +By default, this method first obtains the name of the foreign key field used for this +attribute, and then checks to see if the invocant has a direct attribute with that name. + =cut sub has_subobject { @@ -177,8 +258,8 @@ =item foreign_key() -Turns the supplied argument into a foreign key column. It does this by lower casing the -argument string and appending '_id'. +Given an attribute name, return the name of the field that stores foreign keys for that +attribute. =cut @@ -311,11 +392,10 @@ =item get() -Given an invocant and a supplied attribute name, returns the attribute value. What the attribute -actually is, is decided by first checking has_attr(), has_subobject(), has_r_attr() and returns -the output of either get_no_check(), get_subobject_no_check() or get_r_subobject_no_check() -respectively. B<This method is probably never used and therefore probably buggy.> +Given an invocant and an attribute name, returns the attribute value. +See the section L<> for details of how attribute names are resolved. + =cut sub get { @@ -409,7 +489,7 @@ # $attr = uc $attr; my $q = $self->r2_subobject_query($attr); my $target_class = $self->r2_class($attr); - my $sth = $self->prepare_cached($q); + my $sth = $self->_prepare_cached($q); $sth->execute($self->id); my @results; while (my ($target_id) = $sth->fetchrow) { @@ -464,6 +544,8 @@ sub r2_id_attr { my ($self, $attr) = @_; +# Would it make more sense to use $self->foreign_key($attr) as the fallback here? +# 20091125 MJD $self->r2_attr_hash()->{$attr}->[2] || $self->r2_class($attr)->id_attr; } @@ -544,18 +626,24 @@ =item subobject_class() -Returns the class name for the supplied attribute name. The default is the name of the attribute, -lowercase with initial capital. This may be overridden by an entry in the C<%subobject> hash in the -invocant's class. For example, suppose there are C<Dessert> objects and C<Flavor> objects. Suppose -each C<Dessert> has a C<flavor> and an C<alternate_flavor> attribute, which are C<Flavor> objects. -One could represent this by defining: +Given an attribute name, return the name of the class that represents that attribute. +The default version looks up the attribute name as a key in the hash C<%subobject>, and, +returns the associated value, if there is one. + +If not, it converts the attribute name to all-lowercase, uppercases the first character, +and uses the result. For example, the default class for the C<potato> attribute is C<Potato>. + +For example, suppose there are C<Dessert> objects and C<Flavor> objects. Suppose each +C<Dessert> has a C<flavor> and an C<alternate_flavor> attribute, which are C<Flavor> +objects. One could represent this by defining: + %Dessert::subobject = (flavor => 'Flavor', alternate_flavor => 'Flavor', ); -which says that whenever a C<Dessert> object's C<flavor> or C<alternate_flavor> attributes are -accessed, C<VeryBadORM> should instantiate them as C<Flavor> objects. +which says that whenever a C<Dessert> object's C<flavor> or C<alternate_flavor> attributes +are accessed, C<VeryBadORM> should instantiate them as C<Flavor> objects. But one could omit the first entry from the hash: @@ -566,7 +654,6 @@ One may, of course, override this method to implement any mapping of attribute to class names that is desired. - =cut sub subobject_class { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |