janjan has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,
I'm writing a module that gets all of its information via calls to a database. Is there any point in using AUTOLOAD? I can understand using AUTOLOAD if I'm setting default values for all of the attributes in my constructor, but I'm not doing this, since I don't know what my default values will be until I talk to the database.

Most of my accessor methods (for instance) will look something like this:
sub first_name { my ($self, $id) = @_; unless (defined $self->{'first_name'}{$id} $self->_get_info_from_database; } return $self->{'first_name'}{$id}; }
$self->_get_info_from_database, here, would be a method that reads in a particular table and stores information about the object in $self -- like $self->{'first_name'}{$id}, $self->{'last_name'}{$id}, and so on. That way, the database call only has to be made once per invocation of the program.

I'd really like to be able to use AUTOLOAD or something like it, since the application I'm writing is fairly large, and writing all those get and set methods is a big pain. Is there any way of making AUTOLOAD recognize that when I want to set first_name, for instance, what I really want to do is store a value in the database, or even call another method that I've written to accomplish this? If not, it doesn't seem like AUTOLOAD would be terribly useful for my purposes.

Any ideas, brainstorms, or pointers to CPAN modules appreciated.

Replies are listed 'Best First'.
Re: Point of AUTOLOAD in a database environment?
by robartes (Priest) on Nov 14, 2002 at 19:46 UTC
    There's a good description of something like this in the_damian's book. The key is that when a method call falls into AUTOLOAD, the $AUTOLOAD variable is set to the name of the method that was called. You then basically do something like:
    sub AUTOLOAD { my $self=shift; my $attr=$AUTOLOAD; unless (exists $allowed_attrs{$attr}) die "Hey, that attribute does +not exist!\n"; # %allowed_attrs lists the attributes you are allowed to access unless (exists $self->{$attr}) do_get_the_value_from_db(); # do_... +would get the value and store it in $self->{$attr}; return $self->{$attr}; }
    The above code (which is untested, so be warned) implements read accessor type functions. Possibilities explored in the book mentioned include expanding it to get/set accessors and "caching" the function by playing symbol table games, so you only have to go once through the expensive AUTOLOAD mechanism.

    CU
    Robartes-

    Update: To change this to a set type accessor, just change do_get_... etc. to a call to your function that sets the value in the DB (the value is passed in as an argument). You can also define accessor names like set_value and filter out the set_ with a regexp in the AUTOLOAD method.

      A particularly cool variation on this theme, is when you have different 'levels' of data, with increasing cost of retrieval from your datastore.

      For example, assume you have a database of people, with very basic data bout them stored in a "People" table, one row per person, keyed on SSN. Other less frequently data is stored in a variety of other tables (ie: all of their grades from every class they ever took is stored in some table keyed by SSN & year; their current & historic clothing sizes are another table keyed by SSN, year & clothing-type; etc..). And still more data about the person is very aggregate in nature, and requires 'expensive' computation (ie: their grade to waste size ratio for each year)

      If you typically only care about the "core" data on a person, and occasionally care about their clothing sizes, and very infrequently care about *ALL* of the data you have on them, then you can divide up the data by how easy/fast it can be retrieved, and change your %allowed_attrs hash into an %attr_populator hash, that stores a method ref you can call to retrive data at that level -- if it hasn't allready been retrived. so in the common case, you are only calling the very fast _get_core_data() method, but when neccessary you call the more obscure _get_clothing_sizes() method.

      Something along the lines of...

      %attr_populator = { name => \_get_core_data, bday => \_get_core_data, sizes => \_get_clothing_sizes_data, grades => \_get_grade_data, complex => \_get_really_expensive_data, }; sub AUTOLOAD { my $self=shift; my $attr=$AUTOLOAD; unless (exists $attr_populator{$attr}) die "Hey, that attribute does + not exist!\n"; &{$att_populator{$attr}}($self) unless (exists $self->{$attr}) return $self->{$attr}; }
Re: Point of AUTOLOAD in a database environment?
by princepawn (Parson) on Nov 14, 2002 at 19:39 UTC
    There are several beasts to ease your burden:
    1. Alzabo would do this sort of thing with its Alzabo::MethodMaker class
    2. DBIx::Recordset is a completely different approach to the issue.

    Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality