App-DBCritic
view release on metacpan or search on metacpan
lib/App/DBCritic.pm view on Meta::CPAN
package App::DBCritic;
# ABSTRACT: Critique a database schema for best practices
#pod =head1 SYNOPSIS
#pod
#pod use App::DBCritic;
#pod
#pod my $critic = App::DBCritic->new(
#pod dsn => 'dbi:Oracle:HR', username => 'scott', password => 'tiger');
#pod $critic->critique();
#pod
#pod =head1 DESCRIPTION
#pod
#pod This package is used to scan a database schema and catalog any violations
#pod of best practices as defined by a set of policy plugins. It takes conceptual
#pod and API inspiration from L<Perl::Critic|Perl::Critic>.
#pod
#pod B<dbcritic> is the command line interface.
#pod
#pod This is a work in progress - please see the L</SUPPORT> section below for
#pod information on how to contribute. It especially needs ideas (and
#pod implementations!) of new policies!
#pod
#pod =cut
use strict;
use utf8;
use Modern::Perl '2011'; ## no critic (Modules::ProhibitUseQuotedVersion)
our $VERSION = '0.023'; # VERSION
use Carp;
use English '-no_match_vars';
use List::Util 1.33 'any';
use Module::Pluggable
search_path => [ __PACKAGE__ . '::Policy' ],
sub_name => 'policies',
instantiate => 'new';
#pod =method policies
#pod
#pod Returns an array of loaded policy names that will be applied during
#pod L</critique>. By default all modules under the
#pod C<App::DBCritic::Policy> namespace are loaded.
#pod
#pod =cut
use Moo;
use Scalar::Util 'blessed';
use App::DBCritic::Loader;
for (qw(username password class_name)) { has $_ => ( is => 'ro' ) }
#pod =attr username
#pod
#pod The optional username used to connect to the database.
#pod
#pod =attr password
#pod
#pod The optional password used to connect to the database.
#pod
#pod =attr class_name
#pod
#pod The name of a L<DBIx::Class::Schema|DBIx::Class::Schema> class you wish to
#pod L</critique>.
#pod Only settable at construction time.
#pod
#pod =cut
has dsn => ( is => 'ro', lazy => 1, default => \&_build_dsn );
sub _build_dsn {
my $self = shift;
## no critic (ErrorHandling::RequireUseOfExceptions)
croak 'No schema defined' if not $self->has_schema;
my $dbh = $self->schema->storage->dbh;
## no critic (ValuesAndExpressions::ProhibitAccessOfPrivateData)
return join q{:} => 'dbi', $dbh->{Driver}{Name}, $dbh->{Name};
}
#pod =attr dsn
#pod
#pod The L<DBI|DBI> data source name (required) used to connect to the database.
#pod If no L</class_name> or L</schema> is provided, L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> will then
#pod construct schema classes dynamically to be critiqued.
#pod
#pod =cut
has schema => (
is => 'ro',
coerce => 1,
lazy => 1,
default => \&_build_schema,
coerce => \&_coerce_schema,
predicate => 1,
);
sub _build_schema {
my $self = shift;
my @connect_info = map { $self->$_ } qw(dsn username password);
if ( my $class_name = $self->class_name ) {
return $class_name->connect(@connect_info)
if eval "require $class_name";
}
return _coerce_schema( \@connect_info );
}
sub _coerce_schema {
my $schema = shift;
return $schema if blessed $schema and $schema->isa('DBIx::Class::Schema');
local $SIG{__WARN__} = sub {
if ( $_[0] !~ / has no primary key at /ms ) {
print {*STDERR} $_[0];
}
};
return App::DBCritic::Loader->connect( @{$schema} )
if 'ARRAY' eq ref $schema;
## no critic (ErrorHandling::RequireUseOfExceptions)
croak q{don't know how to make a schema from a } . ref $schema;
}
#pod =attr schema
#pod
#pod A L<DBIx::Class::Schema|DBIx::Class::Schema> object you wish to L</critique>.
#pod Only settable at construction time.
#pod
#pod =attr has_schema
#pod
#pod An attribute predicates that is true or false, depending on whether L</schema>
#pod has been defined.
#pod
#pod =cut
has _elements => ( is => 'ro', lazy => 1, default => \&_build__elements );
sub _build__elements {
my $self = shift;
my $schema = $self->schema;
return {
Schema => [$schema],
ResultSource => [ map { $schema->source($_) } $schema->sources ],
ResultSet => [ map { $schema->resultset($_) } $schema->sources ],
};
}
sub critique {
for ( @{ shift->violations } ) {say}
return;
}
#pod =method critique
#pod
#pod Runs the L</schema> through the C<App::DBCritic> engine using all
#pod the policies that have been loaded and dumps a string representation of
#pod L</violations> to C<STDOUT>.
#pod
lib/App/DBCritic.pm view on Meta::CPAN
has violations => (
is => 'ro',
lazy => 1,
default => sub {
my $self = shift;
[ map { $self->_policy_loop( $_, $self->_elements->{$_} ) }
keys %{ $self->_elements },
];
},
);
#pod =method violations
#pod
#pod Returns an array reference of all
#pod L<App::DBCritic::Violation|App::DBCritic::Violation>s
#pod picked up by the various policies.
#pod
#pod =cut
sub _policy_loop {
my ( $self, $policy_type, $elements_ref ) = @_;
my @violations;
for my $policy ( grep { _policy_applies_to( $_, $policy_type ) }
$self->policies )
{
push @violations, grep {$_}
map { $policy->violates( $_, $self->schema ) } @{$elements_ref};
}
return @violations;
}
sub _policy_applies_to {
my ( $policy, $type ) = @_;
return any { $_ eq $type } @{ $policy->applies_to };
}
1;
__END__
=pod
=encoding UTF-8
=for :stopwords Mark Gardner cpan testmatrix url annocpan anno bugtracker rt cpants
kwalitee diff irc mailto metadata placeholders metacpan
=head1 NAME
App::DBCritic - Critique a database schema for best practices
=head1 VERSION
version 0.023
=head1 SYNOPSIS
use App::DBCritic;
my $critic = App::DBCritic->new(
dsn => 'dbi:Oracle:HR', username => 'scott', password => 'tiger');
$critic->critique();
=head1 DESCRIPTION
This package is used to scan a database schema and catalog any violations
of best practices as defined by a set of policy plugins. It takes conceptual
and API inspiration from L<Perl::Critic|Perl::Critic>.
B<dbcritic> is the command line interface.
This is a work in progress - please see the L</SUPPORT> section below for
information on how to contribute. It especially needs ideas (and
implementations!) of new policies!
=head1 ATTRIBUTES
=head2 username
The optional username used to connect to the database.
=head2 password
The optional password used to connect to the database.
=head2 class_name
The name of a L<DBIx::Class::Schema|DBIx::Class::Schema> class you wish to
L</critique>.
Only settable at construction time.
=head2 dsn
The L<DBI|DBI> data source name (required) used to connect to the database.
If no L</class_name> or L</schema> is provided, L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> will then
construct schema classes dynamically to be critiqued.
=head2 schema
A L<DBIx::Class::Schema|DBIx::Class::Schema> object you wish to L</critique>.
Only settable at construction time.
=head2 has_schema
An attribute predicates that is true or false, depending on whether L</schema>
has been defined.
=head1 METHODS
=head2 policies
Returns an array of loaded policy names that will be applied during
L</critique>. By default all modules under the
C<App::DBCritic::Policy> namespace are loaded.
=head2 critique
Runs the L</schema> through the C<App::DBCritic> engine using all
the policies that have been loaded and dumps a string representation of
L</violations> to C<STDOUT>.
=head2 violations
Returns an array reference of all
L<App::DBCritic::Violation|App::DBCritic::Violation>s
picked up by the various policies.
=head1 SEE ALSO
=over
=item L<Perl::Critic|Perl::Critic>
=item L<DBIx::Class|DBIx::Class>
=item L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader>
=back
=head1 SUPPORT
=head2 Perldoc
You can find documentation for this module with the perldoc command.
( run in 0.955 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )