X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FPackage%2FDeprecationManager.pm;h=e82da3332d38088cbba2e2933fa2572b9f647408;hb=8040005c9eb24e5464bc46962aa03f5df6117403;hp=b18cb658cc090b0f5d33605c6aef24a3258e735f;hpb=dc4fc8c7d1c1c6f1d6689a0b16a5d13e8ecf38e5;p=gitmo%2FPackage-DeprecationManager.git diff --git a/lib/Package/DeprecationManager.pm b/lib/Package/DeprecationManager.pm index b18cb65..e82da33 100644 --- a/lib/Package/DeprecationManager.pm +++ b/lib/Package/DeprecationManager.pm @@ -4,6 +4,7 @@ use strict; use warnings; use Carp qw( croak ); +use List::MoreUtils qw( any ); use Params::Util qw( _HASH ); use Sub::Install; @@ -18,7 +19,7 @@ sub import { my %registry; my $import = _build_import( \%registry ); - my $warn = _build_warn( \%registry, $args{-deprecations} ); + my $warn = _build_warn( \%registry, $args{-deprecations}, $args{-ignore} ); my $caller = caller(); @@ -48,6 +49,8 @@ sub _build_import { my $class = shift; my %args = @_; + $args{-api_version} ||= delete $args{-compatible}; + $registry->{ caller() } = $args{-api_version} if $args{-api_version}; @@ -58,34 +61,62 @@ sub _build_import { sub _build_warn { my $registry = shift; my $deprecated_at = shift; + my $ignore = shift; + + my %ignore = map { $_ => 1 } grep { !ref } @{ $ignore || [] }; + my @ignore_res = grep {ref} @{ $ignore || [] }; my %warned; return sub { + my %args = @_ < 2 ? ( message => shift ) : @_; + my ( $package, undef, undef, $sub ) = caller(1); + my $skipped = 1; + + if ( @ignore_res || keys %ignore ) { + while ( defined $package + && ( $ignore{$package} || any { $package =~ $_ } @ignore_res ) + ) { + $package = caller( $skipped++ ); + } + } + + $package = 'unknown package' unless defined $package; + + unless ( defined $args{feature} ) { + $args{feature} = $sub; + } + my $compat_version = $registry->{$package}; - my $deprecated_at = $deprecated_at->{$sub}; + my $deprecated_at = $deprecated_at->{ $args{feature} }; return if defined $compat_version && defined $deprecated_at && $compat_version lt $deprecated_at; - return if $warned{$package}{$sub}; - - if ( ! @_ ) { - my $msg = "$sub has been deprecated"; + my $msg; + if ( defined $args{message} ) { + $msg = $args{message}; + } + else { + $msg = "$args{feature} has been deprecated"; $msg .= " since version $deprecated_at" if defined $deprecated_at; - - @_ = $msg; } - $warned{$package}{$sub} = 1; + return if $warned{$package}{ $args{feature} }{$msg}; - goto &Carp::cluck; + $warned{$package}{ $args{feature} }{$msg} = 1; + + # We skip at least two levels. One for this anon sub, and one for the + # sub calling it. + local $Carp::CarpLevel = $Carp::CarpLevel + $skipped; + + Carp::cluck($msg); }; } @@ -99,8 +130,120 @@ __END__ =head1 SYNOPSIS - ... + package My::Class; + + use Package::DeprecationManager -deprecations => { + 'My::Class::foo' => '0.02', + 'My::Class::bar' => '0.05', + 'feature-X' => '0.07', + }; + + sub foo { + deprecated( 'Do not call foo!' ); + + ... + } + + sub bar { + deprecated(); + + ... + } + + sub baz { + my %args = @_; + + if ( $args{foo} ) { + deprecated( + message => ..., + feature => 'feature-X', + ); + } + } + + package Other::Class; + + use My::Class -api_version => '0.04'; + + My::Class->new()->foo(); # warns + My::Class->new()->bar(); # does not warn + My::Class->new()->far(); # does not warn again =head1 DESCRIPTION +This module allows you to manage a set of deprecations for one or more modules. + +When you import C, you must provide a set of +C<-deprecations> as a hash ref. The keys are "feature" names, and the values +are the version when that feature was deprecated. + +In many cases, you can simply use the fully qualified name of a subroutine or +method as the feature name. This works for cases where the whole subroutine is +deprecated. However, the feature names can be any string. This is useful if +you don't want to deprecate an entire subroutine, just a certain usage. + +You can also provide an optional array reference in the C<-ignore> +parameter. + +The values to be ignored can be package names or regular expressions (made +with C). Use this to ignore packages in your distribution that can +appear on the call stack when a deprecated feature is used. + +As part of the import process, C will export two +subroutines into its caller. It provides an C sub for the caller and a +C sub. + +The C sub allows callers of I class to specify an C<-api_version> +parameter. If this is supplied, then deprecation warnings are only issued for +deprecations for api versions earlier than the one specified. + +You must call the C sub in each deprecated subroutine. When +called, it will issue a warning using C. + +The C sub can be called in several ways. If you do not pass any +arguments, it will generate an appropriate warning message. If you pass a +single argument, this is used as the warning message. + +Finally, you can call it with named arguments. Currently, the only allowed +names are C and C. The C argument should correspond +to the feature name passed in the C<-deprecations> hash. + +If you don't explicitly specify a feature, the C sub uses +C to identify its caller, using its fully qualified subroutine name. + +A given deprecation warning is only issued once for a given package. This +module tracks this based on both the feature name I the error message +itself. This means that if you provide severaldifferent error messages for the +same feature, all of those errors will appear. + +=head1 BUGS + +Please report any bugs or feature requests to +C, or through the web interface at +L. I will be notified, and then you'll automatically be +notified of progress on your bug as I make changes. + +=head1 DONATIONS + +If you'd like to thank me for the work I've done on this module, please +consider making a "donation" to me via PayPal. I spend a lot of free time +creating free software, and would appreciate any support you'd care to offer. + +Please note that B in order +for me to continue working on this particular software. I will +continue to do so, inasmuch as I have in the past, for as long as it +interests me. + +Similarly, a donation made in this way will probably not make me work on this +software much more, unless I get so many donations that I can consider working +on free software full time, which seems unlikely at best. + +To donate, log into PayPal and send money to autarch@urth.org or use the +button on this page: L + +=head1 CREDITS + +The idea for this functionality and some of its implementation was originally +created as L by Goro Fuji. + =cut