The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package CGI::AppToolkit::Data::Object;

# Copyright 2002 Robert Giseburt. All rights reserved.
# This library is free software; you can redistribute it
# and/or modify it under the same terms as Perl itself.

# Email: rob@heavyhosting.net

$CGI::AppToolkit::Data::Object::VERSION = '0.05';

use Carp;
use strict;
use vars qw/$AUTOLOAD/;

#-------------------------------------#
# OO Custructor                       #
#-------------------------------------#


sub new {
	my $type = shift;
	my $self = bless _clean_vars(@_), $type;
	
	if ($self->can('preinit')) {
		$self->preinit()
	}
	$self->init() || return undef;
	
	$self
}


#-------------------------------------#
# OO Methods                          #
#-------------------------------------#

# prefetch -- return a [$sth, $order] and {$values}


sub init {
	my $self = shift;
	
	#set:
	# table
	
	1; # MUST return true
}


#-------------------------------------#

# fetch some objects
sub fetch {
	my $self = shift;
	my $args = ref $_[0] eq 'HASH' ? shift : {@_};
	
	
}

#-------------------------------------#

# fetch an object
sub fetch_one {
	my $self = shift;
	my $args = ref $_[0] eq 'HASH' ? shift : {@_};

	$args->{'-one'} = 1;
	
	$self->fetch($args);	
}
*fetch_row = \&fetch_one;

#-------------------------------------#

# store an object
sub store {
	my $self = shift;

}


#-------------------------------------#

# update an object
sub update {
	my $self = shift;

}


#-------------------------------------#

# delete an object
sub delete {
	my $self = shift;

}


#-------------------------------------#

# cleanup an object
sub cleanup {
	my $self = shift;
	
	return shift;
}


#-------------------------------------#

# get a prepared db statement
sub db_statement {
	my $self = shift;
	my $name = shift;

}


#-------------------------------------#

# AUTOLOAD
sub AUTOLOAD {
	#my $self = shift;

	my $name = $AUTOLOAD;
	$name =~ s/.*://;	# strip fully-qualified portion
	
	return if $name eq 'DESTROY';

	my $name_lc = lc $name;
	if ($name_lc =~ /^get_(.*)/) {
		$name_lc = $1;
		eval <<"END_SUB" || croak "AUTOLOAD '$AUTOLOAD' failed: \"$@\"";
			sub $name {
				my \$self = shift;
				return \$self->{'$name_lc'} || undef;
			}
			1;
END_SUB
	} elsif ($name_lc =~ /^set_(.*)/) {
		$name_lc = $1;
		eval <<"END_SUB" || croak "AUTOLOAD '$AUTOLOAD' failed: \"$@\"";
			sub $name {
				my \$self = shift;
				if (\@_) {
					return \$self->{'$name_lc'} = shift;
				}
			}
			1;
END_SUB
	}
	
	no strict 'refs';
	goto &{$name};
}


#-------------------------------------#
# Inherited Non-Interface Methods     #
#-------------------------------------#


sub _new_error {
	shift; # ditch $self
	return CGI::AppToolkit::Data::Object::Error->new();
}


#-------------------------------------#
# Non-OO Methods                      #
#-------------------------------------#


sub _clean_vars {
	my $vars = ref $_[0] eq 'HASH' ? shift : {@_};
	
	foreach my $key (keys %$vars) {
		my $oldkey = $key;
		$key =~ s/^-//;
		$key = lc $key;
		$vars->{$key} = delete $vars->{$oldkey} if $key ne $oldkey;
	}
	
	return $vars;
}

1;

package CGI::AppToolkit::Data::Object::Error;

#-------------------------------------#
# OO Custructor                       #
#-------------------------------------#


sub new {
	my $type = shift;
	my $self = bless CGI::AppToolkit::Data::Object::_clean_vars(@_), $type;
	
	$self->{'errors'} = [];
	$self->{'wrong'} = [];
	$self->{'missing'} = [];	
	
	$self
}


#-------------------------------------#
# OO Methods                          #
#-------------------------------------#


sub error {
	my $self = shift;
	my $error = shift;
	
	push @{$self->{'errors'}}, {'text' => $error};
}


#-------------------------------------#


sub wrong {
	my $self = shift;
	my $wrong = shift;
	
	push @{$self->{'wrong'}}, $wrong;
}


#-------------------------------------#


sub missing {
	my $self = shift;
	my $missing = shift;
	
	push @{$self->{'missing'}}, $missing;
}


#-------------------------------------#


sub has_errors {
	my $self = shift;
	
	scalar @{$self->{'errors'}} || scalar @{$self->{'missing'}} || scalar @{$self->{'wrong'}}
}


#-------------------------------------#


sub get {
	my $self = shift;
	
	($self->{'errors'}, $self->{'wrong'}, $self->{'missing'})
}

1;

__DATA__

=head1 NAME

B<CGI::AppToolkit::Data::Object> - A data source component of L<B<CGI::AppToolkit>|CGI::AppToolkit>

=head1 DESCRIPTION

