Anansi-DatabaseComponent
view release on metacpan or search on metacpan
lib/Anansi/DatabaseComponent.pm view on Meta::CPAN
package Anansi::DatabaseComponent;
=head1 NAME
Anansi::DatabaseComponent - A manager template for database drivers.
=head1 SYNOPSIS
package Anansi::Database::Example;
use base qw(Anansi::DatabaseComponent);
sub connect {
my ($self, $channel, %parameters) = @_;
return $self->SUPER::connect(
undef,
INPUT => [
'some text',
{
NAME => 'someParameter',
}, {
INPUT => [
'more text',
{
NAME => 'anotherParameter',
},
'yet more text',
]
}, {
DEFAULT => 'abc',
NAME => 'yetAnotherParameter',
},
],
(%parameters),
);
}
sub validate {
my ($self, $channel, %parameters) = @_;
$parameters{DRIVER} = 'Example';
return Anansi::DatabaseComponent::validate(undef, %parameters);
}
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'AUTOCOMMIT' => 'Anansi::DatabaseComponent::autocommit');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'COMMIT' => 'Anansi::DatabaseComponent::commit');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'CONNECT' => 'connect');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'DISCONNECT' => 'Anansi::DatabaseComponent::disconnect');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'FINISH' => 'Anansi::DatabaseComponent::finish');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'HANDLE' => 'Anansi::DatabaseComponent::handle');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'PREPARE' => 'Anansi::DatabaseComponent::prepare');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'ROLLBACK' => 'Anansi::DatabaseComponent::rollback');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'STATEMENT' => 'Anansi::DatabaseComponent::statement');
Anansi::DatabaseComponent::addChannel('Anansi::Database::Example', 'VALIDATE_AS_APPROPRIATE' => 'validate');
1;
package main;
use Anansi::Database;
my $database = Anansi::Database->new();
my $component = $database->addComponent(undef,
DRIVER => 'Example',
);
if(defined($component)) {
if($database->connect(
undef,
$component,
someParameter => 'some data',
anotherParameter => 'more data',
yetAnotherParameter => 'further data',
)) {
my $result = $database->statement(
undef,
$component,
SQL => 'SELECT someThing FROM someTable where modified = ?;',
INPUT => [
{
NAME => 'modified',
},
],
modified => '2011-02-22 00:21:46',
);
if(!defined($result)) {
} elsif(ref($result) =~ /^ARRAY$/i) {
foreach my $record (@{$result}) {
next if(ref($record) !~ /^HASH$/i);
print 'someThing: "'.${$record}{someThing}.'"'."\n";
}
}
}
}
1;
=head1 DESCRIPTION
Manages a database connection providing generic processes to allow it's opening,
closing and various SQL interactions. Uses L<Anansi::Actor>.
=cut
our $VERSION = '0.05';
use base qw(Anansi::Component);
use Anansi::Actor;
=head1 METHODS
=cut
lib/Anansi/DatabaseComponent.pm view on Meta::CPAN
Attempts to use the supplied parameters to assign values to a SQL statement that
has already been prepared to accept them. Returns B<0> I<(zero)> on failure and
the database statement handle on success.
=cut
sub bind {
my ($self, %parameters) = @_;
return 0 if(!defined($parameters{HANDLE}));
return 0 if(!defined($parameters{INPUT}));
return 0 if(ref($parameters{INPUT}) !~ /^ARRAY$/i);
return 0 if(!defined($parameters{VALUE}));
return 0 if(ref($parameters{VALUE}) !~ /^HASH$/i);
my $index = 1;
foreach my $input (@{$parameters{INPUT}}) {
if(defined(${$parameters{VALUE}}{${$input}{NAME}})) {
if(defined(${$input}{TYPE})) {
$parameters{HANDLE}->bind_param($index, ${$parameters{VALUE}}{${$input}{NAME}}, ${$input}{TYPE});
} else {
$parameters{HANDLE}->bind_param($index, ${$parameters{VALUE}}{${$input}{NAME}});
}
} elsif(defined(${$input}{DEFAULT})) {
if(defined(${$input}{TYPE})) {
$parameters{HANDLE}->bind_param($index, ${$input}{DEFAULT}, ${$input}{TYPE});
} else {
$parameters{HANDLE}->bind_param($index, ${$input}{DEFAULT});
}
} elsif(defined(${$input}{TYPE})) {
$parameters{HANDLE}->bind_param($index, '', ${$input}{TYPE});
} else {
$parameters{HANDLE}->bind_param($index, '');
}
$index++;
}
return $parameters{HANDLE};
}
=head2 binding
if(1 == Anansi::DatabaseComponent::binding($OBJECT));
if(1 == $OBJECT->binding());
=over 4
=item self I<(Blessed Hash B<or> String, Required)>
Either an object or a string of this namespace.
=item parameters I<(Array, Optional)>
An array of hashes. Each hash should contain a I<NAME> key with a string value.
=back
Verifies that the supplied parameters are all hashes and that they each contain
a I<NAME> key with a string value. Returns B<1> I<(one)> when validity is
confirmed and B<0> I<(zero)> when an invalid structure is determined. Used to
validate the I<INPUT> parameter of the B<bind> method.
=cut
sub binding {
my ($self, @parameters) = @_;
foreach my $parameter (@parameters) {
return 0 if(ref($parameter) !~ /^HASH$/i);
return 0 if(!defined(${$parameter}{NAME}));
return 0 if(ref(${$parameter}{NAME}) !~ /^$/);
return 0 if(${$parameter}{NAME} !~ /^[a-zA-Z_]+(\s*[a-zA-Z0-9_]+)*$/);
}
return 1;
}
=head2 commit
if(1 == Anansi::DatabaseComponent::commit($OBJECT, undef));
if(1 == Anansi::DatabaseComponent::channel($OBJECT, 'COMMIT'));
if(1 == $OBJECT->commit(undef));
if(1 == $OBJECT->channel('COMMIT'));
=over 4
=item self I<(Blessed Hash B<or> String, Required)>
Either an object or a string of this namespace.
=item channel I<(String, Required)>
The abstract identifier of a subroutine.
=item parameters I<(Hash, Optional)>
Named parameters.
=back
Attempts to perform a database commit. Returns B<1> I<(one)> on success and
B<0> I<(zero)> on failure.
=cut
sub commit {
my ($self, $channel, %parameters) = @_;
return 0 if(ref($self) =~ /^(|ARRAY|CODE|FORMAT|GLOB|HASH|IO|LVALUE|REF|Regexp|SCALAR|VSTRING)$/i);
return 0 if(!defined($self->{HANDLE}));
return 1 if($self->autocommit());
my $commit;
eval {
$commit = $self->{HANDLE}->commit();
1;
} or do {
$self->rollback();
return 0;
lib/Anansi/DatabaseComponent.pm view on Meta::CPAN
=over 4
=item self I<(Blessed Hash, Required)>
An object of this namespace.
=item channel I<(String, Required)>
The abstract identifier of a subroutine.
=item parameters I<(Hash, Required)>
Named parameters.
=over 4
=item HANDLE I<(DBI::db, Optional)>
The database handle of an existing database connection. Used in preference to
the I<INPUT> parameter.
=item INPUT I<(Array B<or> Scalar, Optional)>
An array or single value containing a description of each parameter in the order
that it is passed to the database driver's I<connect> method. Used when the
I<HANDLE> parameter does not exist.
=over 4
=item I<(Non-Hash)>
An element that does not contain a hash value will be used as the corresponding
I<connect> method's parameter value.
=item I<(Hash)>
An element that contains a hash value is assumed to be a description of how to
generate the corresponding I<connect> method's parameter value. when a value
can not be generated, an B<undef> value will be used.
=over 4
=item DEFAULT I<(Optional)>
The value to use if no other value can be determined.
=item INPUT I<(Array B<or> Scalar, Optional)>
Contains a structure like that given in I<INPUT> above with the exception that
any further I<INPUT> keys will be ignored. As this key is only valid when
I<NAME> is undefined and I<REF> either specifies a string or a hash, it's value
will be either a concatenation of all the calculated strings or a hash
containing all of the specified keys and values.
=item NAME I<(String, Optional)>
The name of the parameter that contains the value to use.
=item REF I<(Array B<or> String, Optional)>
The data types used to validate the value to use.
=back
=back
=back
=back
Either uses an existing database connection or attempts to perform a database
connection using the supplied parameters. Returns B<1> I<(one)> on success and
B<0> I<(zero)> on failure.
=cut
sub connect {
my ($self, $channel, %parameters) = @_;
return 0 if(ref($self) =~ /^(|ARRAY|CODE|FORMAT|GLOB|HASH|IO|LVALUE|REF|Regexp|SCALAR|VSTRING)$/i);
$self->disconnect();
if(defined($parameters{HANDLE})) {
return 0 if(ref($parameters{HANDLE}) !~ /^DBI::db$/);
$self->{HANDLE} = $parameters{HANDLE};
$self->{MANAGE_HANDLE} = 0;
} elsif(!defined($parameters{INPUT})) {
return 0;
} elsif(ref($parameters{INPUT}) !~ /^ARRAY$/i) {
return 0;
} else {
my @inputs;
foreach my $input (@{$parameters{INPUT}}) {
if(ref($input) !~ /^HASH$/i) {
push(@inputs, $input);
next;
}
my $value = undef;
$value = ${$input}{DEFAULT} if(defined(${$input}{DEFAULT}));
if(!defined(${$input}{NAME})) {
if(!defined(${$input}{INPUT})) {
} elsif(ref(${$input}{INPUT}) !~ /^ARRAY$/i) {
} elsif(!defined(${$input}{REF})) {
} elsif(ref(${$input}{REF}) !~ /^$/i) {
} elsif('' eq ${$input}{REF}) {
my @subInputs;
for(my $index = 0; $index < scalar(@{${$input}{INPUT}}); $index++) {
if(ref(${${$input}{INPUT}}[$index]) =~ /^$/i) {
push(@subInputs, ${${$input}{INPUT}}[$index]);
next;
} elsif(ref(${${$input}{INPUT}}[$index]) !~ /^HASH$/) {
next;
}
my $subValue = '';
$subValue = ${${${$input}{INPUT}}[$index]}{DEFAULT} if(defined(${${${$input}{INPUT}}[$index]}{DEFAULT}));
if(!defined(${${${$input}{INPUT}}[$index]}{NAME})) {
} elsif(ref(${${${$input}{INPUT}}[$index]}{NAME}) !~ /^$/) {
} elsif(defined($parameters{${${${$input}{INPUT}}[$index]}{NAME}})) {
if(!defined(${${${$input}{INPUT}}[$index]}{REF})) {
$subValue = $parameters{${${${$input}{INPUT}}[$index]}{NAME}} if('' eq ref($parameters{${${${$input}{INPUT}}[$index]}{NAME}}));
} elsif(ref(${${${$input}{INPUT}}[$index]}{REF}) !~ /^$/) {
} elsif('' ne ${${${$input}{INPUT}}[$index]}{REF}) {
lib/Anansi/DatabaseComponent.pm view on Meta::CPAN
} else {
eval {
$handle = $self->{HANDLE}->prepare($parameters{SQL});
1;
} or do {
$self->rollback();
return 0;
};
my $questionMarks = $parameters{SQL};
my $questionMarks = $questionMarks =~ s/\?/$1/sg;
if(0 == $questionMarks) {
if(defined($parameters{INPUT})) {
$self->rollback();
return 0;
}
} elsif(!defined($parameters{INPUT})) {
$self->rollback();
return 0;
} elsif(ref($parameters{INPUT}) !~ /^ARRAY$/i) {
$self->rollback();
return 0;
} elsif(scalar(@{$parameters{INPUT}}) != $questionMarks) {
$self->rollback();
return 0;
} else {
if(!$self->bind(
HANDLE => $handle,
INPUT => $parameters{INPUT},
VALUE => \%parameters,
)) {
$self->rollback();
return 0;
}
}
}
eval {
$handle->execute();
1;
} or do {
$handle->rollback();
return 0;
};
if(!defined($handle->{NUM_OF_FIELDS})) {
return 1;
} elsif(undef == $handle->{NUM_OF_FIELDS}) {
return 1;
} elsif(0 == $handle->{NUM_OF_FIELDS}) {
return 1;
}
my $result = [];
while(my $row = $handle->fetchrow_hashref()) {
push(@{$result}, $row);
}
$handle->finish() if(!$prepared);
return $result;
}
Anansi::DatabaseComponent::addChannel('Anansi::DatabaseComponent', 'STATEMENT' => 'statement');
=head2 validate
if(1 == Anansi::DatabaseComponent::validate($OBJECT, undef));
if(1 == Anansi::DatabaseComponent::channel($OBJECT, 'VALIDATE_AS_APPROPRIATE'));
if(1 == Anansi::DatabaseComponent->validate(undef, DRIVERS => ['some::driver::module', 'anotherDriver']));
if(1 == Anansi::DatabaseComponent->channel('VALIDATE_AS_APPROPRIATE'));
if(1 == $OBJECT->validate(undef, DRIVER => 'Example'));
if(1 == $OBJECT->channel('VALIDATE_AS_APPROPRIATE', DRIVER => 'Example'));
if(1 == Anansi::DatabaseComponent->validate(undef, DRIVER => 'Example', DRIVERS => 'some::driver'));
if(1 == Anansi::DatabaseComponent->channel('VALIDATE_AS_APPROPRIATE', DRIVER => 'Example'));
=over 4
=item self I<(Blessed Hash B<or> String, Required)>
Either an object or a string of this namespace.
=item channel I<(String, Required)>
The abstract identifier of a subroutine.
=item parameters I<(Hash, Optional)>
Named parameters.
=over 4
=item DRIVER I<(String, Optional)>
Either the namespace of a database driver or the name of a database driver that
should be used.
=item DRIVERS I<(Array B<or> String, Optional)>
An array of strings or a single string containing either the namespace of a
valid database driver or the name of a database driver that should be looked for
among the installed modules.
=item HANDLE I<DBI::db, Optional>
An existing database connection handle.
=back
=back
Generic validation for whether a database should be handled by a component. If
the driver name is supplied then an attempt will be made to use that driver as
long as it matches any of the acceptable B<DRIVERS>, otherwise one of the
acceptable B<DRIVERS> will be tried or a generic driver if none have been
supplied. Returns B<1> I<(one)> for valid and B<0> I<(zero)> for invalid.
=cut
sub validate {
my ($self, $channel, %parameters) = @_;
my $package = $self;
$package = ref($self) if(ref($self) !~ /^$/);
my %modules = Anansi::Actor->modules();
return 0 if(!defined($modules{'Bundle::DBI'}));
my $HANDLE_DRIVER;
if(defined($parameters{HANDLE})) {
return 0 if(ref($parameters{HANDLE}) !~ /^DBI::db$/);
my $driver = $parameters{HANDLE}->get_info(17);
return 0 if(!defined($driver));
$HANDLE_DRIVER = $driver;
}
if(!defined($parameters{DRIVER})) {
if(defined($parameters{DRIVERS})) {
$parameters{DRIVERS} = [( $parameters{DRIVERS} )] if(ref($parameters{DRIVERS}) =~ /^$/);
return 0 if(ref($parameters{DRIVERS}) !~ /^ARRAY$/i);
if(defined($HANDLE_DRIVER)) {
foreach my $DRIVER (@{$parameters{DRIVERS}}) {
return 0 if(ref($DRIVER) !~ /^$/);
return 1 if($DRIVER eq $HANDLE_DRIVER);
}
} else {
my %reduced = map { lc($_) => $modules{$_} } (keys(%modules));
foreach my $DRIVER (@{$parameters{DRIVERS}}) {
return 0 if(ref($DRIVER) !~ /^$/);
return 1 if(defined($modules{$DRIVER}));
return 1 if(defined($modules{'DBD::'.$DRIVER}));
return 1 if(defined($modules{'Bundle::DBD::'.$DRIVER}));
return 1 if(defined($reduced{lc($DRIVER)}));
return 1 if(defined($reduced{lc('DBD::'.$DRIVER)}));
return 1 if(defined($reduced{lc('Bundle::DBD::'.$DRIVER)}));
}
}
return 0;
} elsif(defined($HANDLE_DRIVER)) {
} elsif(!defined($modules{'Bundle::DBD'})) {
return 0;
}
} elsif(ref($parameters{DRIVER}) !~ /^$/) {
return 0;
} elsif(defined($parameters{DRIVERS})) {
$parameters{DRIVERS} = [( $parameters{DRIVERS} )] if(ref($parameters{DRIVERS}) =~ /^$/);
return 0 if(ref($parameters{DRIVERS}) !~ /^ARRAY$/i);
my %DRIVERS;
$DRIVERS{$parameters{DRIVER}} = 1;
$DRIVERS{'DBD::'.$parameters{DRIVER}} = 1;
$DRIVERS{'Bundle::DBD::'.$parameters{DRIVER}} = 1;
$DRIVERS{lc($parameters{DRIVER})} = 1;
$DRIVERS{lc('DBD::'.$parameters{DRIVER})} = 1;
$DRIVERS{lc('Bundle::DBD::'.$parameters{DRIVER})} = 1;
my $found = 0;
foreach my $DRIVER (@{$parameters{DRIVERS}}) {
return 0 if(ref($DRIVER) !~ /^$/);
$found = 1;
last if(defined($DRIVERS{$DRIVER}));
last if(defined($DRIVERS{'DBD::'.$DRIVER}));
last if(defined($DRIVERS{'Bundle::DBD::'.$DRIVER}));
last if(defined($DRIVERS{lc($DRIVER)}));
last if(defined($DRIVERS{lc('DBD::'.$DRIVER)}));
last if(defined($DRIVERS{lc('Bundle::DBD::'.$DRIVER)}));
$found = 0;
}
return 0 if(!$found);
if(defined($HANDLE_DRIVER)) {
foreach my $DRIVER (@{$parameters{DRIVERS}}) {
return 0 if(ref($DRIVER) !~ /^$/);
return 1 if($DRIVER eq $HANDLE_DRIVER);
}
} else {
my %reduced = map { lc($_) => $modules{$_} } (keys(%modules));
foreach my $DRIVER (@{$parameters{DRIVERS}}) {
return 1 if(defined($modules{$DRIVER}));
return 1 if(defined($modules{'DBD::'.$DRIVER}));
return 1 if(defined($modules{'Bundle::DBD::'.$DRIVER}));
return 1 if(defined($reduced{lc($DRIVER)}));
return 1 if(defined($reduced{lc('DBD::'.$DRIVER)}));
return 1 if(defined($reduced{lc('Bundle::DBD::'.$DRIVER)}));
}
}
return 0;
} elsif(defined($modules{$parameters{DRIVER}})) {
if(defined($HANDLE_DRIVER)) {
return 0 if($parameters{DRIVER} ne $HANDLE_DRIVER);
}
} elsif(defined($modules{'DBD::'.$parameters{DRIVER}})) {
if(defined($HANDLE_DRIVER)) {
return 0 if('DBD::'.$parameters{DRIVER} ne $HANDLE_DRIVER);
}
} elsif(!defined($modules{'Bundle::DBD::'.$parameters{DRIVER}})) {
my %reduced = map { lc($_) => $modules{$_} } (keys(%modules));
if(defined($reduced{lc($parameters{DRIVER})})) {
if(defined($HANDLE_DRIVER)) {
return 0 if(lc($parameters{DRIVER}) ne lc($HANDLE_DRIVER));
}
} elsif(defined($reduced{lc('DBD::'.$parameters{DRIVER})})) {
if(defined($HANDLE_DRIVER)) {
return 0 if(lc('DBD::'.$parameters{DRIVER}) ne lc($HANDLE_DRIVER));
}
} elsif(defined($reduced{lc('Bundle::DBD::'.$parameters{DRIVER})})) {
if(defined($HANDLE_DRIVER)) {
return 0 if(lc('Bundle::DBD::'.$parameters{DRIVER}) ne lc($HANDLE_DRIVER));
}
} else {
return 0;
}
}
return 1;
}
Anansi::DatabaseComponent::addChannel('Anansi::DatabaseComponent', 'VALIDATE_AS_APPROPRIATE' => 'validate');
=head1 NOTES
This module is designed to make it simple, easy and quite fast to code your
design in perl. If for any reason you feel that it doesn't achieve these goals
then please let me know. I am here to help. All constructive criticisms are
also welcomed.
=cut
=head1 AUTHOR
Kevin Treleaven <kevin I<AT> treleaven I<DOT> net>
=cut
1;
( run in 0.836 second using v1.01-cache-2.11-cpan-e93a5daba3e )