Bio::Genex::ArrayLayout - Methods for processing data from the GeneX DB


Bio-Genex documentation Contained in the Bio-Genex distribution.

Index


Code Index:

NAME

Top

Bio::Genex::ArrayLayout - Methods for processing data from the GeneX DB table: ArrayLayout

SYNOPSIS

Top

  use Bio::Genex::ArrayLayout;

  # instantiating an instance
  my $ArrayLayout = Bio::Genex::ArrayLayout->new(id=>47);

  # retrieve data from the DB for all columns
  $ArrayLayout->fetch();

  # creating an instance, without pre-fetching all columns
  my $ArrayLayout = new Bio::Genex::ArrayLayout(id=>47);

  # creating an instance with pre-fetched data
  my $ArrayLayout = new Bio::Genex::ArrayLayout(id=>47, 'fetch_all'=>1);

  # retrieving multiple instances via primary keys
  my @objects = Bio::Genex::ArrayLayout->get_objects(23,57,98)




  # retrieving all instances from a table
  my @objects = Bio::Genex::ArrayLayout->get_all_objects();

  # retrieving the primary key for an object, generically
  my $primary_key = $ArrayLayout->id();

  # or specifically
  my $al_pk_val = $ArrayLayout->al_pk();

  # retreving other DB column attributes
  my $con_fk_val = $ArrayLayout->con_fk();
  $ArrayLayout->con_fk($value);

  my $name_val = $ArrayLayout->name();
  $ArrayLayout->name($value);

  my $technology_type_val = $ArrayLayout->technology_type();
  $ArrayLayout->technology_type($value);

  my $identifier_code_val = $ArrayLayout->identifier_code();
  $ArrayLayout->identifier_code($value);

  my $medium_val = $ArrayLayout->medium();
  $ArrayLayout->medium($value);

  my $coating_val = $ArrayLayout->coating();
  $ArrayLayout->coating($value);

  my $default_spot_conc_val = $ArrayLayout->default_spot_conc();
  $ArrayLayout->default_spot_conc($value);

  my $default_spot_conc_units_val = $ArrayLayout->default_spot_conc_units();
  $ArrayLayout->default_spot_conc_units($value);

  my $default_spot_conc_error_val = $ArrayLayout->default_spot_conc_error();
  $ArrayLayout->default_spot_conc_error($value);




DESCRIPTION

Top

Each Genex class has a one to one correspondence with a GeneX DB table of the same name (i.e. the corresponding table for Bio::Genex::ArrayLayout is ArrayLayout).

Most applications will first create an instance of Bio::Genex::ArrayLayout and then fetch the data for the object from the DB by invoking fetch(). However, in cases where you may only be accessing a single value from an object the built-in delayed fetch mechanism can be used. All objects are created without pre-fetching any data from the DB. Whenever an attribute of the object is accessed via a getter method, the data for that attribute will be fetched from the DB if it has not already been. Delayed fetching happens transparently without the user needing to enable or disable any features.

Since data is not be fetched from the DB until it is accessed by the calling application, it could presumably save a lot of access time for large complicated objects when only a few attribute values are needed.

ATTRIBUTES

Top

There are three different types of attributes which instances of Bio::Genex::ArrayLayout can access: raw foreign key attributes, Obect-Oriented foreign key attributes, and simple column attributes.

Raw Foreign Keys Attributes
Object Oriented Foreign Key Attributes

This mode presents foreign key attributes in a special way, with all non-foreign key attributes presented normally. Foreign keys are first retrieved from the DB, and then objects of the appropriate classes are created and stored in slots. This mode is useful for applications that want to process information from the DB because it automates looking up information.

Specifying the 'recursive_fetch' parameter when calling new(), modifies the behavior of this mode. The value given specifies the number of levels deep that fetch will be invoked on sub-objects created.

Simple Column Attributes

CLASS VARIABLES

Top

Class Bio::Genex::ArrayLayout defines the following utility variables for assisting programmers to access the ArrayLayout table.

$Bio::Genex::ArrayLayout::LIMIT

If defined, $LIMIT will set a limit on any select statements that can return multiple instances of this class (for example get_objects() or any call to a ONE_TO_MANY or LOOKUP_TABLE foreign key accessor method).

