The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Tie::DBI::ER;

# $Id: ER.pm 1049 2003-12-08 21:10:33Z jonasbn $

use Tie::DBI;

use strict qw(vars);
use warnings;

use vars qw(@ISA $VERSION);

$VERSION = '1.08';
@ISA = qw(Tie::DBI);

sub TIEHASH {
    my ($class, $hash, $struct) = @_;
    my $self = $class->SUPER::TIEHASH($hash);
    bless $self, $class;
    if (UNIVERSAL::isa($struct, 'HASH') && %$struct) {
        $self->{'struct'} = $struct;
    }
    $self;
}

sub FETCH {
    my ($self, $key) = @_;
    my $val = $self->SUPER::FETCH($key);
    return undef unless ($val);
    my $struct = $self->{'struct'};
    return $val unless ($struct);
    my %stub;
    tie %stub, Tie::DBI::ER::Datum, tied(%$val), $struct;
    \%stub;
}

sub STORE {
    my ($self, $key, $val) = @_;
    my $struct = $self->{'struct'} || {};
    my %hash = %$val;
    foreach (keys %$struct) {
        my $collection = $struct->{$_};
        my $obj = $val->{$_};
        my $index = $obj->{$self->{'key'}};
        $collection->{$index} = $obj # Prevent inifinite loop
            unless ($collection->{$index} == $obj);
        $hash{$_} = $index;
    }
    $self->SUPER::STORE($key, \%hash);
}

package Tie::DBI::ER::Datum;

use vars qw($AUTOLOAD);

sub TIEHASH {
    my ($class, $stub, $struct) = @_;
    bless {'stub' => $stub, 'struct' => $struct}, $class;
}

sub FETCH {
    my ($self, $key) = @_;
    my $val = $self->{'stub'}->FETCH($key);
    my $collection = $self->{'struct'}->{$key};
    $val = $collection->{$val} if (UNIVERSAL::isa($collection, 'HASH'));
    $val;
}

sub STORE {
    my ($self, $key, $val) = @_;
    my $collection = $self->{'struct'}->{$key};
    $val = $val->{$self->{'stub'}->{'key'}} 
		if (UNIVERSAL::isa($collection, 'HASH'));
    $self->{'stub'}->STORE($key, $val);
}

sub AUTOLOAD {
    my $method = (split(/::/, $AUTOLOAD))[-1];
    my $package = substr($AUTOLOAD, 0, -(length($method) + 2));
    my $code = <<EOM;
        package $package;

        sub ${package}::$method {
            my \$self = shift;
            my \$stub = \$self->{'stub'};
            \$stub->$method(\@_);
        }
EOM
    $@ = undef;
    eval $code;
    die $@ if ($@);
    goto &$AUTOLOAD;
}