| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::DBCritic; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 7 |  |  | 7 |  | 678370 | use strict; | 
|  | 7 |  |  |  |  | 12 |  | 
|  | 7 |  |  |  |  | 238 |  | 
| 4 | 7 |  |  | 7 |  | 3402 | use utf8; | 
|  | 7 |  |  |  |  | 53 |  | 
|  | 7 |  |  |  |  | 33 |  | 
| 5 | 7 |  |  | 7 |  | 235 | use Modern::Perl '2011';    ## no critic (Modules::ProhibitUseQuotedVersion) | 
|  | 7 |  |  |  |  | 8 |  | 
|  | 7 |  |  |  |  | 47 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.021';     # TRIAL VERSION | 
| 8 | 7 |  |  | 7 |  | 1011 | use Carp; | 
|  | 7 |  |  |  |  | 9 |  | 
|  | 7 |  |  |  |  | 415 |  | 
| 9 | 7 |  |  | 7 |  | 1608 | use English '-no_match_vars'; | 
|  | 7 |  |  |  |  | 7136 |  | 
|  | 7 |  |  |  |  | 44 |  | 
| 10 | 7 |  |  | 7 |  | 6150 | use List::MoreUtils 'any'; | 
|  | 7 |  |  |  |  | 6460 |  | 
|  | 7 |  |  |  |  | 594 |  | 
| 11 |  |  |  |  |  |  | use Module::Pluggable | 
| 12 | 7 |  |  |  |  | 57 | search_path => [ __PACKAGE__ . '::Policy' ], | 
| 13 |  |  |  |  |  |  | sub_name    => 'policies', | 
| 14 | 7 |  |  | 7 |  | 3425 | instantiate => 'new'; | 
|  | 7 |  |  |  |  | 52446 |  | 
| 15 | 7 |  |  | 7 |  | 5085 | use Moo; | 
|  | 7 |  |  |  |  | 78504 |  | 
|  | 7 |  |  |  |  | 44 |  | 
| 16 | 7 |  |  | 7 |  | 9922 | use Scalar::Util 'blessed'; | 
|  | 7 |  |  |  |  | 74 |  | 
|  | 7 |  |  |  |  | 417 |  | 
| 17 | 7 |  |  | 7 |  | 2781 | use App::DBCritic::Loader; | 
|  | 7 |  |  |  |  | 24 |  | 
|  | 7 |  |  |  |  | 49 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | for (qw(username password class_name)) { has $ARG => ( is => 'ro' ) } | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | has dsn => ( is => 'ro', lazy => 1, default => \&_build_dsn ); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub _build_dsn { | 
| 24 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | ## no critic (ValuesAndExpressions::ProhibitAccessOfPrivateData) | 
| 27 | 0 |  |  |  |  | 0 | my $dbh = $self->schema->storage->dbh; | 
| 28 | 0 |  |  |  |  | 0 | return join q{:} => 'dbi', $dbh->{Driver}{Name}, $dbh->{Name}; | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | has schema => ( | 
| 32 |  |  |  |  |  |  | is      => 'ro', | 
| 33 |  |  |  |  |  |  | coerce  => 1, | 
| 34 |  |  |  |  |  |  | lazy    => 1, | 
| 35 |  |  |  |  |  |  | default => \&_build_schema, | 
| 36 |  |  |  |  |  |  | coerce  => \&_coerce_schema, | 
| 37 |  |  |  |  |  |  | ); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | sub _build_schema { | 
| 40 | 1 |  |  | 1 |  | 446 | my $self = shift; | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 1 |  |  |  |  | 3 | my @connect_info = map { $self->$ARG } qw(dsn username password); | 
|  | 3 |  |  |  |  | 418 |  | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 1 | 50 |  |  |  | 6 | if ( my $class_name = $self->class_name ) { | 
| 45 | 0 | 0 |  |  |  | 0 | return $class_name->connect(@connect_info) | 
| 46 |  |  |  |  |  |  | if eval "require $class_name"; | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 1 |  |  |  |  | 5 | return _coerce_schema( \@connect_info ); | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub _coerce_schema { | 
| 53 | 7 |  |  | 7 |  | 2266744 | my $schema = shift; | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 7 | 100 | 66 |  |  | 466 | return $schema if blessed $schema and $schema->isa('DBIx::Class::Schema'); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | local $SIG{__WARN__} = sub { | 
| 58 | 0 | 0 |  | 0 |  | 0 | if ( $ARG[0] !~ / has no primary key at /ms ) { | 
| 59 | 0 |  |  |  |  | 0 | print {*STDERR} $ARG[0]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 60 |  |  |  |  |  |  | } | 
| 61 | 1 |  |  |  |  | 8 | }; | 
| 62 | 1 | 50 |  |  |  | 4 | return App::DBCritic::Loader->connect( @{$schema} ) | 
|  | 1 |  |  |  |  | 15 |  | 
| 63 |  |  |  |  |  |  | if 'ARRAY' eq ref $schema; | 
| 64 |  |  |  |  |  |  | ## no critic (ErrorHandling::RequireUseOfExceptions) | 
| 65 | 0 |  |  |  |  | 0 | croak q{don't know how to make a schema from a } . ref $schema; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | has _elements => ( is => 'ro', lazy => 1, default => \&_build__elements ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | sub _build__elements { | 
| 71 | 5 |  |  | 5 |  | 1814 | my $self   = shift; | 
| 72 | 5 |  |  |  |  | 20 | my $schema = $self->schema; | 
| 73 |  |  |  |  |  |  | return { | 
| 74 | 7 |  |  |  |  | 283 | Schema       => [$schema], | 
| 75 | 7 |  |  |  |  | 3031 | ResultSource => [ map { $schema->source($ARG) } $schema->sources ], | 
| 76 | 5 |  |  |  |  | 1941 | ResultSet    => [ map { $schema->resultset($ARG) } $schema->sources ], | 
| 77 |  |  |  |  |  |  | }; | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub critique { | 
| 81 | 1 |  |  | 1 | 1 | 2 | for ( @{ shift->violations } ) {say} | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 82 | 1 |  |  |  |  | 32 | return; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | has violations => ( | 
| 86 |  |  |  |  |  |  | is      => 'ro', | 
| 87 |  |  |  |  |  |  | lazy    => 1, | 
| 88 |  |  |  |  |  |  | default => sub { | 
| 89 |  |  |  |  |  |  | my $self = shift; | 
| 90 |  |  |  |  |  |  | [   map { $self->_policy_loop( $ARG, $self->_elements->{$ARG} ) } | 
| 91 |  |  |  |  |  |  | keys %{ $self->_elements }, | 
| 92 |  |  |  |  |  |  | ]; | 
| 93 |  |  |  |  |  |  | }, | 
| 94 |  |  |  |  |  |  | ); | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | sub _policy_loop { | 
| 97 | 15 |  |  | 15 |  | 164 | my ( $self, $policy_type, $elements_ref ) = @_; | 
| 98 | 15 |  |  |  |  | 25 | my @violations; | 
| 99 | 15 |  |  |  |  | 81 | for my $policy ( grep { _policy_applies_to( $ARG, $policy_type ) } | 
|  | 60 |  |  |  |  | 85281 |  | 
| 100 |  |  |  |  |  |  | $self->policies ) | 
| 101 |  |  |  |  |  |  | { | 
| 102 | 4 |  |  |  |  | 1643 | push @violations, grep {$ARG} | 
|  | 28 |  |  |  |  | 5504 |  | 
| 103 | 20 |  |  |  |  | 27 | map { $policy->violates( $ARG, $self->schema ) } @{$elements_ref}; | 
|  | 20 |  |  |  |  | 37 |  | 
| 104 |  |  |  |  |  |  | } | 
| 105 | 15 |  |  |  |  | 227 | return @violations; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub _policy_applies_to { | 
| 109 | 60 |  |  | 60 |  | 91 | my ( $policy, $type ) = @_; | 
| 110 | 60 |  |  | 60 |  | 178 | return any { $ARG eq $type } @{ $policy->applies_to }; | 
|  | 60 |  |  |  |  | 15331 |  | 
|  | 60 |  |  |  |  | 187 |  | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | 1; | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | # ABSTRACT: Critique a database schema for best practices | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | __END__ |