$Bio::Genex::ArrayLayout::USE_CACHE

This variable controls whether the class will cache any objects created in calls to new(). Objects are cached by primary key. The caching is very simple, and no effort is made to track whether different invocations of new() are being made for an object with the same primary key value, but with different options set. If you desire to reinstantiate an object with a different set of parameters, you would need to undefine $USE_CACHE first.

WARNING: variables other than those listed here are for internal use only and are subject to change without notice. Use them at your own risk.

DELAYED FETCH

Top

It is possible to retrieve only the subset of attributes one chooses by simply creating an object instance and then calling the appropriate getter function. The object will automatically fetch the value from the DB when requested. This can potentially save time for large complicated objects. This triggers a separate DB query for each attribute that is accessed, whereas calling fetch() will retrieve all fields of the object with a single query.

For example:

  my $ArrayLayout = Bio::Genex::ArrayLayout->new(id=>47);
  my $val = $ArrayLayout->al_pk();

The attribute's value is then cached in the object so any further calls to that attribute's getter method do not trigger a DB query.

NOTE: Methods may still return undef if their value in the DB is NULL.

CLASS METHODS

Top

The following methods can all be called without first having an instance of the class via the Bio::Genex::ArrayLayout->methodname() syntax.

new(%args)

new() accepts the following arguments:

id

Numeric or string value. The value of the primary key for looking up the object in the DB.

linking_table()

Used by generic functions to determine if a specified class is a linking table class. For Bio::Genex::ArrayLayout it returns 0, since it is not a linking table class.

pkey_name()

This method returns the name of the column which is used as the primary key for this DB table. This method only exists for non-linking table classes, and for Bio::Genex::ArrayLayout it returns the value 'al_pk';

table_name()

Returns the name of the DB table represented by this class. For Bio::Genex::ArrayLayout it returns 'ArrayLayout';

column2name()

This method returns a hashref that translates DB column names into human readable format.

name2column()

This method returns a hashref that is a reverse lookup table to translate the human readable version of a DB column name back into the column_name. This is useful for preparing table output in CGI scripts:



    %column2name = %{$class->column2name()};
    if (exists $column2name{$_}) {
      push(@column_copy,$column2name{$_});
    }

    # now that we've translated the names, we sort them
    @column_copy = sort @column_copy;

    # make a header element. 
    push(@rows,th(\@column_copy));




fkeys()

This method returns a hashref that holds all the foreign key entries for the ArrayLayout table.

column_names()

This method returns an array ref which holds the names of all the columns in table ArrayLayout.



    # first retrieve the data from the DB
    $object = $full_module_name->new(id=>$id);
    $object->fetch();

    # now extract the data from the object
    foreach (@{$class->column_names}) {
    # we use this to temporarily relax the strict pragma
    # to use symbolic references
      no strict 'refs';
      $tmp_values{$_} = $object->$_;

    # back to our regularily scheduled strictness
    }




insert_db($dbh)

This method inserts the data for the object into the database specified by the DB handle $dbh. To use this method, create a blank object with new(), set the attributes that you want, and then call insert_db().

  my $dbh = Bio::Genex::current_connection(USER=>$SU_USERNAME,
                                      PASSWORD=>$SU_PASSWORD);
  my ArrayLayout = Bio::Genex::ArrayLayout->new();
  ArrayLayout->con_fk('some_value');
  ArrayLayout->insert_db($dbh);

NOTE: You must log into the DB with a user/password that has INSERT priveleges in the DB, otherwise you will get a DBI error.

WARNING: fetch() will not be called, so if you are using this method to insert a copy of an existing DB object, then it is up to you to call fetch(), otherwise, only the attributes that are currently set in the object will be inserted.

update_db($dbh)

This method update the data for an object already in the database specified by the DB handle $dbh. To use this method, fetch an object from the DB, change the attributes that you want, and then call update_db().

  my $dbh = Bio::Genex::current_connection(USER=>$SU_USERNAME,
                                      PASSWORD=>$SU_PASSWORD);
  my ArrayLayout = Bio::Genex::ArrayLayout->new(id=>43);
  ArrayLayout->con_fk('some_value');
  ArrayLayout->update_db($dbh);

