|
From: <rv...@us...> - 2009-11-23 17:14:07
|
Revision: 295
http://treebase.svn.sourceforge.net/treebase/?rev=295&view=rev
Author: rvos
Date: 2009-11-23 17:13:57 +0000 (Mon, 23 Nov 2009)
Log Message:
-----------
Added POD to VeryBadORM.pm
Modified Paths:
--------------
trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm
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-23 14:59:51 UTC (rev 294)
+++ trunk/treebase-core/src/main/perl/lib/CIPRES/TreeBase/VeryBadORM.pm 2009-11-23 17:13:57 UTC (rev 295)
@@ -1,4 +1,3 @@
-
package CIPRES::TreeBase::VeryBadORM;
use Carp 'croak';
use strict 'vars';
@@ -7,14 +6,75 @@
our %dbh;
our $DBH;
+=head1 NAME
+
+CIPRES::TreeBase::VeryBadORM
+
+=head1 DESCRIPTION
+
+Superclass for TreeBASE objects. This class is subclassed by packages in TreeBaseObjects.
+
+=head1 PACKAGE VARIABLES
+
+=over
+
+=item %dbh
+
+This hash holds cached (in theory different) database handles keyed on class names.
+
+=item $DBH
+
+Holds a singleton database handle
+
+=back
+
+=head1 PACKAGE METHODS
+
+=over
+
+=item set_db_connection()
+
+Sets the database handle for the invoking child class. Called as a package method.
+
+=cut
+
sub set_db_connection { my $class = shift; $DBH = $dbh{$class} = shift; }
+
+=item get_db_connection()
+
+Gets the database handle for the invoking child class. Called as a package method.
+
+=cut
+
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 {
my ($self, $q) = @_;
return $DBH->prepare_cached($q);
}
+=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 }
+
+=cut
+
my %cache;
sub new {
my ($class, $id) = @_;
@@ -31,6 +91,15 @@
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 returns 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
sub AUTOLOAD {
my $obj = shift;
@@ -51,36 +120,85 @@
}
}
+=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.
+
+=cut
+
sub has_attr {
my $base = shift;
my $class = ref($base) || $base;
return $class->attr_hash()->{shift()};
}
+=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.
+
+=cut
+
sub has_r_attr {
my $base = shift;
my $class = ref($base) || $base;
return $class->r_class(shift());
}
+=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.
+
+=cut
+
sub has_r2_attr {
my $base = shift;
my $class = ref($base) || $base;
return $class->r2_class(shift());
}
+=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()).
+
+=cut
+
sub has_subobject {
my $base = shift;
my $subobj = shift;
return $base->has_attr($base->foreign_key($subobj));
}
+=item foreign_key()
+
+Turns the supplied argument into a foreign key column. It does this by lower casing the
+argument string and appending '_id'.
+
+=cut
+
sub foreign_key {
my $base = shift;
my $subobj = lc(shift); # XXX
return $subobj . "_id";
}
+=item attr_hash()
+
+Returns a hash reference of all available attributes for the invocant. It does this by first
+checking to see if there is an %attr hash defined in the invocant's class (and returns
+a reference to that if it's there). Otherwise it calls attr_list, uses its contents as keys
+(values are 1) and adds the class name . '_id', i.e. a lookup of the primary key. On subsequent
+calls the output is cached due to the autovivification of the package hash.
+
+=cut
+
sub attr_hash {
my $base = shift;
my $class = ref($base) || $base;
@@ -90,40 +208,69 @@
my $attr_list = $base->attr_list;
if (@$attr_list) {
%$attr_hash = map { $_ => 1 } @$attr_list;
- $attr_hash->{"$class\_id"} = 1;
+ $attr_hash->{"$class\_id"} = 1; # XXX case correct?
return $attr_hash;
}
return;
}
+=item attr_list()
+
+Returns an array reference of available attributes. It does this by checking if there is an
+array ref $attr available in the invocant's class (and returns that). Otherwise it checks
+the invocant's mapped database table and collects the returned column names and returns those.
+On subsequent calls the output is cached due to the autovivification of the package array.
+
+=cut
+
sub attr_list {
my $base = shift;
my $class = ref($base) || $base;
my $attr_list = \@{"$class\::attr"};
return $attr_list if @$attr_list;
- my $q = "select * from " . $base->table . " fetch first 1 rows only";
+ my $q = "select * from " . $base->table . " fetch first 1 rows only"; # XXX case correct?
my $sth = $DBH->prepare_cached($q);
$sth->execute();
while (my $row = $sth->fetchrow_hashref) {
- @$attr_list = keys %$row;
+ @$attr_list = keys %$row;
}
$sth->finish;
return $attr_list;
}
+=item r_attr_hash()
+
+Returns the %r_attr hash defined in the invocant's class (see TreeBaseObjects for a description
+of what that hash is for).
+
+=cut
+
sub r_attr_hash {
my $base = shift;
my $class = ref($base) || $base;
return my $r_attr_hash = \%{"$class\::r_attr"};
}
+=item r2_attr_hash()
+
+Returns the %r2_attr hash defined in the invocant's class (see TreeBaseObjects for a description
+of what that hash is for).
+
+=cut
+
sub r2_attr_hash {
my $base = shift;
my $class = ref($base) || $base;
return my $r_attr_hash = \%{"$class\::r2_attr"};
}
+=item reify()
+
+Populates the invocant object's attributes from the database.
+
+=cut
+
sub reify {
my $obj = shift;
return $obj if $obj->reified;
@@ -134,40 +281,76 @@
$sth->execute($id_value);
my $rows = 0;
while (my $row = $sth->fetchrow_hashref()) {
- %$obj = %$row;
- $obj->{ID} = $obj->{$id_attr};
- $obj->set_reified();
- if (++$rows > 1) {
- croak("Table '$table' has multiple entries for $id_attr = $id_value");
- }
+ %$obj = %$row;
+ $obj->{'id'} = $obj->{$id_attr};
+ $obj->set_reified();
+ if (++$rows > 1) {
+ croak("Table '$table' has multiple entries for $id_attr = $id_value");
+ }
}
return $obj;
}
-sub reified { $_[0]{reified} }
-sub set_reified { $_[0]{reified} = 1 }
+=item reified()
+Returns whether the invocant has been reified (see reify()).
+
+=cut
+
+sub reified { $_[0]{'reified'} }
+
+=item set_reified()
+
+Flags that the invocant object has been reified.
+
+=cut
+
+sub set_reified { $_[0]{'reified'} = 1 }
+
+=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.>
+
+=cut
+
sub get {
my ($self, $attr) = @_;
if ($self->has_attr($attr)) {
- return $self->get_no_check($attr);
+ return $self->get_no_check($attr);
} elsif ($self->has_subobject($attr)) {
- return $self->get_subobject_no_check($attr, @_);
+ return $self->get_subobject_no_check($attr, @_);
} elsif ($self->has_r_attr($attr)) {
- return $self->get_r_subobject_no_check($attr, @_);
+ return $self->get_r_subobject_no_check($attr, @_);
}
my $trace = Devel::StackTrace->new;
print $trace->as_string; # like carp
croak($self->class . " has no attribute named '$attr'");
}
+=item get_no_check()
+
+Returns the value of the supplied attribute name as applies to the invocant object. This will
+most likely just return scalar, non-reference values such as titles and labels.
+
+=cut
+
sub get_no_check {
my ($self, $attr) = @_;
- return $self->id if $attr eq "id";
+ return $self->id if $attr eq 'id';
return $self->{$attr} if exists $self->{$attr};
return $self->{$attr} = $self->reify->{$attr};
}
+=item get_subobject_no_check()
+
+Treats the supplied attribute name as either a true attribute or name from which a subobject
+(in one-to-one relation) is instantiated. See description of %subobject hash in TreeBaseObjects.
+
+=cut
+
sub get_subobject_no_check {
my ($self, $attr) = @_;
return $self->{$attr} if exists $self->{$attr};
@@ -176,6 +359,13 @@
return $self->{$attr} = $self->subobject_class($attr)->new($id);
}
+=item get_r_subobject_no_check()
+
+Treats the supplied attribute name as either a true attribute or name from which a subobject
+(in many-to-one relation) is instantiated. See description of %r_attr hash in TreeBaseObjects.
+
+=cut
+
# Example: Studies have analyses as a subobject
# $study->get_r_subobject_no_check("analyses")
# should query
@@ -198,6 +388,13 @@
return @results;
}
+=item get_r2_subobject_no_check()
+
+Treats the supplied attribute name as either a true attribute or name from which a subobject
+(in many-to-one relation) is instantiated. See description of %r2_attr hash in TreeBaseObjects.
+
+=cut
+
# Example: Treeblocks have submissions as subobjects
# and vice versa
# $treeblock->get_r2_subobject_no_check("submission")
@@ -206,7 +403,7 @@
# and return a list of submission objects
sub get_r2_subobject_no_check {
my ($self, $attr) = @_;
- $attr = uc $attr;
+# $attr = uc $attr;
my $q = $self->r2_subobject_query($attr);
my $target_class = $self->r2_class($attr);
my $sth = $self->prepare_cached($q);
@@ -217,6 +414,19 @@
}
return @results;
}
+
+=item r2_subobject_query()
+
+Creates a SQL statement to resolve the many-to-many relationship (through intersection table)
+between the invocant object and the supplied attribute. It does this by looking up the class
+name to instantiate from (by calling r2_class()), the intersection table to look up the relation
+(by calling r2_table()) and the field name of the id column in the intersection table (by calling
+r2_id_attr()).
+
+See description of %r2_attr hash in TreeBaseObjects.
+
+=cut
+
sub r2_subobject_query {
my ($self, $attr) = @_;
@@ -228,65 +438,183 @@
return $q;
}
+=item r2_id_attr()
+
+Returns name of the foreign key column in the intersection table of the referenced objects
+(as opposed to instances of the invocant column) in a many-to-many relation. By default, it
+consults %r2_attr first. And if that doesn't work, it consults the foreign class's %r2 instead,
+to see if the relationship was defined in the other direction.
+
+See description of %r2_attr hash in TreeBaseObjects. This method returns the 3rd element (index 2)
+in the value array ref.
+
+=cut
+
sub r2_id_attr {
my ($self, $attr) = @_;
- $self->r2_attr_hash()->{uc $attr}->[2] || $self->r2_class($attr)->id_attr;
+ $self->r2_attr_hash()->{$attr}->[2] || $self->r2_class($attr)->id_attr;
}
+=item to_str()
+
+Stringification method. Returns at least the invocant's class name and its ID number, possibly
+augmented by other attributes (as implemented in child classes).
+
+=cut
+
sub to_str { my $self = shift;
my %attr = @_;
return $self->class . " #" . $self->id; }
+=item id()
+
+Returns the invocant's identifier number.
+
+=cut
+
sub id { $_[0]{'id'} }
+
+=item id_attr()
+
+Returns the name of the column that contains the primary key for instances of the invocant class.
+
+=cut
+
sub id_attr { return lc($_[0]->class . "_id") };
+
+=item class()
+
+Returns the invocant class name.
+
+=cut
+
sub class { return ref($_[0]) || $_[0]; }
my %known_class;
+=item known_class_hash()
+
+Returns a reference to the private %known_class hash, in which child classes register themselves.
+
+=cut
+
sub known_class_hash { return \%known_class; }
+
+=item register()
+
+Called by child classes in the package body. Causes these classes to be registered in the
+%known_class hash. Can be called with arguments, in which case the arguments are considered
+class names to register, or without any, in which case the class name is deduced by using
+caller().
+
+=cut
+
sub register {
my $my_class = shift;
my @classes = @_;
@classes = scalar(caller()) unless @classes;
for my $class (@classes) {
push @{"$class\::ISA"}, $my_class;
- $class->known_class_hash->{uc $class} = $class;
+ $class->known_class_hash->{uc $class} = $class; # XXX casing correct?
}
}
+=item alias()
+
+Returns a registered alias for the supplied class name.
+
+=cut
+
sub alias {
my ($base, $class) = @_;
return $base->known_class_hash->{$class};
}
+=item subobject_class()
+
+Returns the class name for the supplied subobject name. This is either a value in the invocant
+class's %subobject hash (see TreeBaseObjects), an alias as returned by the alias() method or
+the supplied subobject's name itself.
+
+=cut
+
sub subobject_class {
my ($self, $subobj) = @_;
my $subobj_class = \%{$self->class . "::subobject"};
return $subobj_class->{$subobj} if exists $subobj_class->{$subobj};
- return $self->alias($subobj) || $subobj;#ucfirst(lc($subobj));
+ return $self->alias($subobj) || $subobj;#ucfirst(lc($subobj)); # XXX really?
}
+=item get_id_pair()
+
+Returns the name of the column that contains the primary key in the table onto which the invocant's
+class is mapped, and the value of the id for the invocant instance.
+
+=cut
+
sub get_id_pair {
my $self = shift;
return ($self->id_attr, $self->id);
}
-sub table { return $_[0]->class; }
+=item table()
+
+Returns the name of the table onto which the invocant's class is mapped.
+
+=cut
+
+sub table { return $_[0]->class; } # XXX case correct?
+
+=item r_class()
+
+Returns the class name for the supplied attribute. This is a value in the %r_attr hash.
+
+=cut
+
sub r_class {
my ($self, $r_attr) = @_;
return $self->r_attr_hash()->{$r_attr};
}
+=item r2_table()
+
+Returns the name of the intersection table that connects instances of invocant's class to other
+objects in a many-to-many relation.
+
+See a description of the %r2_attr hash in TreeBaseObjects. This method returns the first field
+(index 0) in the value array reference.
+
+=cut
+
sub r2_table {
my ($self, $r_attr) = @_;
return $self->r2_attr_hash()->{$r_attr}->[0];
}
+=item r2_class()
+
+Returns the class name for objects that are in a many-to-many relationship with the invocant
+object through an intersection table.
+
+See a description of the %r2_attr hash in TreeBaseObjects. This method returns the second field
+(index 1) in the value array reference.
+
+=cut
+
sub r2_class {
my ($self, $r_attr) = @_;
return $self->r2_attr_hash()->{$r_attr}->[1];
}
+=item dump()
+
+Traverses invocant, executes supplied handlers as defined by the named 'action' argument. The
+'action' argument provides a subroutine reference whose first argument is the invocant, remaining
+arguments are a pass-through of @_. The dump method recurses through the invocant, an operation
+whose depth can be limited by providing a named 'maxdepth' argument.
+
+=cut
+
sub dump {
my $self = shift();
my %attr = @_;
@@ -303,10 +631,44 @@
delete $attr{$class};
}
+=item recurse()
+
+Empty placeholder method. Implemented by child classes in TreeBaseObjects.
+
+=cut
+
sub recurse { }
+
+=item consistent()
+
+Empty placeholder method. Implemented by child classes in TreeBaseObjects. Returns true by default.
+
+=cut
+
sub consistent { 1; }
+
+=item is_nested()
+
+Empty placeholder method. Implemented by child classes in TreeBaseObjects. Returns false by default.
+
+=cut
+
sub is_nested { 0; }
+=item DESTROY()
+
+Empty destructor, needed here so that it's not dispatched to AUTOLOAD
+
+=cut
+
sub DESTROY { }
+=back
+
+=head1 SEE ALSO
+
+L<TreeBaseObjects>
+
+=cut
+
1;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|