| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # $Id: Roles.pm,v 1.18 2006/01/30 10:58:51 dk Exp $ | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | package DBIx::Roles; | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 1 |  |  | 1 |  | 42885 | use DBI; | 
|  | 1 |  |  |  |  | 33469 |  | 
|  | 1 |  |  |  |  | 86 |  | 
| 6 | 1 |  |  | 1 |  | 13 | use Scalar::Util qw(weaken); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 141 |  | 
| 7 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 36 |  | 
| 8 | 1 |  |  | 1 |  | 5 | use vars qw($VERSION %loaded_packages $DBI_connect %DBI_select_methods $debug $ExportDepth); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 187 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | $VERSION = '1.04'; | 
| 11 |  |  |  |  |  |  | $ExportDepth = 0; | 
| 12 |  |  |  |  |  |  | $DBI_connect = \&DBI::connect; | 
| 13 |  |  |  |  |  |  | %DBI_select_methods = map { $_ => 1 } qw( | 
| 14 |  |  |  |  |  |  | selectrow_array | 
| 15 |  |  |  |  |  |  | selectrow_arrayref | 
| 16 |  |  |  |  |  |  | selectrow_hashref | 
| 17 |  |  |  |  |  |  | selectall_arrayref | 
| 18 |  |  |  |  |  |  | selectall_hashref | 
| 19 |  |  |  |  |  |  | selectcol_arrayref | 
| 20 |  |  |  |  |  |  | ); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub import | 
| 23 |  |  |  |  |  |  | { | 
| 24 | 5 |  |  | 5 |  | 4293 | shift; | 
| 25 | 5 | 100 |  |  |  | 2275 | return unless @_; | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # if given list of imports, override DBI->connect() with it | 
| 28 | 1 |  |  |  |  | 4 | my $callpkg = caller($ExportDepth); | 
| 29 | 1 |  |  | 1 |  | 5 | no strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 36 |  | 
| 30 | 1 |  |  |  |  | 4 | *{$callpkg."::DBIx_ROLES"}=[@_]; | 
|  | 1 |  |  |  |  | 5 |  | 
| 31 | 1 |  |  | 1 |  | 3 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 94 |  | 
| 32 | 1 |  |  | 0 |  | 9 | local $SIG{__WARN__} = sub {}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 33 | 1 |  |  |  |  | 775 | *DBI::connect = \&__DBI_import_connect; | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | # called instead of DBI-> connect | 
| 37 |  |  |  |  |  |  | sub __DBI_import_connect | 
| 38 |  |  |  |  |  |  | { | 
| 39 | 4 |  |  | 4 |  | 29 | shift; | 
| 40 | 4 |  |  |  |  | 12 | my $callpkg = caller(0); | 
| 41 | 1 |  |  | 1 |  | 4 | no strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 48 |  | 
| 42 | 4 |  |  |  |  | 5 | my @packages = @{$callpkg."::DBIx_ROLES"}; | 
|  | 4 |  |  |  |  | 43 |  | 
| 43 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 996 |  | 
| 44 | 4 | 100 |  |  |  | 13 | if ( @packages) { | 
| 45 | 3 |  |  |  |  | 18 | return DBIx::Roles-> new( @packages)-> connect( @_); | 
| 46 |  |  |  |  |  |  | } else { | 
| 47 | 1 |  |  |  |  | 5 | return $DBI_connect->( 'DBI', @_); | 
| 48 |  |  |  |  |  |  | } | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # prepare new instance, do not connect to DB | 
| 52 |  |  |  |  |  |  | sub new | 
| 53 |  |  |  |  |  |  | { | 
| 54 | 5 |  |  | 5 | 1 | 1622 | my ( $class, @packages) = @_; | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | # load the necessary packages | 
| 57 | 5 |  |  |  |  | 14 | for my $p ( @packages) { | 
| 58 | 25 | 50 |  |  |  | 516 | $p = "DBIx::Roles::$p" unless $p =~ /:/; | 
| 59 | 25 | 100 |  |  |  | 81 | next if exists $loaded_packages{$p}; | 
| 60 | 1 |  |  | 1 |  | 816 | eval "use $p;"; | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 1 |  | 15 |  | 
|  | 1 |  |  | 1 |  | 757 |  | 
|  | 1 |  |  | 1 |  | 3 |  | 
|  | 1 |  |  | 1 |  | 18 |  | 
|  | 1 |  |  | 1 |  | 564 |  | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  |  |  | 16 |  | 
|  | 1 |  |  |  |  | 667 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 16 |  | 
|  | 1 |  |  |  |  | 726 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 19 |  | 
|  | 1 |  |  |  |  | 655 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 17 |  | 
|  | 1 |  |  |  |  | 631 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 14 |  | 
|  | 1 |  |  |  |  | 613 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 26 |  | 
|  | 8 |  |  |  |  | 436 |  | 
| 61 | 8 | 50 |  |  |  | 33 | die $@ if $@; | 
| 62 | 8 |  |  |  |  | 30 | $loaded_packages{$p} = 1; | 
| 63 |  |  |  |  |  |  | } | 
| 64 | 5 |  |  |  |  | 17 | push @packages, 'DBIx::Roles::Default'; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | ##  create the object: | 
| 67 |  |  |  |  |  |  | # internal data instance | 
| 68 | 30 |  |  |  |  | 154 | my $instance	= { | 
| 69 |  |  |  |  |  |  | dbh	=> undef,     # DBI handle | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | packages=> \@packages, # array of DBIx::Roles::* packages to use | 
| 72 |  |  |  |  |  |  | private	=> {          # packages' private data - all separated | 
| 73 | 5 |  |  |  |  | 15 | map { $_ => undef } @packages | 
| 74 |  |  |  |  |  |  | }, | 
| 75 |  |  |  |  |  |  | defaults=> {},        # default values and source packages for attributes | 
| 76 |  |  |  |  |  |  | disabled=> {},        # dynamically disabled packages | 
| 77 |  |  |  |  |  |  | attr	=> {},        # packages' public data - all mixed, and | 
| 78 |  |  |  |  |  |  | vmt	=> {},        # packages' public methods - also all mixed | 
| 79 |  |  |  |  |  |  | # name clashes in public and vmt will be explicitly fatal | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | loops   => [], | 
| 82 |  |  |  |  |  |  | }; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # populate package info | 
| 85 | 5 |  |  |  |  | 18 | for my $p ( @packages) { | 
| 86 | 30 |  |  |  |  | 217 | my $ref = $p->can('initialize'); | 
| 87 | 30 | 100 |  |  |  | 80 | next unless $ref; | 
| 88 | 19 |  |  |  |  | 75 | my ( $storage, $data, @vmt) = $ref->( $instance); | 
| 89 | 19 |  |  |  |  | 155 | $instance-> {private}-> {$p} = $storage; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | # store default data | 
| 92 | 19 | 100 |  |  |  | 45 | if ( $data) { | 
| 93 | 13 |  |  |  |  | 20 | my $dst = $instance->{attr}; | 
| 94 | 13 |  |  |  |  | 21 | my $def = $instance->{defaults}; | 
| 95 | 13 |  |  |  |  | 326 | while ( my ( $key, $value) = each %$data) { | 
| 96 | 40 | 50 |  |  |  | 79 | die | 
| 97 |  |  |  |  |  |  | "Fatal: package '$p' defines attribute '$key' ". | 
| 98 |  |  |  |  |  |  | "that conflicts with package '$def->{$key}->[0]'" | 
| 99 |  |  |  |  |  |  | if exists $dst->{$key}; | 
| 100 | 40 |  |  |  |  | 551 | $def->{$key} = [$p, $value]; | 
| 101 | 40 |  |  |  |  | 303 | $dst->{$key} = $value; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | # store public methods | 
| 106 | 19 |  |  |  |  | 1172 | my $dst = $instance->{vmt}; | 
| 107 | 19 |  |  |  |  | 45 | for my $key ( @vmt) { | 
| 108 | 18 | 50 |  |  |  | 40 | die | 
| 109 |  |  |  |  |  |  | "Fatal: package '$p' defines method '$key' ". | 
| 110 |  |  |  |  |  |  | "that conflicts with package '$dst->{$key}'" | 
| 111 |  |  |  |  |  |  | if exists $dst->{$key}; | 
| 112 | 18 |  |  |  |  | 58 | $dst->{$key} = $p; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  | # DBIx::Roles::Instance provides API for the packages | 
| 116 | 5 |  |  |  |  | 17 | bless $instance, 'DBIx::Roles::Instance'; | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | # DBI attributes | 
| 119 | 5 |  |  |  |  | 11 | my $self 	= {}; | 
| 120 | 5 |  |  |  |  | 9 | tie %{$self}, 'DBIx::Roles::Instance', $instance; | 
|  | 5 |  |  |  |  | 29 |  | 
| 121 | 5 |  |  |  |  | 9 | bless $self, $class; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | # use this trick for cheap self-referencing ( otherwise the object is never destroyed ) | 
| 124 | 5 |  |  |  |  | 22 | $instance->{self} = $self; | 
| 125 | 5 |  |  |  |  | 53 | weaken( $instance->{self}); | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 5 |  |  |  |  | 22 | return $self; | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | # connect to DB | 
| 131 |  |  |  |  |  |  | sub connect | 
| 132 |  |  |  |  |  |  | { | 
| 133 | 4 |  |  | 4 | 1 | 14 | my $self = shift; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 4 | 50 |  |  |  | 18 | unless ( ref($self)) { | 
| 136 |  |  |  |  |  |  | # called as DBIx::Roles-> connect(), packages provided | 
| 137 | 0 |  |  |  |  | 0 | $self = $self-> new( @{shift()}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 138 |  |  |  |  |  |  | } # else the object is just being reconnected | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 4 |  |  |  |  | 12 | my $inst = $self-> instance; | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 4 | 50 |  |  |  | 14 | $self-> disconnect if $inst->{dbh}; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 4 |  |  |  |  | 13 | my @p = @_; | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | # ask each package what do they think about params to connect | 
| 147 | 4 |  |  |  |  | 16 | $inst-> dispatch( 'rewrite', 'connect', \@p); | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | # now, @p can be assumed to be in DBI-compatible format | 
| 150 | 4 |  |  |  |  | 10 | my ( $dsn, $user, $password, $attr) = @p; | 
| 151 | 4 |  | 100 |  |  | 20 | $attr ||= {}; | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | # validate each package's individual parameters | 
| 154 | 4 |  |  |  |  | 29 | for my $k ( keys %$attr) { | 
| 155 | 2 | 50 |  |  |  | 8 | next unless exists $inst->{defaults}->{$k}; | 
| 156 | 2 |  |  |  |  | 15 | $inst-> dispatch( 'STORE', $k, $attr->{$k}); | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | # apply eventual attributes passed from outside, | 
| 160 |  |  |  |  |  |  | # override with defaults those that have survived disconnect() | 
| 161 | 4 |  |  |  |  | 8 | for my $k ( keys %{$inst->{defaults}}) { | 
|  | 4 |  |  |  |  | 20 |  | 
| 162 | 40 | 100 |  |  |  | 78 | if ( exists $attr-> {$k}) { | 
| 163 | 2 |  |  |  |  | 5 | $inst-> {attr}-> {$k} = $attr-> {$k}; | 
| 164 | 2 |  |  |  |  | 7 | delete $attr-> {$k}; | 
| 165 |  |  |  |  |  |  | } else { | 
| 166 | 38 |  |  |  |  | 86 | $inst-> {attr}-> {$k} = $inst->{defaults}->{$k}->[1]; | 
| 167 |  |  |  |  |  |  | }; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | # try to connect | 
| 171 | 4 | 50 |  |  |  | 44 | return $self | 
| 172 |  |  |  |  |  |  | if $inst-> {dbh} = $inst-> connect( $dsn, $user, $password, $attr); | 
| 173 | 0 | 0 |  |  |  | 0 | die "Unable to connect: no suitable roles found\n" | 
| 174 |  |  |  |  |  |  | if $attr->{RaiseError}; | 
| 175 | 0 |  |  |  |  | 0 | return undef; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # access object data instance | 
| 179 | 26 |  |  | 26 | 0 | 50 | sub instance {  tied %{ $_[0] } } | 
|  | 26 |  |  |  |  | 81 |  | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # disconnect from DB, but retain the object | 
| 182 |  |  |  |  |  |  | sub disconnect | 
| 183 |  |  |  |  |  |  | { | 
| 184 | 0 |  |  | 0 | 0 | 0 | my $self = $_[0]; | 
| 185 | 0 |  |  |  |  | 0 | my $inst = $self-> instance; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 0 | 0 |  |  |  | 0 | $inst-> disconnect if $inst->{dbh}; | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | sub AUTOLOAD | 
| 191 |  |  |  |  |  |  | { | 
| 192 | 12 |  |  | 12 |  | 3083 | my @p = @_; | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 1 |  |  | 1 |  | 11 | use vars qw($AUTOLOAD); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 7069 |  | 
| 195 | 12 |  |  |  |  | 31 | my $method = $AUTOLOAD; | 
| 196 | 12 |  |  |  |  | 223 | $method =~ s/^.*:://; | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 12 |  |  |  |  | 24 | my $self = shift @p; | 
| 199 | 12 |  |  |  |  | 38 | my $inst = $self-> instance; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 12 |  |  |  |  | 18 | my $package; | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 12 | 100 | 66 |  |  | 124 | if ( | 
|  |  | 100 |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | exists( $DBI::DBI_methods{common}->{$method}) or | 
| 205 |  |  |  |  |  |  | exists( $DBI::DBI_methods{db}->{$method}) | 
| 206 |  |  |  |  |  |  | ) { | 
| 207 |  |  |  |  |  |  | # is it a DBI native method? | 
| 208 |  |  |  |  |  |  | # rewrite | 
| 209 | 9 |  |  |  |  | 36 | $inst-> dispatch( 'rewrite', $method, \@p); | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | # dispatch | 
| 212 | 9 |  |  |  |  | 236 | @_ = ( $inst, $method, @p); | 
| 213 | 9 |  |  |  |  | 59 | goto $inst-> can('dispatch_dbi_method'); | 
| 214 |  |  |  |  |  |  | } elsif ( exists $inst->{vmt}->{$method}) { | 
| 215 |  |  |  |  |  |  | # is it an exported method for outside usage? | 
| 216 | 1 |  |  |  |  | 4 | my $package = $inst->{vmt}->{$method}; | 
| 217 | 1 |  |  |  |  | 9 | my $ref = $package-> can( $method); | 
| 218 | 1 | 50 |  |  |  | 15 | die "Package '$package' declared method '$method' as available, but it is not" | 
| 219 |  |  |  |  |  |  | unless $ref; # XXX AUTOLOAD cases are not handled | 
| 220 | 1 |  |  |  |  | 5 | @_ = ( $inst, $inst->{private}->{$package}, @p); | 
| 221 | 1 |  |  |  |  | 6 | goto $ref; | 
| 222 |  |  |  |  |  |  | } else { | 
| 223 |  |  |  |  |  |  | # none of the above, try wildcards | 
| 224 | 2 |  |  |  |  | 11 | @_ = ( $inst, 'any', $method, @p); | 
| 225 | 2 |  |  |  |  | 11 | goto $inst-> can('dispatch'); | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub DESTROY | 
| 230 |  |  |  |  |  |  | { | 
| 231 | 5 |  |  | 5 |  | 2008 | my $self = $_[0]; | 
| 232 | 5 |  |  |  |  | 13 | my $inst = $self-> instance; | 
| 233 | 5 | 100 |  |  |  | 34 | $inst-> disconnect if $inst->{dbh}; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 5 |  |  |  |  | 21 | untie %$inst; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | # internal API | 
| 239 |  |  |  |  |  |  | package DBIx::Roles::Instance; | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # since DBI::connect can be overloaded, call the connect method by reference | 
| 242 | 0 |  |  | 0 |  | 0 | sub DBI_connect { shift; $DBIx::Roles::DBI_connect->('DBI', @_ ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | # iterate through each package in the recursive way | 
| 245 |  |  |  |  |  |  | sub get_super | 
| 246 |  |  |  |  |  |  | { | 
| 247 | 236 |  |  | 236 |  | 284 | my ( $self) = @_; | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 236 |  |  |  |  | 228 | my $ref; | 
| 250 | 236 |  |  |  |  | 339 | my $ctx = $self->{loops}->[-1]; | 
| 251 | 236 |  |  |  |  | 247 | while ( 1) { | 
| 252 | 724 | 100 |  |  |  | 1036 | if ( $ctx->[0] < scalar @{$self-> {packages}}) { | 
|  | 724 | 100 |  |  |  | 1694 |  | 
| 253 |  |  |  |  |  |  | # next package | 
| 254 | 695 |  |  |  |  | 1275 | my $package = $self-> {packages}->[ $ctx->[0]++]; | 
| 255 | 695 | 50 |  |  |  | 3156 | next if $self->{disabled}->{$package}; | 
| 256 | 695 | 100 |  |  |  | 5186 | next unless $ref = $package-> can( $ctx->[1]); | 
| 257 | 207 | 50 |  |  |  | 383 | print STDERR ('  'x @{$self->{loops}}), "-> $package\n" if $DBIx::Roles::debug; | 
|  | 0 |  |  |  |  | 0 |  | 
| 258 | 207 |  |  |  |  | 700 | return ( $ref, $self-> {private}-> {$package}); | 
| 259 |  |  |  |  |  |  | } elsif ( $ctx->[2]) { | 
| 260 |  |  |  |  |  |  | # signal end of list | 
| 261 | 11 |  |  |  |  | 36 | return $ctx->[2]->( $self, $ctx); | 
| 262 |  |  |  |  |  |  | } else { | 
| 263 | 18 |  |  |  |  | 41 | return; | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | # iterate through each package in the recursive way | 
| 269 |  |  |  |  |  |  | sub super | 
| 270 |  |  |  |  |  |  | { | 
| 271 | 229 |  |  | 229 |  | 340 | my $self = shift; | 
| 272 | 229 |  |  |  |  | 505 | my ( $ref, $private) = $self-> get_super; | 
| 273 | 229 | 100 |  |  |  | 526 | return unless $ref; | 
| 274 | 211 |  |  |  |  | 459 | unshift @_, $self, $private; | 
| 275 | 211 |  |  |  |  | 785 | goto $ref; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | # saves and restores context of dispatch calls - needed if underlying roles | 
| 279 |  |  |  |  |  |  | # are needed to be restarted | 
| 280 |  |  |  |  |  |  | sub context | 
| 281 |  |  |  |  |  |  | { | 
| 282 | 19 | 100 |  | 19 |  | 46 | if ( $#_) { | 
| 283 | 6 |  |  |  |  | 48 | @{$_[0]->{loops}->[-1]} = @{$_[1]}; | 
|  | 6 |  |  |  |  | 37 |  | 
|  | 6 |  |  |  |  | 12 |  | 
| 284 |  |  |  |  |  |  | } else { | 
| 285 | 13 |  |  |  |  | 14 | return [ @{$_[0]->{loops}->[-1]} ]; | 
|  | 13 |  |  |  |  | 73 |  | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | # call $method in all packages, where available, returns the result of the call | 
| 290 |  |  |  |  |  |  | sub dispatch | 
| 291 |  |  |  |  |  |  | { | 
| 292 | 74 |  |  | 74 |  | 107 | my $self = shift; | 
| 293 | 74 | 100 | 66 |  |  | 360 | my $eol_handler = shift if $_[0] and ref($_[0]); | 
| 294 | 74 |  |  |  |  | 119 | my $method = shift; | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 74 |  |  |  |  | 81 | my @ret; | 
| 297 | 74 |  |  |  |  | 90 | my $wa = wantarray; | 
| 298 | 74 |  |  |  |  | 691 | push @{$self->{loops}}, [ 0, $method, $eol_handler, 0]; | 
|  | 74 |  |  |  |  | 262 |  | 
| 299 | 0 | 0 |  |  |  | 0 | print STDERR ('  'x @{$self->{loops}}), "dispatch(", | 
|  | 0 |  |  |  |  | 0 |  | 
| 300 | 74 | 50 |  |  |  | 156 | ( join ',', map { defined($_) ? $_ : "undef"} $method,@_), ")\n" | 
| 301 |  |  |  |  |  |  | if $DBIx::Roles::debug; | 
| 302 | 74 |  |  |  |  | 106 | eval { | 
| 303 | 74 | 100 |  |  |  | 134 | if ( $wa) { | 
| 304 | 4 |  |  |  |  | 15 | @ret = $self-> super( @_); | 
| 305 |  |  |  |  |  |  | } else { | 
| 306 | 70 |  |  |  |  | 177 | $ret[0] = $self-> super( @_); | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  | }; | 
| 309 | 74 | 50 |  |  |  | 1543 | print STDERR ('  'x @{$self->{loops}}), "done $method\n" if $DBIx::Roles::debug; | 
|  | 0 |  |  |  |  | 0 |  | 
| 310 | 74 |  |  |  |  | 75 | pop @{$self->{loops}}; | 
|  | 74 |  |  |  |  | 134 |  | 
| 311 | 74 | 50 |  |  |  | 184 | die $@ if $@; | 
| 312 | 74 | 100 |  |  |  | 423 | return wantarray ? @ret : $ret[0]; | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | # if called, then that means that all $method hooks were called, | 
| 316 |  |  |  |  |  |  | # and now 'dbi_method' round must be run | 
| 317 |  |  |  |  |  |  | sub _dispatch_dbi_eol | 
| 318 |  |  |  |  |  |  | { | 
| 319 | 11 |  |  | 11 |  | 17 | my ( $self, $ctx, $params) = @_; | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 11 |  |  |  |  | 16 | $ctx->[0] = 0;               # reset the counter | 
| 322 | 11 |  |  |  |  | 25 | my $method = $ctx->[1]; | 
| 323 | 11 |  |  |  |  | 20 | $ctx->[1] = 'dbi_method';    # call that hook instead | 
| 324 | 11 |  |  |  |  | 15 | $ctx->[2] = undef;           # clear the eol handler | 
| 325 | 11 | 50 |  |  |  | 27 | print STDERR ('  'x @{$self->{loops}}), "done($method),dispatch(dbi_method)\n" if $DBIx::Roles::debug; | 
|  | 0 |  |  |  |  | 0 |  | 
| 326 | 11 |  |  | 11 |  | 52 | return sub { $_[0]-> super( $method, @_[2..$#_]) } | 
| 327 | 11 |  |  |  |  | 78 | } | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | # dispatch a native DBI method - first $method, then dbi_method hooks | 
| 330 |  |  |  |  |  |  | sub dispatch_dbi_method | 
| 331 |  |  |  |  |  |  | { | 
| 332 | 24 |  |  | 24 |  | 73 | my ( $self, $method, @parameters) = @_; | 
| 333 | 24 |  |  |  |  | 62 | splice( @_, 1, 0, \&_dispatch_dbi_eol); | 
| 334 | 24 |  |  |  |  | 69 | goto &dispatch; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | sub enable_roles | 
| 338 |  |  |  |  |  |  | { | 
| 339 | 0 |  |  | 0 |  | 0 | my $hash = shift->{disabled}; | 
| 340 | 0 |  |  |  |  | 0 | for my $p (@_) { | 
| 341 | 0 | 0 |  |  |  | 0 | my $g = ($p =~ /:/) ? $p : "DBIx::Roles::$p"; | 
| 342 | 0 | 0 |  |  |  | 0 | $hash->{$g}-- if $hash->{$g} > 0; | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | sub disable_roles | 
| 347 |  |  |  |  |  |  | { | 
| 348 | 0 |  |  | 0 |  | 0 | my $hash = shift->{disabled}; | 
| 349 | 0 |  |  |  |  | 0 | for my $p (@_) { | 
| 350 | 0 | 0 |  |  |  | 0 | my $g = ($p =~ /:/) ? $p : "DBIx::Roles::$p"; | 
| 351 | 0 |  |  |  |  | 0 | $hash->{$g}++; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | # R/W access to the underlying DBI connection handle | 
| 356 |  |  |  |  |  |  | sub dbh | 
| 357 |  |  |  |  |  |  | { | 
| 358 | 35 | 100 |  | 35 |  | 193 | return $_[0]-> {dbh} unless $#_; | 
| 359 | 12 |  |  |  |  | 43 | $_[0]-> {dbh} = $_[1]; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | # access to the DBIx::Roles object | 
| 363 | 4 |  |  | 4 |  | 29 | sub object { $_[0]-> {self} } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | # all unknown functions, called by roles internally, are assumed to be DBI methods | 
| 366 |  |  |  |  |  |  | sub AUTOLOAD | 
| 367 |  |  |  |  |  |  | { | 
| 368 | 1 |  |  | 1 |  | 18 | use vars qw($AUTOLOAD); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 963 |  | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 15 |  |  | 15 |  | 37 | my $method = $AUTOLOAD; | 
| 371 | 15 |  |  |  |  | 114 | $method =~ s/^.*:://; | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 15 |  |  |  |  | 48 | splice( @_, 1, 0, $method); | 
| 374 | 15 |  |  |  |  | 50 | goto &dispatch_dbi_method; | 
| 375 |  |  |  |  |  |  | } | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 5 |  |  | 5 |  | 16 | sub TIEHASH { $_[1] } | 
| 378 | 4 |  |  | 4 |  | 12 | sub EXISTS  { shift-> dispatch( 'EXISTS', @_) } | 
| 379 | 5 |  |  | 5 |  | 10652 | sub FETCH   { shift-> dispatch( 'FETCH',  @_) } | 
| 380 | 15 |  |  | 15 |  | 1961 | sub STORE   { shift-> dispatch( 'STORE',  @_) } | 
| 381 | 4 |  |  | 4 |  | 12 | sub DELETE  { shift-> dispatch( 'DELETE', @_) } | 
| 382 |  |  |  |  |  |  |  | 
| 383 | 5 |  |  | 5 |  | 539 | sub DESTROY { shift-> dispatch( 'DESTROY') } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | package DBIx::Roles::Default; | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | sub connect | 
| 388 |  |  |  |  |  |  | { | 
| 389 | 9 |  |  | 9 |  | 21 | my ( $self, $storage, $dsn, $user, $password, $attr) = @_; | 
| 390 | 9 |  |  |  |  | 36 | return $DBIx::Roles::DBI_connect->( 'DBI', $dsn, $user, $password, $attr); | 
| 391 |  |  |  |  |  |  | } | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | sub disconnect | 
| 394 |  |  |  |  |  |  | { | 
| 395 | 1 |  |  | 1 |  | 2 | my $self = $_[0]; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 1 |  |  |  |  | 29 | $self-> {dbh}-> disconnect; | 
| 398 | 1 |  |  |  |  | 8 | $self-> {dbh} = undef; | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub dbi_method | 
| 402 |  |  |  |  |  |  | { | 
| 403 | 0 |  |  | 0 |  | 0 | my ( $self, $storage, $method, @parameters) = @_; | 
| 404 | 0 |  |  |  |  | 0 | return $self-> {dbh}-> $method( @parameters); | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | sub any | 
| 408 |  |  |  |  |  |  | { | 
| 409 | 0 |  |  | 0 |  | 0 | my ( $self, $storage, $method) = @_; | 
| 410 | 0 |  |  |  |  | 0 | my @c = caller( $self-> {loops}->[-1]->[3] * 2); | 
| 411 | 0 |  |  |  |  | 0 | die "Cannot locate method '$method' at $c[1] line $c[2]\n"; | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | sub EXISTS | 
| 415 |  |  |  |  |  |  | { | 
| 416 | 4 |  |  | 4 |  | 9 | my ( $self, $storage, $key) = @_; | 
| 417 | 4 | 50 |  |  |  | 12 | if ( exists $self-> {attr}-> {$key}) { | 
| 418 | 0 |  |  |  |  | 0 | return exists $self-> {attr}-> {$key}; | 
| 419 |  |  |  |  |  |  | } else { | 
| 420 | 4 |  |  |  |  | 21 | return exists $self-> {dbh}-> {$key}; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  | } | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | sub FETCH | 
| 425 |  |  |  |  |  |  | { | 
| 426 | 5 |  |  | 5 |  | 15 | my ( $self, $storage, $key) = @_; | 
| 427 | 5 | 50 |  |  |  | 19 | if ( exists $self-> {attr}-> {$key}) { | 
| 428 | 5 |  |  |  |  | 18 | return $self-> {attr}-> {$key}; | 
| 429 |  |  |  |  |  |  | } else { | 
| 430 | 0 |  |  |  |  | 0 | return $self-> {dbh}-> {$key}; | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | sub STORE | 
| 435 |  |  |  |  |  |  | { | 
| 436 | 17 |  |  | 17 |  | 33 | my ( $self, $storage, $key, $val) = @_; | 
| 437 | 17 | 100 |  |  |  | 42 | if ( exists $self-> {attr}-> {$key}) { | 
| 438 | 9 |  |  |  |  | 68 | $self-> {attr}-> {$key} = $val; | 
| 439 |  |  |  |  |  |  | } else { | 
| 440 | 8 |  |  |  |  | 66 | $self-> {dbh}-> {$key} = $val; | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | sub DELETE | 
| 445 |  |  |  |  |  |  | { | 
| 446 | 4 |  |  | 4 |  | 38 | my ( $self, $storage, $key) = @_; | 
| 447 | 4 | 50 |  |  |  | 11 | if ( exists $self-> {attr}-> {$key}) { | 
| 448 | 0 |  |  |  |  | 0 | delete $self-> {attr}-> {$key}; | 
| 449 |  |  |  |  |  |  | } else { | 
| 450 | 4 |  |  |  |  | 20 | delete $self-> {dbh}-> {$key}; | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | 1; | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | __DATA__ |