File Coverage

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


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