File Coverage

blib/lib/MRO/Magic.pm
Criterion Covered Total %
statement 81 82 98.7
branch 18 20 90.0
condition 4 5 80.0
subroutine 17 18 94.4
pod n/a
total 120 125 96.0


line stmt bran cond sub pod time code
1             package MRO::Magic 0.100002;
2 6     6   277650 use 5.010; # uvar magic does not work prior to version 10
  6         64  
3 6     6   34 use strict;
  6         8  
  6         134  
4 6     6   28 use warnings;
  6         11  
  6         201  
5             # ABSTRACT: write your own method dispatcher
6              
7 6     6   31 use mro;
  6         21  
  6         72  
8 6     6   3326 use MRO::Define;
  6         2847  
  6         236  
9 6     6   41 use Scalar::Util qw(reftype);
  6         11  
  6         334  
10 6     6   2920 use Variable::Magic qw/wizard cast/;
  6         6727  
  6         1214  
11              
12             #pod =head1 WARNING
13             #pod
14             #pod First off, at present (2009-05-25) this code requires a development version of
15             #pod perl. It should run on perl5 v10.1, but that isn't out yet, so be patient or
16             #pod install a development perl.
17             #pod
18             #pod Secondly, the API is not guaranteed to change in massive ways. This code is
19             #pod the result of playing around, not of careful design.
20             #pod
21             #pod Finally, using MRO::Magic anywhere will impact the performance of I of
22             #pod your program. Every time a method is called via MRO::Magic, the entire method
23             #pod resolution class for all classes is cleared.
24             #pod
25             #pod B
26             #pod
27             #pod =head1 USAGE
28             #pod
29             #pod First you write a method dispatcher.
30             #pod
31             #pod package MRO::Classless;
32             #pod use MRO::Magic
33             #pod metamethod => \'invoke_method',
34             #pod passthru => [ qw(VERSION import unimport DESTROY) ];
35             #pod
36             #pod sub invoke_method {
37             #pod my ($invocant, $method_name, $args) = @_;
38             #pod
39             #pod ...
40             #pod
41             #pod return $rv;
42             #pod }
43             #pod
44             #pod In a class using this dispatcher, any method not in the passthru specification
45             #pod is redirected to C, which can do any kind of ridiculous thing it
46             #pod wants.
47             #pod
48             #pod Now you use the dispatcher:
49             #pod
50             #pod package MyDOM;
51             #pod use MRO::Classless;
52             #pod use mro 'MRO::Classless';
53             #pod 1;
54             #pod
55             #pod ...and...
56             #pod
57             #pod use MyDOM;
58             #pod
59             #pod my $dom = MyDOM->new(type => 'root');
60             #pod
61             #pod The C call will actually result in a call to C in the form:
62             #pod
63             #pod invoke_method('MyDOM', 'new', [ type => 'root' ]);
64             #pod
65             #pod Assuming it returns an object blessed into MyDOM, then:
66             #pod
67             #pod $dom->children;
68             #pod
69             #pod ...will redispatch to:
70             #pod
71             #pod invoke_method($dom, 'children', []);
72             #pod
73             #pod For examples of more practical use, look at the test suite.
74             #pod
75             #pod =cut
76              
77             sub import {
78 8     8   2176 my $self = shift;
79 8         14 my $arg;
80              
81 8 100 66     62 if (@_ == 1 and reftype $_[0] eq 'CODE') {
82 2         7 $arg = { metamethod => $_[0] };
83             } else {
84 6         25 $arg = { @_ };
85             }
86              
87 8         24 my $caller = caller;
88 8         15 my %to_install;
89              
90 8         118 my $code = $arg->{metamethod};
91 8   100     47 my $metamethod = $arg->{metamethod_name} || '__metamethod__';
92              
93 8 100       60 if (reftype $code eq 'SCALAR') {
94 4 100       52 Carp::confess("can't find metamethod via name ${ $arg->{metamethod} }")
  1         117  
95             unless $code = $caller->can($$code);
96             }
97              
98 6 100   6   49 if (do { no strict 'refs'; defined *{"$caller\::$metamethod"}{CODE} }) {
  6         11  
  6         921  
  7         16  
  7         12  
  7         58  
99 1         234 Carp::confess("can't install metamethod as $metamethod; already defined");
100             }
101              
102 6         12 my $method_name;
103              
104             my $wiz = wizard
105             copy_key => 1,
106 6     6   262 data => sub { \$method_name },
107             fetch => $self->_gen_fetch_magic({
108             metamethod => $metamethod,
109             passthru => $arg->{passthru},
110 6         42 });
111              
112             $to_install{ $metamethod } = sub {
113 105     105   186 my $invocant = shift;
114 105         330 $code->($invocant, $method_name, \@_);
115 6         255 };
116              
117 6     6   41 no strict 'refs';
  6         14  
  6         2268  
118 6         25 for my $key (keys %to_install) {
119 6         13 *{"$caller\::$key"} = $to_install{ $key };
  6         27  
120             }
121              
122 6 100       23 if ($arg->{overload}) {
123 1         2 my %copy = %{ $arg->{overload} };
  1         4  
124 1         2 for my $ol (keys %copy) {
125 2 100       6 next if $ol eq 'fallback';
126 1 50       3 next if ref $copy{ $ol };
127            
128 1         2 my $name = $copy{ $ol };
129             $copy{ $ol } = sub {
130 0     0   0 $_[0]->$name(@_[ 1 .. $#_ ]);
131 1         3 };
132             }
133              
134             # We need string eval to set the caller to a variable. -- rjbs, 2009-03-26
135             # We must do this before casting magic so that overload.pm can find the
136             # right entries in the stash to muck with. -- rjbs, 2009-03-26
137 1 50   1   67 die unless eval qq{
  1         1211  
  1         989  
  1         7  
138             package $caller;
139             use overload %copy;
140             1;
141             };
142             }
143              
144             MRO::Define::register_mro($caller, sub {
145 88     88   25579 return [ undef, $caller ];
146 6         47 });
147              
148 6         10 cast %{"::$caller\::"}, $wiz;
  6         45  
149             }
150              
151             sub _gen_fetch_magic {
152 6     6   18 my ($self, $arg) = @_;
153              
154 6         11 my $metamethod = $arg->{metamethod};
155 6         12 my $passthru = $arg->{passthru};
156              
157 6     6   3864 use Data::Dumper;
  6         41153  
  6         1595  
158             return sub {
159 146 100   146   119615 return if $_[2] ~~ $passthru;
160              
161 137 100       472 return if substr($_[2], 0, 1) eq '(';
162              
163 124         189 ${ $_[1] } = $_[2];
  124         311  
164 124         216 $_[2] = $metamethod;
165 124         402 mro::method_changed_in('UNIVERSAL');
166              
167 124         143870 return;
168 6         35 };
169             }
170              
171             1;
172              
173             __END__