File Coverage

blib/lib/Devel/Deprecate.pm
Criterion Covered Total %
statement 81 81 100.0
branch 41 42 97.6
condition 6 9 66.6
subroutine 14 14 100.0
pod 1 1 100.0
total 143 147 97.2


line stmt bran cond sub pod time code
1             package Devel::Deprecate;
2              
3 4     4   102841 use warnings;
  4         9  
  4         109  
4 4     4   20 use strict;
  4         7  
  4         134  
5              
6             =head1 NAME
7              
8             Devel::Deprecate - Create deprecation schedules in your code
9              
10             =head1 VERSION
11              
12             Version 0.01
13              
14             =cut
15              
16 4     4   21 use base 'Exporter';
  4         11  
  4         478  
17 4     4   22 use Carp ();
  4         8  
  4         71  
18 4     4   5215 use DateTime;
  4         828817  
  4         177  
19 4     4   49 use Scalar::Util qw(reftype blessed);
  4         8  
  4         314  
20 4     4   26 use vars qw($VERSION @EXPORT_OK);
  4         8  
  4         4613  
21             $VERSION = '0.01';
22             @EXPORT_OK = qw(deprecate);
23              
24             =head1 SYNOPSIS
25              
26             use Devel::Deprecate 'deprecate';
27              
28             sub name {
29             my ( $self ) = @_;
30              
31             deprecate(
32             reason => 'Please use the set_name() method for setting names',
33             warn => '2008-11-01', # also accepts DateTime objects
34             die => '2009-01-01', # two month deprecation period
35             if => sub { return @_ > 1 },
36             );
37             if ( @_ > 1 ) {
38             $self->{name} = $_[1];
39             return $self;
40             }
41             return $self->{name};
42             }
43              
44             =head1 DESCRIPTION
45              
46             Many times we find ourselves needing to deprecate code or have a deadline and
47             just don't have time to refactor. Instead of trying to remember about this,
48             posting it to a wiki or sending an email, it's better to have an automatic way
49             to deprecate something. This module allows you to do that and embeds the
50             deprecation directly in the code you wish to deprecate.
51              
52             As we don't want to break production code, deprecations are only triggered
53             when running tests.
54              
55             =head1 EXPORT
56              
57             =head1 FUNCTIONS
58              
59             =head2 C
60              
61             deprecate( reason => 'The foo() method does not appear to be used' );
62              
63             deprecate(
64             reason => 'Please use the set_name() method for setting names',
65             warn => '2008-11-01', # also accepts DateTime objects
66             die => '2009-01-01', # two month deprecation period
67             if => sub { return @_ > 1 },
68             );
69              
70             This function is exported on demand. It takes an even-sized list of key/value
71             pairs. Its function is to spit out a warning (or croak) when deprecation
72             criteria are hit.
73              
74             Deprecation warnings or failures only occur when running tests (but see
75             L below)
76             and are designed to be extremely noisy (and with a strack trace):
77              
78             # DEPRECATION WARNING
79             #
80             # Package: Our::Customer
81             # File: lib/Our/Customer.pm
82             # Line: 58
83             # Subroutine: Our::Customer::name
84             #
85             # Reason: Please use the set_name() method for setting names
86             #
87             # This warning becomes FATAL on (2009-01-01)
88              
89             And after the due date:
90              
91             # DEPRECATION FAILURE
92             #
93             # Package: Our::Customer
94             # File: lib/Our/Customer.pm
95             # Line: 58
96             # Subroutine: Our::Customer::name
97             #
98             # Reason: Please use the set_name() method for setting names
99             #
100             # This deprecation became fatal on (2009-01-01)
101              
102             Allowed key/value pairs:
103              
104             =over 4
105              
106             =item * C
107              
108             This is the only required key.
109              
110             This should be a human readable string explaining why the deprecation is
111             needed.
112              
113             reason => 'This module should be replaced by the Our::Improved::Module'
114              
115             If C is called with only a reason, it begins issuing deprecation
116             warnings immediately.
117              
118             =item * C
119              
120             Optional. If not present, deprecation warnings start immediately.
121              
122             This should be a string in 'YYYY-MM-DD' format or a C object
123             indicating when the deprecation warnings should start.
124              
125             warn => '2008-06-06'
126             # or ...
127             warn => DateTime->new( year => 2008, month => 06, day => 06 )
128              
129             =item * C
130              
131             Optional. If not present, deprecation warnings never become fatal.
132              
133             This should be a string in 'YYYY-MM-DD' format or a C object
134             indicating when the deprecation warnings should become fatal.
135              
136             die => '2009-06-06'
137             # or ...
138             die => DateTime->new( year => 2009, month => 06, day => 06 )
139              
140             =item * C
141              
142             Optional. May be a boolean value or a code reference.
143              
144             if => ( @_ > 1 )
145             # or
146             if => sub { @_ > 1 }
147              
148             If the 'if' condition evaluates to false, no deprecation action action is
149             taken.
150              
151             If the 'if' argument is a code reference, it will receive the C
152             argument list has a hash reference in C<$_[0]>, minus the 'if' key/value pair.
153              
154             =back
155              
156             =head1 PRODUCTION ENVIRONMENTS
157              
158             Don't break them. Just don't. People get mad at you and scratch you off
159             their Christmas card list. To ensure that C doesn't break
160             production environments, C returns immediately if
161             C<$ENV{HARNESS_ACTIVE}> evaluates as false, thus ensuring that deprecations
162             are generally only triggered by tests.
163              
164             However, sometimes you might find this variable set in production code, so you
165             can still disable this module by setting the C<$ENV{PERL_DEVEL_DEPRECATE_OFF}>
166             variable to a true value.
167              
168             Failing that, simply omit the C key. Then, at most you'll get lots of
169             warnings and never a fatal error.
170              
171             =head1 SCHEDULING DEPRECATIONS
172              
173             Typically you'll just want something like the following in your code:
174              
175             deprecate( reason => 'Use CGI.pm instead of cgi-lib.pl' );
176              
177             That issues noisy warnings about a deprecation, but at times you'll want to
178             schedule a deprecation period. Perhaps the deprecation won't even start until
179             a new software package is installed in three months and it's agreed that the
180             "old" interface is to be supported for six months. Assuming today is the
181             first day of 2008, you might write a deprecation like this:
182              
183             use Devel::Deprecate 'deprecate';
184              
185             sub report : Path('/report/sales') {
186             deprecate(
187             reason => 'Pointy-haired bosses bought a reporting package',
188             warn => '2008-04-01',
189             die => '2008-10-01',
190             );
191             ...
192              
193             That subroutine I only run while testing (see
194             L) and will likely annoy the heck our of developers
195             with verbose error messages. Of course, that's the point. The deprecation
196             period, however, should be carefully thought you. In fact, you may wish to
197             omit it entirely to ensure that the deprecation is never a fatal error.
198              
199             Alternately, you might write it like this:
200              
201             sub report : Path('/report/sales') {
202             deprecate(
203             reason => 'Pointy-haired bosses bought a reporting package',
204             die => '2008-10-01',
205             if => \&other_software_is_installed,
206             );
207             ...
208              
209             With this, the deprecation warnings begin if and only if the
210             C subroutine returns true. Further, even the
211             C will be be triggered unless this condition holds.
212              
213             =cut
214              
215             sub deprecate {
216 26 100   26 1 23057 return if !$ENV{HARNESS_ACTIVE}; # only in testing
217 24 100       78 return if $ENV{PERL_DEVEL_DEPRECATE_OFF}; # or let 'em force it
218 23 100       75 if ( @_ % 2 ) {
219 1         4 Carp::croak("deprecate() called with odd number of elements in hash assignment");
220             }
221 22         90 my %arg_for = @_;
222 22 100       65 unless ( exists $arg_for{reason} ) {
223 2         10 Carp::croak("deprecate() called without a 'reason' argument");
224             }
225              
226 20 100       49 if (exists $arg_for{if}) {
227 4         10 my $should_deprecate = delete $arg_for{if};
228              
229 4 100 100     30 if ('CODE' eq ( reftype $should_deprecate || '' )) {
230 2 100       9 return unless $should_deprecate->(\%arg_for);
231             }
232 3 100       17 return unless $should_deprecate;
233             }
234              
235 18         36 my $reason = delete $arg_for{reason};
236              
237 18         56 _check_dates(\%arg_for);
238 16         311 my $warn = _date(delete $arg_for{warn});
239 16         644 my $die = _date(delete $arg_for{die});
240              
241 16         213 my $should_warn = _should_warn($warn);
242 16         3453 my $should_die = _should_die($die);
243              
244             # parting is such sweet sorrow -- and the default
245 16 100       2043 $should_warn = 0 if $should_die;
246              
247 16 100       52 _warn($reason, $die) if $should_warn;
248 16 100       108 _die($reason, $die) if $should_die;
249             }
250              
251             sub _check_dates {
252 18     18   26 my $args = shift;
253 18 100       54 return unless my $warn = _date($args->{warn});
254 13 100       1660 return unless my $die = _date($args->{die});
255 5 100       562 if ( $die <= $warn ) {
256 1         82 Carp::croak("deprecate() die date ($args->{die}) must be after warn date ($args->{warn})");
257             }
258             }
259              
260             sub _should_warn {
261 16     16   28 my $date = _date(shift);
262 16 100       33 return 1 unless defined $date; # warn by default
263 12         68 return $date <= DateTime->today;
264             }
265              
266             sub _should_die {
267 16     16   34 my $date = _date(shift);
268 16 100       41 return unless defined $date; # do not die by default
269 7         23 return $date <= DateTime->today;
270             }
271              
272             sub _date {
273 95     95   187 my $date = shift;
274 95 100       220 return unless defined $date; # it's OK if they haven't passed on
275 57 100 66     424 return $date if blessed $date and $date->isa('DateTime');
276 11 100       53 if ( $date =~ /\A(\d\d\d\d)-(\d\d)-(\d\d)\z/ ) {
277 10         44 return DateTime->new(
278             year => $1,
279             month => $2,
280             day => $3,
281             );
282             }
283 1         7 Carp::croak("Cannot parse unknown date format ($date)");
284             }
285              
286             sub _warn {
287 11     11   18 my ( $reason, $die ) = @_;
288              
289 11         83 my ( $package, $filename, $line ) = caller(1);
290              
291             # need to get past deprecate()
292 11         61 my ( undef, undef, undef, $subroutine ) = caller(2);
293 11   50     31 $subroutine ||= 'n/a';
294 11         20 my $padding = ' ' x 18;
295 11         32 $reason =~ s/\n/\n#$padding/g;
296              
297 11         54 $reason = <<" END";
298             # DEPRECATION WARNING
299             #
300             # Package: $package
301             # File: $filename
302             # Line: $line
303             # Subroutine: $subroutine
304             #
305             # Reason: $reason
306             END
307              
308 11 100       25 if ( $die ) {
309 4         726 $die = $die->ymd;
310 4         49 $reason = <<" END";
311             $reason#
312             # This warning becomes FATAL on ($die)
313             END
314             }
315 11         43 Carp::cluck($reason);
316             }
317              
318             sub _die {
319 2     2   5 my ( $reason, $die ) = @_;
320              
321 2         15 my ( $package, $filename, $line ) = caller(1);
322              
323             # need to get past deprecate()
324 2         11 my ( undef, undef, undef, $subroutine ) = caller(2);
325 2   50     6 $subroutine ||= 'n/a';
326 2         5 my $padding = ' ' x 18;
327 2         3 $reason =~ s/\n/\n#$padding/g;
328              
329 2         11 $reason = <<" END";
330             # DEPRECATION FAILURE
331             #
332             # Package: $package
333             # File: $filename
334             # Line: $line
335             # Subroutine: $subroutine
336             #
337             # Reason: $reason
338             END
339              
340 2 50       7 if ( $die ) {
341 2         86 $die = $die->ymd;
342 2         21 $reason = <<" END";
343             $reason#
344             # This deprecation became fatal on ($die)
345             END
346             }
347 2         10 Carp::confess($reason);
348             }
349              
350             =head1 AUTHOR
351              
352             Curtis "Ovid" Poe, C<< >>
353              
354             =head1 BUGS
355              
356             Please report any bugs or feature requests to C
357             rt.cpan.org>, or through the web interface at
358             L. I will be
359             notified, and then you'll automatically be notified of progress on your bug as
360             I make changes.
361              
362             =head1 SUPPORT
363              
364             You can find documentation for this module with the perldoc command.
365              
366             perldoc Devel::Deprecate
367              
368             You can also look for information at:
369              
370             =over 4
371              
372             =item * RT: CPAN's request tracker
373              
374             L
375              
376             =item * AnnoCPAN: Annotated CPAN documentation
377              
378             L
379              
380             =item * CPAN Ratings
381              
382             L
383              
384             =item * Search CPAN
385              
386             L
387              
388             =back
389              
390             =head1 ACKNOWLEDGEMENTS
391              
392             =over 4
393              
394             =item * L.
395              
396             The "Refactoring Databases" book explained the rationale as to why we want
397             automated deprecation periods.
398              
399             =item * L
400              
401             Several helpful comments on the Perl Monks discussion, particularly comments
402             by Jenda about not breaking production code.
403              
404             =back
405              
406             =head1 COPYRIGHT & LICENSE
407              
408             Copyright 2008 Curtis "Ovid" Poe, all rights reserved.
409              
410             This program is free software; you can redistribute it and/or modify it
411             under the same terms as Perl itself.
412              
413             =cut
414              
415             1;