File Coverage

lib/Class/DBI/AutoIncrement.pm
Criterion Covered Total %
statement 27 101 26.7
branch 0 36 0.0
condition 0 21 0.0
subroutine 9 17 52.9
pod 4 4 100.0
total 40 179 22.3


line stmt bran cond sub pod time code
1             #################################################################
2             #
3             # Class::DBI::AutoIncrement - Emulate auto-incrementing columns in a Class::DBI table
4             #
5             # $Id: AutoIncrement.pm,v 1.7 2006/06/08 08:58:32 erwan Exp $
6             #
7             # 060412 erwan Created
8             # 060517 erwan Added croak when no other parent than Class::DBI::AutoIncrement
9             # 060607 erwan Fixed rt 19752, backward compatibility issue with create()
10             # 060607 erwan Refactor to use maximum_value_of() (credits to David Westbrook)
11             #
12             #################################################################
13              
14             #################################################################
15             #
16             # an object holding data describing one auto-incremented table.
17             # in charge of incrementing that table's index
18             #
19             #################################################################
20              
21             package Class::DBI::AutoIncrement::Descriptor;
22              
23 1     1   72821 use 5.006;
  1         3  
  1         57  
24 1     1   4 use strict;
  1         2  
  1         34  
25 1     1   5 use warnings;
  1         11  
  1         28  
26 1     1   5 use base qw(Class::Accessor);
  1         1  
  1         177  
27              
28             Class::Accessor->mk_accessors('table', # name of the table to auto-increment
29             'column', # name of the auto-incremented column in that table
30             'min', # the start value for the index sequence
31             'step', # the increment step of the index sequence
32             'cache', # if true, the index value is cached instead of being queried upon each insert
33             'index', # value of the index used at the last insert (if caching is on)
34             );
35              
36 0     0     sub new { return bless({},__PACKAGE__); }
37              
38             1;
39              
40             #################################################################
41             #
42             # Class::DBI::AutoIncrement
43             #
44             #################################################################
45              
46             package Class::DBI::AutoIncrement;
47              
48 1     1   12 use 5.006;
  1         2  
  1         34  
49 1     1   4 use strict;
  1         1  
  1         24  
50 1     1   4 use warnings;
  1         7  
  1         38  
51 1     1   4 use Carp qw(croak confess);
  1         1  
  1         136  