NOTE: You must log into the DB with a user/password that has INSERT priveleges in the DB, otherwise you will get a DBI error.

NOTE: Any modification of the primary key value will be discarded ('al_pk' for module Bio::Genex::ArrayLayout).

get_objects(@id_list)
get_all_objects()
get_objects({column=>'col_name',value=>'val'})

This method is used to retrieve multiple instances of class Bio::Genex::ArrayLayout simultaneously. There are three different ways to invoke this method.

By passing in an @id_list, get_objects() uses each element of the list as a primary key for the ArrayLayout table and returns a single instance for each entry.

WARNING: Passing incorrect id values to get_objects() will cause a warning from Bio::Genex::ArrayLayout::initialize(). Objects will be created for other correct id values in the list.

get_all_objects() returns an instance for every entry in the table.

By passing an anonymous hash reference that contains the 'column' and 'name' keys, the method will return all objects from the DB whose that have the specified value in the specified column.

NOTE: All objects must have the 'id' parameter set before attempting to use fetch() or any of the objects getter functions.

INSTANCE METHODS

Top

The following methods can only be called by first having valid instance of class Bio::Genex::ArrayLayout.

fetch()

This method triggers a DB query to retrieve ALL columns from the DB associated with this object.

WARNING: methods other than those listed here are for internal use only and are subject to change without notice. Use them at your own risk.

FOREIGN KEY ACCESSOR METHODS

Top

There are two major categories of foreign key accessor methods: Object Oriented foreign key methods, and raw foreign key methods.

Each foreign key column in the table is represented by two methods, one OO method and one raw method. The raw method enables fethcing the exact numeric or string values stored in the DB. The OO method creates objects of the class the fkey column refers to. The idea is that if only the numeric fkey value is desired, the raw fkey method can be used. If it is necessary to get attributes from the table referred to by the fkey column, then the OO method should be invoked, and the necessary methods on that object can be queried.

The names of the raw fkey methods is the same as the fkey columns in the DB table they represent (all fkey columns end in the suffix '_fk'). The OO methods have the same names as the column they represent, with the difference that they have the suffix '_obj' instead of '_fk'.

So for example, in class Bio::Genex::ArrayMeasurement the 'primary_es_fk' column is represented by two methods, the raw method primary_es_fk(), and the OO method primary_es_obj.

The following foreign key accessors are defined for class Bio::Genex::ArrayLayout:

@id_list = al_spots_fk()
@obj_list = al_spots_obj()

This is an attribute of type ONE_TO_MANY_LT and refers to class Bio::Genex::AL_Spots. The raw accessor method, al_spots_fk() returns a list of foreign key ids. The OO accessor method, al_spots_obj() returns a list of objects of class Bio::Genex::AL_Spots.

Every foreign key in a DB table belongs to a certain class of foreign keys. Each type of foreign key confers a different behavior on the class that contains it. The classifications used in Genex.pm are:

ATTRIBUTE METHODS

Top

These are the setter and getter methods for attributes in class Bio::Genex::ArrayLayout.

NOTE: To use the getter methods, you may either invoke the fetch() method to retrieve all the values for an object, or else rely on delayed fetching to retrieve the attributes as needed.

id()

id() is a special attribute method that is common to all the Genex classes. This method returns the primary key of the given instance (and for class Bio::Genex::ArrayLayout it is synonomous with the al_pk()method). The id() method can be useful in writing generic methods because it avoids having to know the name of the primary key column.

al_pk()

This is the primary key attribute for Bio::Genex::ArrayLayout. It has no setter method.

$value = con_fk();
con_fk($value);

Methods for the con_fk attribute.

$value = name();
name($value);

Methods for the name attribute.

$value = technology_type();
technology_type($value);

Methods for the technology_type attribute.

$value = identifier_code();
identifier_code($value);

Methods for the identifier_code attribute.

$value = medium();
medium($value);

Methods for the medium attribute.

$value = coating();
coating($value);

Methods for the coating attribute.

$value = default_spot_conc();
default_spot_conc($value);

Methods for the default_spot_conc attribute.

$value = default_spot_conc_units();
default_spot_conc_units($value);

Methods for the default_spot_conc_units attribute.

