File Coverage

blib/lib/Class/DBI/Loader/Generic.pm
Criterion Covered Total %
statement 121 143 84.6
branch 22 48 45.8
condition 10 25 40.0
subroutine 22 27 81.4
pod 5 5 100.0
total 180 248 72.5


line stmt bran cond sub pod time code
1             package Class::DBI::Loader::Generic;
2              
3 1     1   9 use strict;
  1         2  
  1         52  
4 1     1   6 use vars qw($VERSION);
  1         3  
  1         63  
5 1     1   8 use Carp;
  1         3  
  1         107  
6 1     1   6395 use Lingua::EN::Inflect;
  1         43942  
  1         173  
7              
8             $VERSION = '0.30';
9              
10             =head1 NAME
11              
12             Class::DBI::Loader::Generic - Generic Class::DBI::Loader Implementation.
13              
14             =head1 SYNOPSIS
15              
16             See L
17              
18             =head1 DESCRIPTION
19              
20             =head1 METHODS
21              
22             =head2 new %args
23              
24             See the documentation for Cnew()>
25              
26             =cut
27              
28             sub new {
29 1     1 1 8 my ( $class, %args ) = @_;
30 1 50       79 if ( $args{debug} ) {
31 1     1   13 no strict 'refs';
  1         2  
  1         1239  
32 0     0   0 *{"$class\::debug"} = sub { 1 };
  0         0  
  0         0  
33             }
34 1   50     9 my $additional = $args{additional_classes} || [];
35 1 50       5 $additional = [$additional] unless ref $additional eq 'ARRAY';
36 1   50     70 my $additional_base = $args{additional_base_classes} || [];
37 1 50       6 $additional_base = [$additional_base]
38             unless ref $additional_base eq 'ARRAY';
39 1   50     4 my $left_base = $args{left_base_classes} || [];
40 1 50       6 $left_base = [$left_base] unless ref $left_base eq 'ARRAY';
41 1   50     17 my $self = bless {
42             _datasource =>
43             [ $args{dsn}, $args{user}, $args{password}, $args{options} ],
44             _namespace => $args{namespace},
45             _additional => $additional,
46             _additional_base => $additional_base,
47             _left_base => $left_base,
48             _constraint => $args{constraint} || '.*',
49             _exclude => $args{exclude},
50             _relationships => $args{relationships},
51             _inflect => $args{inflect},
52             _require => $args{require},
53             _require_warn => $args{require_warn},
54             CLASSES => {},
55             }, $class;
56 1 50       8 warn qq/\### START Class::DBI::Loader dump ###\n/ if $self->debug;
57 1         9 $self->_load_classes;
58 1 50       13 $self->_relationships if $self->{_relationships};
59 1 50       9 warn qq/\### END Class::DBI::Loader dump ###\n/ if $self->debug;
60              
61             # disconnect to avoid confusion.
62 1         7 foreach my $table ($self->tables) {
63 4         1513 $self->find_class($table)->db_Main->disconnect;
64             }
65              
66 1         488 $self;
67             }
68              
69             =head3 find_class
70              
71             Returns a tables class.
72              
73             my $class = $loader->find_class($table);
74              
75             =cut
76              
77             sub find_class {
78 18     18 1 36711 my ( $self, $table ) = @_;
79 18         176 return $self->{CLASSES}->{$table};
80             }
81              
82             =head3 classes
83              
84             Returns a sorted list of classes.
85              
86             my $@classes = $loader->classes;
87              
88             =cut
89              
90             sub classes {
91 0     0 1 0 my $self = shift;
92 0         0 return sort values %{ $self->{CLASSES} };
  0         0  
93             }
94              
95             =head3 debug
96              
97             Overload to enable debug messages.
98              
99             =cut
100              
101 19     19 1 81 sub debug { 0 }
102              
103             =head3 tables
104              
105             Returns a sorted list of tables.
106              
107             my @tables = $loader->tables;
108              
109             =cut
110              
111             sub tables {
112 2     2 1 16 my $self = shift;
113 2         5 return sort keys %{ $self->{CLASSES} };
  2         26  
114             }
115              
116             # Overload in your driver class
117 0     0   0 sub _db_class { croak "ABSTRACT METHOD" }
118              
119             # Setup has_a and has_many relationships
120             sub _has_a_many {
121 1     1   6 my ( $self, $table, $column, $other ) = @_;
122 1         7 my $table_class = $self->find_class($table);
123 1         5 my $other_class = $self->find_class($other);
124 1 50       4 warn qq/\# Has_a relationship\n/ if $self->debug;
125 1 50       4 warn qq/$table_class->has_a( '$column' => '$other_class' );\n\n/
126             if $self->debug;
127 1         18 $table_class->has_a( $column => $other_class );
128 1         1262 my ($table_class_base) = $table_class =~ /.*::(.+)/;
129 1         10 my $plural = Lingua::EN::Inflect::PL( lc $table_class_base );
130 1 50 33     7573 $plural = $self->{_inflect}->{ lc $table_class_base }
131             if $self->{_inflect}
132             and exists $self->{_inflect}->{ lc $table_class_base };
133 1 50       8 warn qq/\# Has_many relationship\n/ if $self->debug;
134 1 50       5 warn qq/$other_class->has_many( '$plural' => '$table_class' );\n\n/
135             if $self->debug;
136 1         19 $other_class->has_many( $plural => $table_class );
137             }
138              
139             # Load and setup classes
140             sub _load_classes {
141 1     1   3 my $self = shift;
142 1         5 my @tables = $self->_tables();
143 1         7 my $db_class = $self->_db_class();
144 1         1 my $additional = join '', map "use $_;\n", @{ $self->{_additional} };
  1         6  
145 1         8 my $additional_base = join '', map "use base '$_';\n",
146 1         2 @{ $self->{_additional_base} };
147 1         3 my $left_base = join '', map "use base '$_';\n", @{ $self->{_left_base} };
  1         6  
148 1         3 my $constraint = $self->{_constraint};
149 1         2 my $exclude = $self->{_exclude};
150              
151 1         31 my $use_connection = $Class::DBI::VERSION >= 0.96;
152 1         3 foreach my $table (@tables) {
153 4 50       105 next unless $table =~ /$constraint/;
154 4 50 33     19 next if ( defined $exclude && $table =~ /$exclude/ );
155 4         30 my $class = $self->_table2class($table);
156 4 50       56 warn qq/\# Initializing table "$table" as "$class"\n/ if $self->debug;
157             {
158 1     1   6 no strict 'refs';
  1         2  
  1         347  
  4         7  
159 4         7 @{"$class\::ISA"} = $db_class;
  4         197  
160             }
161 4 50       15 if ($use_connection) {
162 4         16 $class->connection(@{$self->{_datasource}});
  4         51  
163             } else {
164 0         0 $class->set_db( Main => @{ $self->{_datasource} } );
  0         0  
165             }
166 4         1410 $class->set_up_table($table);
167 4         20336 $self->{CLASSES}->{$table} = $class;
168              
169 4         132 my $code = "package $class;$additional_base$additional$left_base";
170 4 50       23 warn qq/$code/ if $self->debug;
171 4 50       14 warn qq/$class->table('$table');\n\n/ if $self->debug;
172 1     1   11 eval $code;
  1     1   2  
  1     1   1732  
  1     1   246  
  1     1   3  
  1     1   777  
  1     1   19  
  1     1   2  
  1         157  
  1         6  
  1         2  
  1         62  
  1         11  
  1         2  
  1         239  
  1         8  
  1         3  
  1         139  
  1         12  
  1         3  
  1         214  
  1         9  
  1         4  
  1         88  
  4         925  
173 4 50       109 croak qq/Couldn't load additional classes "$@"/ if $@;
174             {
175 1     1   7 no strict 'refs';
  1         6  
  1         615  
  4         7  
176 4         9 unshift @{"$class\::ISA"}, $_ foreach ( @{ $self->{_left_base} } );
  4         22  
  4         93  
177             }
178              
179 4 50       24 if ($self->{_require}) {
180 4         493 eval "require $class";
181 4 50 33     210 if ($self->{_require_warn} && $@ && $@ !~ /Can't locate/) {
      66        
182 0         0 warn;
183             }
184             }
185             }
186             }
187              
188             # Find and setup relationships
189             sub _relationships {
190 0     0   0 my $self = shift;
191 0         0 foreach my $table ( $self->tables ) {
192 0         0 my $dbh = $self->find_class($table)->db_Main;
193 0 0       0 if ( my $sth = $dbh->foreign_key_info( '', '', '', '', '', $table ) ) {
194 0         0 for my $res ( @{ $sth->fetchall_arrayref( {} ) } ) {
  0         0  
195 0         0 my $column = $res->{FK_COLUMN_NAME};
196 0         0 my $other = $res->{UK_TABLE_NAME};
197 0         0 eval { $self->_has_a_many( $table, $column, $other ) };
  0         0  
198 0 0 0     0 warn qq/\# has_a_many failed "$@"\n\n/ if $@ && $self->debug;
199             }
200             }
201             }
202             }
203              
204             # Make a class from a table
205             sub _table2class {
206 4     4   9 my ( $self, $table ) = @_;
207 4   50     20 my $namespace = $self->{_namespace} || "";
208 4         13 $namespace =~ s/(.*)::$/$1/;
209 4         46 my $subclass = join '', map ucfirst, split /[\W_]+/, $table;
210 4 50       26 my $class = $namespace ? "$namespace\::" . $subclass : $subclass;
211             }
212              
213             # Overload in driver class
214 0     0     sub _tables { croak "ABSTRACT METHOD" }
215              
216             =head1 SEE ALSO
217              
218             L, L, L,
219             L
220              
221             =cut
222              
223             1;