52              
53             our $VERSION = '0.05';
54              
55             # set at runtime by _set_inheritance()
56             our @ISA;
57              
58             ##################################################################
59             #
60             # PRIVATE FUNCTIONS
61             #
62             ##################################################################
63              
64             #-----------------------------------------------------------------
65             #
66             # _set_inheritance - make Class::DBI::AutoIncrement inherit from the same parents as the calling class
67             # see discussion below.
68             #
69              
70             my $inherited = 0;
71              
72             sub _set_inheritance {
73 0 0   0     return if ($inherited);
74              
75 0           my($caller) = shift;
76 1     1   4 no strict 'refs';
  1         2  
  1         914  
77              
78 0           my @parents = grep { $_ ne __PACKAGE__ } @{"$caller\::ISA"};
  0            
  0            
79 0 0         croak __PACKAGE__." expects class $caller to inherit from at least 1 more parent class" if (scalar @parents == 0);
80            
81             # inherit from same parents as the calling class, this in order
82             # to have the proper inheritance toward the local *::DBI class,
83             # without having to know its name and explicitly 'use base' it
84              
85             # this might not always work, since it redefines the class hierarchy under time
86             # plus we then require twice the same set of parent classes...
87             # an alternative would be to emulate ::SUPER-> and skip __PACKAGE__ while
88             # calling the methods insert() and create()...
89              
90 0           foreach my $class (@parents) {
91 0           eval qq{ require $class; };
92 0 0 0       if (defined $@ && $@ ne "") {
93 0           confess "BUG: \'require $class\' failed because of: ".$@;
94             }
95             }
96 0           push @ISA, @parents;
97              
98 0           $inherited = 1;
99             }
100              
101             #-----------------------------------------------------------------
102             #
103             # _get_descriptor - instanciate an object holding information about the calling class
104             #
105              
106             my $descriptors;
107              
108             sub _get_descriptor {
109 0     0     my $class = shift;
110 0 0         if (!exists $descriptors->{$class}) {
111 0           $descriptors->{$class} = new Class::DBI::AutoIncrement::Descriptor($class);
112             }
113 0           return $descriptors->{$class};
114             }
115              
116             #-----------------------------------------------------------------
117             #
118             # _do_increment - increment sequence if needed, see insert() and create()
119             #
120              
121             sub _do_increment {
122 0     0     my($proto,$class,$values) = @_;
123              
124 0           _set_inheritance($class);
125              
126 0           my $info = _get_descriptor($class);
127              
128             # check that we know all that should be known (column name, table name...)
129 0 0         if (!defined $info->column) {
130 0           croak "no auto-incremented column has been specified for class $class.";
131             }
132              
133 0 0         if (!defined $info->table) {
134 0           croak "no database table has been specified for class $class.";
135             }
136              
137 0           my $column = $info->column;
138              
139             # if the index column is not set, set it to its next known value
140 0 0 0       if (!exists $values->{$column} || !defined $values->{$column}) {
141 0           my $index;
142              
143 0 0         if (!defined $info->index) {
144            
145 0           my $id = $proto->maximum_value_of($info->column);
146            
147 0 0         if (defined $id) {
148 0           $index = $id + $info->step;
149             } else {
150             # table is empty, this will be the first row inserted
151 0           $index = $info->min;
152             }
153              
154             # if caching is on, save the index for next time we need it
155 0 0         if ($info->cache) {
156 0           $info->index($index);
157             }
158             } else {
159             # we are caching the index, and we know the current highest index
160 0           $info->index($info->step+$info->index);
161 0           $index = $info->index;
162             }
163              
164 0           $values->{$column} = $index;
165             }
166             }
167              
168             ##################################################################
169             #
170             # PUBLIC (INHERITED) FUNCTIONS
171             #
172             ##################################################################
173              
174             #-----------------------------------------------------------------
175             #
176             # autoincrement - register which column should be automatically incremented
177             #
178              
179             sub autoincrement {
180 0     0 1   my($proto,$column,%args) = @_;
181 0   0       my $class = ref $proto || $proto;
182 0           _set_inheritance($class);
183              
184 0 0         if (!defined $column) {
185 0           croak "you must define a column name to autoincrement.";
186             }
187              
188 0           my $info = _get_descriptor($class);
189              
190 0 0         if (defined $info->column()) {
191 0           croak "class $class already has one auto-incremented column";
192             }
193              
194 0           $info->column($column);
195              
196 0 0         if (exists $args{Min}) {
197 0 0         if ($args{Min} !~ /^-?\d+$/) {
198 0           croak "parameter 'Min' of method 'autoincrement' must be a number.";
199             }
200 0           $info->min($args{Min});
201             } else {
202 0           $info->min(0);
203             }
204              
205 0 0         if (exists $args{Step}) {
206 0 0         if ($args{Step} !~ /^-?\d+$/) {
207 0           croak "parameter 'Step' of method 'autoincrement' must be a number.";
208             }
209 0           $info->step($args{Step});
210             } else {
211 0           $info->step(1);
212             }
213              
214 0 0 0       if (exists $args{Cache} && $args{Cache}) {
215 0           $info->cache(1);
216             } else {
217 0           $info->cache(0);
218             }
219             }
220              
221             #-----------------------------------------------------------------
222             #
223             # table - override *::DBI->table() in order to intercept the table name
224             #
225              
226             sub table {
227 0     0 1   my($proto,$table,@args) = @_;
228 0   0       my $class = ref $proto || $proto;
229 0           _set_inheritance($class);
230              
231 0 0         if (defined $table) {
232 0           my $info = _get_descriptor($class);
233 0           $info->table($table);
234             }
235              
236 0           return $class->SUPER::table($table,@args);
237             }
238              
239             #-----------------------------------------------------------------
240             #
241             # insert - insert a new value, after incrementing its sequence if necessary
242             #
243              
244             sub insert {
245 0     0 1   my($proto,$values) = @_;
246 0   0       my $class = ref $proto || $proto;
247 0           _do_increment($proto,$class,$values);
248 0           return $class->SUPER::insert($values);
249             }
250              
251             # backward compatibility
252             sub create {
253 0     0 1   my($proto,$values) = @_;
254 0   0       my $class = ref $proto || $proto;
255 0           _do_increment($proto,$class,$values);
256 0           return $class->SUPER::create($values);
257             }
258              
259             1;
260              
261             __END__