$value = default_spot_conc_error();
default_spot_conc_error($value);

Methods for the default_spot_conc_error attribute.

WARNING: methods other than those listed here are for internal use only and are subject to change without notice. Use them at your own risk.

IMPLEMENTATION DETAILS

Top

These classes are automatically generated by the create_genex_classes.pl script. Each class is a subclass of the Class::ObjectTemplate::DB class (which is in turn a subclass of Class::ObjectTemplate written by Sriram Srinivasan, described in Advanced Perl Programming, and modified by Jason Stewart). ObjectTemplate implements automatic class creation in perl (there exist other options such as Class::Struct and Class::MethodMaker by Damian Conway) via an attributes() method call at class creation time.

BUGS

Top

Please send bug reports to genex@ncgr.org

LAST UPDATED

Top

on Mon Feb 5 21:23:53 2001 by /home/jasons/work/GeneX-Server/Genex/scripts/create_genex_class.pl --dir=/home/jasons/work/GeneX-Server/Genex --target=ArrayLayout --support=AL_Spots

AUTHOR

Top

Jason E. Stewart (jes@ncgr.org)

SEE ALSO

Top

perl(1).


Bio-Genex documentation Contained in the Bio-Genex distribution.

##############################
#
# Bio::Genex::ArrayLayout
#
# created on Mon Feb  5 21:23:53 2001 by /home/jasons/work/GeneX-Server/Genex/scripts/create_genex_class.pl --dir=/home/jasons/work/GeneX-Server/Genex --target=ArrayLayout --support=AL_Spots
#
# cvs id: $Id: ArrayLayout.pm,v 1.17 2001/02/06 18:58:51 jes Exp $ 
#
##############################
package Bio::Genex::ArrayLayout;

use strict;
use POSIX 'strftime';
use Carp;
use DBI;
use IO::File;
use Bio::Genex::DBUtils qw(:CREATE
		      :ASSERT
		      fetch_last_id
		     );
# import the fkey constants and undefined
use Bio::Genex qw(undefined);
use Bio::Genex::Fkey qw(:FKEY);

use Class::ObjectTemplate::DB 0.21;

use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $FKEYS $COLUMN2NAME $NAME2COLUMN $COLUMN_NAMES %_CACHE $USE_CACHE $LIMIT $FKEY_OBJ2RAW $TABLE2PKEY);

require Exporter;