B<CGI::AppToolkit::Data::Object>s provide a common interface to multiple data sources. The data sources are provided by B<CGI::AppToolkit::Data::Object> decendants that you create, generally on a per-project basis. Providing a data source requires creating an object in the B<CGI::AppToolkit::Data::> namespace that inherits from B<CGI::AppToolkit::Data::Object>.

You B<do not> C<use> this module or it's descendants in your code directly, but instead call B<CGI::AppToolkit-E<gt>data()> to load it for you.

=head2 USAGE

For a B<Person> object, you might start the module like this:

  package CGI::AppToolkit::Data::Person;
  
  use CGI::AppToolkit::Data::Object;
  use strict;
  
  @CGI::AppToolkit::Data::Person::ISA = qw/CGI::AppToolkit::Data::Object/;

After that, you simply have to override four subroutines: B<fetch>, B<store>, B<delete>, and B<update>. All of these are called with two parameters: the object, of course, and the arguments. The arguments are passed in a single parameter which is usually a hashref.

  sub store { # or fetch, update, or delete
    my $self = shift;
    my $args = shift;
    
	# args is usually a hashref, so you would use it like this
	my $id = $args->{'id'};
	
	#... do the actual storing and such here
  }

There is nothing forcing you to implement all of these subroutines. For example, if you are implementing a read-only object, then you could override only B<fetch>. 

Conventionally a relational database is used, but there's nothing forcing you to that either. The data can come from any source at all. However, if you data I<is> coming from a DBI accessed RDBMS that uses SQL, then you should take a look at L<B<CGI::AppToolkit::Data::SQLObject>|CGI::AppToolkit::Data::SQLObject> and L<B<CGI::AppToolkit::Data::Automorph>|CGI::AppToolkit::Data::Automorph>. B<CGI::AppToolkit::Data::SQLObject> handles a few of the common DBI tasks for you, and it's descendant B<CGI::AppToolkit::Data::Automorph> attempts to handle the rest of them.

=head2 METHODS

B<CGI::AppToolkit::Data::Object> provides several convenience methods to inherit.

=over 4

=item B<get_kit()>

Returns the creating B<CGI::AppToolkit> object. This can be used to retrieve required data.

  my $dbi = $self->get_kit()->get_dbi();

The B<CGI::AppToolkit> object has an autoload mechanism that provides all variables that are passed to it's B<new()> method as method calls, with B<get_> or B<set_> added to the beginning to retrieve or set the value, repectively.

In particular, B<CGI::AppToolkit-E<gt>get_dbi()> retrieves the DBI object stored from a call to B<CGI::AppToolkit-E<gt>connect()>.

=item B<_new_error()>

Returns a new, empty L<B<CGI::AppToolkit::Data::Object::Error>|"CGI::AppToolkit::Data::Object::Error"> object, as described in detail below.

=item B<AUTOLOAD>

Using the built-in AUTOLOAD mechanism, you can retrieve and set object variables with named method calls. These method names are B<not> case sensitive.

  # setting
  $self->set_wierd_variable($value);
  
  # retrieving
  my $value = $self->get_wierd_variable();

=back

=head1 CGI::AppToolkit::Data::Object::Error

=head2 DESCRIPTION

CGI::AppToolkit::Data::Object::Error provides a simple interface to errors returned by B<Data->fetch()>. This class seperates errors into three classes: plain text errors (B<errors>), missing items (B<missing>), and wrong items (B<wrong>). Plain text errors are for sending back to the interface. Missing items and wrong items are both lists of keys that were missing or wrong in the args provided to B<store>.

=head2 METHODS

The following methods are for use by scripts using B<CGI::AppToolkit::Data> and will only need to retrieve errors.

=over 4

=item B<has_errors()>

Return nonzero if there are errors and zero if there are not.

=item B<get()>

Returns three arrayrefs: errors, missing, and wrong, in that order. The errors arrayref points to an array of hashes of the form C<{'text' =E<gt> $error}>. The other two arrayrefs point to arrays of strings.

  # as called in a script using CGI::AppToolkit
  # $of is an instance of CGI::AppToolkit
  my $ret = $CGI::AppToolkit->data('person')->store(\%person);
  if (ref $ret =~ /Error/) {
    my ($errors_a, $missing_a, $wrong_a) = $ret->get();
	# ...
  }

=back

The following methods are for use inside B<CGI::AppToolkit::Data::Object> descendants. 

=over 4

=item B<error(>I<ERROR>B<)>

Adds an error with the text I<ERROR> to the error object.

  $error->error('You screwed up, dude!');

=item B<missing(>I<ITEM>B<)>

=item B<wrong(>I<ITEM>B<)>

Adds a missing or wrong I<ITEM> to the error object.

  $error->missing('address1'); # missing a required field
  $error->wrong('email'); # malformed email address

=back

=head1 AUTHOR

Copyright 2002 Robert Giseburt (rob@heavyhosting.net).  All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Please visit http://www.heavyhosting.net/AppToolkit/ for complete documentation.

=cut