File Coverage

blib/lib/Class/C3/Adopt/NEXT.pm
Criterion Covered Total %
statement 56 56 100.0
branch 19 20 95.0
condition 8 11 72.7
subroutine 13 13 100.0
pod n/a
total 96 100 96.0


line stmt bran cond sub pod time code
1 7     7   216312 use strict;
  7         18  
  7         239  
2 7     7   36 use warnings;
  7         12  
  7         365  
3              
4             package Class::C3::Adopt::NEXT;
5             BEGIN {
6 7     7   178 $Class::C3::Adopt::NEXT::AUTHORITY = 'cpan:FLORA';
7             }
8             BEGIN {
9 7     7   226 $Class::C3::Adopt::NEXT::VERSION = '0.13';
10             }
11             # ABSTRACT: make NEXT suck less
12              
13 7     7   6239 use NEXT;
  7         40412  
  7         222  
14 7     7   7232 use MRO::Compat;
  7         30067  
  7         342  
15 7     7   8267 use List::MoreUtils qw/none/;
  7         11892  
  7         755  
16 7     7   50 use warnings::register;
  7         15  
  7         1317  
17              
18              
19             {
20             my %c3_mro_ok;
21             my %warned_for;
22             my @no_warn_regexes;
23              
24             {
25             my $orig = NEXT->can('AUTOLOAD');
26              
27 7     7   37 no warnings 'redefine';
  7         13  
  7         4182  
28             *NEXT::AUTOLOAD = sub {
29 26   66 26   8127 my $class = ref $_[0] || $_[0];
30 26         202 my $caller = caller();
31              
32             # 'NEXT::AUTOLOAD' is cargo-culted from C::P::C3, I have no idea if/why it's needed
33 26   50     516 my $wanted = our $AUTOLOAD || 'NEXT::AUTOLOAD';
34 26         283 my ($wanted_class) = $wanted =~ m{(.*)::};
35              
36 26 100       89 unless (exists $c3_mro_ok{$class}) {
37 6         15 eval { mro::get_linear_isa($class, 'c3') };
  6         208  
38 6 100       40 if (my $error = $@) {
39 1         22 warn "Class::C3::calculateMRO('${class}') Error: '${error}';"
40             . ' Falling back to plain NEXT.pm behaviour for this class';
41 1         9 $c3_mro_ok{$class} = 0;
42             }
43             else {
44 5         16 $c3_mro_ok{$class} = 1;
45             }
46             }
47              
48 26 100 100     159 if (length $c3_mro_ok{$class} && $c3_mro_ok{$class}) {
49 22 100       58 unless ($warned_for{$caller}) {
50 12         24 $warned_for{$caller} = 1;
51 12 100 66 3   59 if (!@no_warn_regexes || none { $caller =~ $_ } @no_warn_regexes) {
  3         23  
52 9         458 warnings::warnif("${caller} uses NEXT, which is deprecated. Please see "
53             . "the Class::C3::Adopt::NEXT documentation for details. NEXT used ");
54             }
55             }
56             }
57              
58 26 100       3361 unless ($c3_mro_ok{$class}) {
59 4         8 $NEXT::AUTOLOAD = $wanted;
60 4         29 goto &$orig;
61             }
62              
63 22 100       74 goto &next::method if $wanted_class =~ /^NEXT:.*:ACTUAL/;
64 19         79 goto &maybe::next::method;
65             };
66              
67             *NEXT::ACTUAL::AUTOLOAD = \&NEXT::AUTOLOAD;
68             }
69              
70             sub import {
71 8     8   73 my ($class, @args) = @_;
72 8         23 my $target = caller();
73              
74 8         2269 for my $arg (@args) {
75 1 50       1789 $warned_for{$target} = 1
76             if $arg eq '-no_warn';
77             }
78             }
79              
80             sub unimport {
81 2     2   14310 my $class = shift;
82 2 100       6 my @strings = grep { !ref $_ || ref($_) ne 'Regexp' } @_;
  4         25  
83 2 100       5 my @regexes = grep { ref($_) && ref($_) eq 'Regexp' } @_;
  4         19  
84 2         10 @c3_mro_ok{@strings} = ('') x @strings;
85 2         3756 push @no_warn_regexes, @regexes;
86             }
87             }
88              
89             1;
90              
91             __END__
92             =pod
93              
94             =head1 NAME
95              
96             Class::C3::Adopt::NEXT - make NEXT suck less
97              
98             =head1 SYNOPSIS
99              
100             package MyApp::Plugin::FooBar;
101             #use NEXT;
102             use Class::C3::Adopt::NEXT;
103             # or 'use Class::C3::Adopt::NEXT -no_warn;' to suppress warnings
104              
105             # Or use warnings::register
106             # no warnings 'Class::C3::Adopt::NEXT';
107              
108             # Or suppress warnings in a set of modules from one place
109             # no Class::C3::Adopt::NEXT qw/ Module1 Module2 Module3 /;
110             # Or suppress using a regex
111             # no Class::C3::Adopt::NEXT qr/^Module\d$/;
112              
113             sub a_method {
114             my ($self) = @_;
115             # Do some stuff
116              
117             # Re-dispatch method
118             # Note that this will generate a warning the _first_ time the package
119             # uses NEXT unless you un comment the 'no warnings' line above.
120             $self->NEXT::method();
121             }
122              
123             =head1 DESCRIPTION
124              
125             L<NEXT> was a good solution a few years ago, but isn't any more. It's slow,
126             and the order in which it re-dispatches methods appears random at times. It
127             also encourages bad programming practices, as you end up with code to
128             re-dispatch methods when all you really wanted to do was run some code before
129             or after a method fired.
130              
131             However, if you have a large application, then weaning yourself off C<NEXT>
132             isn't easy.
133              
134             This module is intended as a drop-in replacement for NEXT, supporting the same
135             interface, but using L<Class::C3> to do the hard work. You can then write new
136             code without C<NEXT>, and migrate individual source files to use C<Class::C3>
137             or method modifiers as appropriate, at whatever pace you're comfortable with.
138              
139             =head1 WARNINGS
140              
141             This module will warn once for each package using NEXT. It uses
142             L<warnings::register>, and so can be disabled like by adding C<no warnings
143             'Class::C3::Adopt::NEXT';> to each package which generates a warning, or adding
144             C<use Class::C3::Adopt::NEXT -no_warn;>, or disable multiple modules at once by
145             saying:
146              
147             no Class::C3::Adopt::NEXT qw/ Module1 Module2 Module3 /;
148              
149             somewhere before the warnings are first triggered. You can also setup entire
150             name spaces of modules which will not warn using a regex, e.g.
151              
152             no Class::C3::Adopt::NEXT qr/^Module\d$/;
153              
154             =head1 MIGRATING
155              
156             =head2 Current code using NEXT
157              
158             You add C<use MRO::Compat> to the top of a package as you start converting it,
159             and gradually replace your calls to C<NEXT::method()> with
160             C<maybe::next::method()>, and calls to C<NEXT::ACTUAL::method()> with
161             C<next::method()>.
162              
163             Example:
164              
165             sub yourmethod {
166             my $self = shift;
167              
168             # $self->NEXT::yourmethod(@_); becomes
169             $self->maybe::next::method();
170             }
171              
172             sub othermethod {
173             my $self = shift;
174              
175             # $self->NEXT::ACTUAL::yourmethodname(); becomes
176             $self->next::method();
177             }
178              
179             On systems with L<Class::C3::XS> present, this will automatically be used to
180             speed up method re-dispatch. If you are running perl version 5.9.5 or greater
181             then the C3 method resolution algorithm is included in perl. Correct use of
182             L<MRO::Compat> as shown above allows your code to be seamlessly forward and
183             backwards compatible, taking advantage of native versions if available, but
184             falling back to using pure perl C<Class::C3>.
185              
186             =head2 Writing new code
187              
188             Use L<Moose> and make all of your plugins L<Moose::Roles|Moose::Role>, then use
189             method modifiers to wrap methods.
190              
191             Example:
192              
193             package MyApp::Role::FooBar;
194             use Moose::Role;
195              
196             before 'a_method' => sub {
197             my ($self) = @_;
198             # Do some stuff
199             };
200              
201             around 'a_method' => sub {
202             my $orig = shift;
203             my $self = shift;
204             # Do some stuff before
205             my $ret = $self->$orig(@_); # Run wrapped method (or not!)
206             # Do some stuff after
207             return $ret;
208             };
209              
210             package MyApp;
211             use Moose;
212              
213             with 'MyApp::Role::FooBar';
214              
215             =head1 CAVEATS
216              
217             There are some inheritance hierarchies that it is possible to create which
218             cannot be resolved to a simple C3 hierarchy. In that case, this module will
219             fall back to using C<NEXT>. In this case a warning will be emitted.
220              
221             Because calculating the MRO of every class every time C<< ->NEXT::foo >> is
222             used from within it is too expensive, runtime manipulations of C<@ISA> are
223             prohibited.
224              
225             =head1 FUNCTIONS
226              
227             This module replaces C<NEXT::AUTOLOAD> with it's own version. If warnings are
228             enabled then a warning will be emitted on the first use of C<NEXT> by each
229             package.
230              
231             =head1 SEE ALSO
232              
233             L<MRO::Compat> and L<Class::C3> for method re-dispatch and L<Moose> for method
234             modifiers and L<roles|Moose::Role>.
235              
236             L<NEXT> for documentation on the functionality you'll be removing.
237              
238             =begin Pod::Coverage
239              
240             import
241              
242             unimport
243              
244             =end Pod::Coverage
245              
246             =head1 AUTHORS
247              
248             Florian Ragwitz <rafl@debian.org>
249             Tomas Doran <bobtfish@bobtfish.net>
250              
251             =head1 COPYRIGHT AND LICENSE
252              
253             This software is copyright (c) 2010 by Florian Ragwitz.
254              
255             This is free software; you can redistribute it and/or modify it under
256             the same terms as the Perl 5 programming language system itself.
257              
258             =cut
259