@ISA = qw(Class::ObjectTemplate::DB Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT_OK = qw();

BEGIN {
  $USE_CACHE = 1;

  %_CACHE = ();

  $COLUMN_NAMES = [
          'al_pk',
          'con_fk',
          'name',
          'technology_type',
          'identifier_code',
          'medium',
          'coating',
          'default_spot_conc',
          'default_spot_conc_units',
          'default_spot_conc_error'
        ]
;
 $FKEYS = {
          'con_obj' => bless( {
                                'table_name' => 'Contact',
                                'fkey_type' => 'FKEY_OO',
                                'fkey_name' => 'con_obj',
                                'pkey_name' => 'con_pk'
                              }, 'Bio::Genex::Fkey' ),
          'al_spots_fk' => bless( {
                                    'table_name' => 'AL_Spots',
                                    'fkey_type' => 'ONE_TO_MANY_LT',
                                    'fkey_name' => 'al_spots_fk',
                                    'pkey_name' => 'al_fk'
                                  }, 'Bio::Genex::Fkey' ),
          'al_spots_obj' => bless( {
                                     'fkey_type' => 'ONE_TO_MANY_LT_OO',
                                     'table_name' => 'AL_Spots',
                                     'fkey_name' => 'al_spots_obj',
                                     'pkey_name' => 'al_fk'
                                   }, 'Bio::Genex::Fkey' )
        }
;

  $COLUMN2NAME  = {
          'technology_type' => 'Technology Type',
          'default_spot_conc_error' => 'Default Spot Concentration Error',
          'default_spot_conc_units' => 'Default Spot Concentration Units',
          'default_spot_conc' => 'Default Spot Concentration',
          'name' => 'Layout Name',
          'coating' => 'Coating',
          'con_fk' => 'Contact',
          'identifier_code' => 'Identifier Code',
          'al_pk' => 'Accession Number',
          'medium' => 'Medium'
        }
;
  $NAME2COLUMN  = {
          'Default Spot Concentration Error' => 'default_spot_conc_error',
          'Default Spot Concentration Units' => 'default_spot_conc_units',
          'Coating' => 'coating',
          'Contact' => 'con_fk',
          'Technology Type' => 'technology_type',
          'Accession Number' => 'al_pk',
          'Default Spot Concentration' => 'default_spot_conc',
          'Medium' => 'medium',
          'Layout Name' => 'name',
          'Identifier Code' => 'identifier_code'
        }
;
  $FKEY_OBJ2RAW = {
          'con_obj' => 'con_fk',
          'al_spots_obj' => 'al_spots_fk'
        }
;
}


attributes (no_lookup=>['fetched', 'fetch_all', 'fetched_attr', 'id'], lookup=>['al_pk', 'con_fk', 'name', 'technology_type', 'identifier_code', 'medium', 'coating', 'default_spot_conc', 'default_spot_conc_units', 'default_spot_conc_error', 'con_obj', 'al_spots_obj', 'al_spots_fk']);

sub table_name {return 'ArrayLayout';} # probably unnecessary

sub fkeys {return $FKEYS;}

sub column2name {return $COLUMN2NAME;}

sub name2column {return $NAME2COLUMN;}

sub fkey_obj2raw {return $FKEY_OBJ2RAW;}

sub column_names {return $COLUMN_NAMES;}

sub pkey_name {return 'al_pk';}

sub linking_table {return 0;}
sub insert_db {
  my ($self,$dbh) = @_;
  assert_dbh($dbh);

  # iterate over the fields and add them to the INSERT
  my %values;
  foreach my $col (@{$COLUMN_NAMES}) {
    no strict 'refs';

    # we don't want Bio::Genex::undefined() to get called
    next unless defined $self->get_attribute($col);

    $values{$col} = $self->$col();
  }

  # don't store a primary key
  delete $values{'al_pk'};

  if (grep {$_ eq 'last_updated'} @{$COLUMN_NAMES}) {
    # we set the 'last_updated' field ourselves
    my $timeformat = '%r %A %B %d %Y'; 
    $values{last_updated} = strftime($timeformat, localtime);
  }

  # execute the INSERT
  my $sql = create_insert_sql($dbh,'ArrayLayout',\%values);
  $dbh->do($sql);
  
  # on error
  if ($dbh->err) {
    warn "Bio::Genex::ArrayLayout::insert_db: SQL=<$sql>, DBI=<$DBI::errstr>";
    return undef;
  }
  my $pkey = fetch_last_id($dbh,'ArrayLayout');
  $self->id($pkey);
  $self->al_pk($pkey);
  return $pkey;
}

sub update_db {
  my ($self,$dbh) = @_;
  assert_dbh($dbh);
  die "Bio::Genex::ArrayLayout::update_db: object not in DB"
    unless defined $self->id() && defined $self->al_pk();

  # we must pre-fetch all the attributes 
  $self->fetch();

  # iterate over the fields and add them to the INSERT
  my %values;
  foreach my $col (@{$COLUMN_NAMES}) {
    no strict 'refs';

    # we don't want Bio::Genex::undefined() to get called
    next unless defined $self->get_attribute($col);

    $values{$col} = $self->$col();
  }

  if (grep {$_ eq 'last_updated'} @{$COLUMN_NAMES}) {
    # we set the 'last_updated' field ourselves
    my $timeformat = '%r %A %B %d %Y'; 
    $values{last_updated} = strftime($timeformat, localtime);
  }

  # execute the UPDATE
  my $WHERE = 'al_pk=' . $dbh->quote($self->al_pk());
  my $sql = create_update_sql($dbh,
			      TABLE=>'ArrayLayout',
			      SET=>\%values,
			      WHERE=>$WHERE);
  $dbh->do($sql);

  # on error
  if ($dbh->err) {
    warn "Bio::Genex::ArrayLayout::update_db: SQL=<$sql>, DBI=<$DBI::errstr>";
    return undef;
  }
  return 1;
}
#
# a workhorse function for retrieving ALL objects of a class
#
sub get_all_objects {
  my ($class) = shift;
  my @objects;
  my $COLUMN2FETCH;
  my $VALUE2FETCH;
  my $pkey_name;
  my $has_args = 0;
  $pkey_name = $class->pkey_name();
  if (ref($_[0]) eq 'HASH') {
    # we were called with an anonymous hash as the first parameter
    # grab it and parse the parameter => value pairs
    my $hashref = shift;
    $has_args = 1;
    $COLUMN2FETCH =  $hashref->{column} if exists $hashref->{column};
    $VALUE2FETCH =  $hashref->{value} if exists $hashref->{value};
    die "Bio::Genex::ArrayLayout::get_all_objects: Must define both 'column' and 'value'" 
      if ((defined $VALUE2FETCH) && not (defined $COLUMN2FETCH)) || 
          ((defined $COLUMN2FETCH) && not (defined $VALUE2FETCH));
  }

  my @ids;

  # using class methods seems indirect, but it deals
  # properly with inheritance
  my $FROM = [$class->table_name()];

  # we fetch *all* columns, so that we can populate the new objects
  my $COLUMNS = ['*'];

  my $dbh = Bio::Genex::current_connection();
  my @args = (COLUMNS=>$COLUMNS, FROM=>$FROM);
  if (defined $COLUMN2FETCH) {
    my $where =  "$COLUMN2FETCH = ". $dbh->quote($VALUE2FETCH);
    push(@args,WHERE=>$where);
  }
  push(@args,LIMIT=>$LIMIT) if defined $LIMIT;
  my $sql = create_select_sql($dbh,@args);
  my $sth = $dbh->prepare($sql) 
    or die "Bio::Genex::ArrayLayout::get_all_objects:\nSQL=<$sql>,\nDBI=<$DBI::errstr>";
  $sth->execute() 
    or die "Bio::Genex::ArrayLayout::get_all_objects:\nSQL=<$sql>,\nDBI=<$DBI::errstr>";

  # if there were no objects, return. decide whether to return an 
  # empty list or an empty arrayref using wantarray
  unless ($sth->rows()) {
    return () if wantarray;
    return []; # if not wantarray
  }

  # we use the 'NAME' attribute of the statement handle to get the
  # list of columns that were fetched.
  my @column_names = @{$sth->{NAME}};
  my $rows = $sth->fetchall_arrayref();
  die "Bio::Genex::ArrayLayout::get_all_objects:\nSQL=<$sql>,\nDBI=<$DBI::errstr>" 
    if $sth->err;
  foreach my $col_ref (@{$rows}) {
    # we create a blank object, and populate it with data ourselves
    my $obj = $class->new();

    # %fetched_attrs is used to track which attributes have
    # already been retrieved from the DB, so that Bio::Genex::undefined
    # doesn't try to fetch them a second time if their value is undef
    my %fetched_attrs;
    for (my $i=0;$i < scalar @column_names; $i++) {
      no strict 'refs';
      my $col = $column_names[$i];
      $obj->$col($col_ref->[$i]);

      # record the column as fetched
      $fetched_attrs{$col}++;
    }
    # store the record of the fetched columns
    $obj->fetched_attr(\%fetched_attrs);
    $obj->fetched(1);

    # now we set the id so that delayed-fetching will work for
    # the OO attributes
    $obj->id($obj->get_attribute("$pkey_name"));
    push(@objects,$obj);
  }
  $sth->finish();

  # decide whether to return a list or an arrayref using wantarray
  return @objects if wantarray;
  return \@objects; # if not wantarray
}

#
# a workhorse function for retrieving multiple objects of a class
#
sub get_objects {
  my ($class) = shift;
  my @objects;
  if (ref($_[0]) eq 'HASH' || scalar @_ == 0) {
    croak("Bio::Genex::ArrayLayout::get_objects called with no ID's, perhaps you meant to use Bio::Genex::ArrayLayout::get_all_objects
");
  } 
  my @ids = @_;
  my $obj;
  foreach (@ids) {
    if ($USE_CACHE && exists $_CACHE{$_}) {
	$obj = $_CACHE{$_};	# use it if it's in the cache
    } else {
	my @args = (id=>$_);
	$obj = $class->new(@args);

	# if the id was bad, $obj will be undefined
	next unless defined $obj;
	$_CACHE{$_} = $obj if $USE_CACHE; # stick it in the cache for later
    }
    push(@objects, $obj);
  }
  # decide whether to return a list or an arrayref using wantarray
  return @objects if wantarray;
  return \@objects; # if not wantarray
}


# ObjectTemplate automagically creates a new() method for us 
# that method invokes $self->initialize() after first setting all 
# parameters specified in invocation
sub initialize {
  my $self = shift;

  # we only need to be concerned with caching and id verification
  # if the user has specified and 'id'.
  my $id = $self->get_attribute('id');
  if (defined $id) {
    # 
    # executive decision: if it's in the cache, use it without
    # checking that the parameters are the same
    return $_CACHE{$id} if $USE_CACHE && 
      defined $id &&
      exists $_CACHE{$id};
  
    # 
    # The object is not in the cache, so now we check whether we've
    # been given a valid id
    #
    my $pkey_name = $self->pkey_name();
    my $dbh = Bio::Genex::current_connection();
    my $FROM = [$self->table_name()];
    my $COLUMNS = [$pkey_name];
    my @args = (COLUMNS=>$COLUMNS, FROM=>$FROM, 
  		WHERE=> $pkey_name . " = '$id'");
    my $sql = create_select_sql($dbh,@args);
    my $count = scalar @{$dbh->selectall_arrayref($sql)};
    die "Bio::Genex::ArrayLayout::initialize: $DBI::errstr" if $dbh->err;
  
    # if there was a problem, return an error to new(), so that 
    # new will return undef to the calling function
    if ($count < 1) {
      warn("Bio::Genex::ArrayLayout::initialize: no DB entries for id: $id");
      return -1 unless $count > 0;
    }
  }

  #
  # now that we know we have a valid id, we can resume initialization
  #

  # we need to initialize these for Bio::Genex::undefined() to work
  $self->fetched(0);		# we have not retrieved data via fetch
  $self->fetched_attr({});	# no attr's have been delayed_fetched

  # actually get the object's data if we've been told to
  if (defined $self->get_attribute('fetch_all')) {
    die "Can't use 'fetch_all' without setting 'id'" unless defined $id;
    $self->fetch();
  }
}

sub fetch {
  my ($self) = @_;

  # recursion in this is bad
  return if $self->fetched();

  # can't fetch without a primary key to lookup the data
  my $pkey = $self->get_attribute('id');
  die "Must define an id for fetch"  unless defined $pkey;

  # we don't want to get into loops in Bio::Genex::undefined()
  $self->fetched(1);

  my $dbh = Bio::Genex::current_connection();

  # we make these method calls instead of hardcoding the values
  # for the purpose of inheritance
  assert_table_defined($dbh,$self->table_name());
  my $sql = create_select_sql($dbh,
                    COLUMNS=>['al_pk', 'con_fk', 'name', 'technology_type', 'identifier_code', 'medium', 'coating', 'default_spot_conc', 'default_spot_conc_units', 'default_spot_conc_error'],
                    FROM=>[$self->table_name()],
                    WHERE=>$self->pkey_name() . " = '$pkey'",
                              );
  my $sth = $dbh->prepare($sql) || die "Bio::Genex::ArrayLayout::initialize: $DBI::errstr";
  $sth->execute() || die "Bio::Genex::ArrayLayout::initialize: $DBI::errstr";

  # sanity check to see if bogus id
  my $ref = $sth->fetchrow_hashref();
  die "ArrayLayout: ", $self->pkey_name(), " $pkey, not in DB"
    unless defined $ref;

  while (my ($key,$val) = each %{$ref}) {
    # no use for storing undef, since all attributes 
    # start as undef
    next unless defined $val;

    # we only want to set attributes that do not already exist
    # for example, we are called by update_db(), we don't want to force
    # users to call fetch() before modifying the object's attributes
    next if defined $self->get_attribute($key);

    { # we use this to temporarily relax the strict pragma
      # to use symbolic references
      no strict 'refs';
      $self->$key($val);
    } # back to our regularily scheduled strictness
  }
  $sth->finish();
}


1;