File Coverage

blib/lib/Module/Replace.pm
Criterion Covered Total %
statement 55 57 96.4
branch 8 14 57.1
condition 2 6 33.3
subroutine 10 10 100.0
pod 2 2 100.0
total 77 89 86.5


line stmt bran cond sub pod time code
1             package Module::Replace;
2              
3 2     2   35252 use warnings;
  2         6  
  2         103  
4 2     2   11 use strict;
  2         3  
  2         533  
5              
6             #use 5.010;
7              
8             =head1 NAME
9              
10             Module::Replace - Replace functionality in other modules
11              
12             =head1 VERSION
13              
14             Version 0.01
15              
16             =cut
17              
18             our $VERSION = '0.02';
19              
20              
21             =head1 SYNOPSIS
22              
23             use Module::Replace 'Other::Module' => qw(new);
24              
25             =head1 DESCRIPTION
26              
27             The purpose of this module is to allow you to override functions in one
28             module with the same-named functions in another.
29              
30             This can be a global change, or a temporary change.
31              
32             The reasons why you may want to do this include:
33              
34             =over 4
35              
36             =item *
37              
38             Changing the behaviour of code you don't own by changing what it calls.
39              
40             For example, if you're using the popular Foo::Framework class, and you want
41             to change what object it retrieves when it calls Foo::Object->new, you can
42             simply replace Foo::Object::new with your own new which would then create
43             your object, presumably derived from Foo::Object.
44              
45             =item *
46              
47             Building a general framework that doesn't rely on the user specifying what
48             objects to create. Here you merely tell the user to:
49              
50             use Module::Replace 'YourFramework::Type', qw(new);
51              
52             in their derived package and this will allow your framework to stay
53             blissfully unaware of who is deriving from you in the current application.
54              
55             Note that this doesn't help when you want multiple derivations from the
56             same type. A real factory is still required at that point.
57              
58             =back
59              
60             =head1 USAGE
61              
62             There are two types of usage: global and local replacement.
63              
64             =over 4
65              
66             =item Global replacement
67              
68             This is primarily targetted at frameworks. Here you call:
69              
70             use Module::Replace 'YourFramework::Type', qw(new);
71              
72             from within the derived object. This will both call C
73             and override new with your own. Note that access to the original new method
74             is still available via SUPER_new, e.g.:
75              
76             sub new {
77             my $class = shift;
78             # allow re-derivations
79             $class = __PACKAGE__ if $class eq 'YourFramework::Type';
80             my $self = bless $class->SUPER_new(), $class;
81             # ...
82             }
83              
84             =item Local replacement
85              
86             Sometimes you only want to replace a function for a little while. For
87             example, changing the way that File::Spec::catdir works only when calling
88             another function. Here you call the replace and restore functions directly.
89              
90             use Module::Replace;
91             Module::Replace::replace('File::Spec', \'File::Spec::UNIX', qw(catdir));
92             Some::Other::function();
93             Module::Replace::restore('File::Spec', \'File::Spec::UNIX');
94              
95             Note that if you leave off the reference to the source package, it will assume
96             the caller package.
97              
98             This will cause catdir to work UNIX-like on all platforms for the duration
99             of C.
100              
101             It is up to you to ensure that exceptions are handled so that the methods
102             are restored at the proper time.
103              
104             =back
105              
106             =head1 FUNCTIONS
107              
108             =over 4
109              
110             =cut
111              
112             sub import
113             {
114 2     2   17 my $self = shift;
115 2         3 my $class = shift;
116 2         14 my ($caller) = caller(0);
117              
118 2 100       20 return 1 unless $class;
119              
120             # first, load the module, and make the caller derived from it.
121             # 'base' does a lot of work - let's abuse that.
122 1     1   71 eval qq[
  1         4  
  1         8  
  1         1025  
123             package $caller;
124             use base '$class';
125             ];
126 1 50       5 die $@ if $@;
127 1 50       9 replace($class, \$caller, @_) if @_;
128 1         79 1;
129             }
130              
131             =item replace
132              
133             Input:
134              
135             =over 4
136              
137             =item 1
138              
139             Package to replace.
140              
141             =item 2
142              
143             Reference to package that contains the wanted function (optional - defaults
144             to caller's package)
145              
146             =item 3
147              
148             List of functions to replace. Each function will be renamed to SUPER_$func
149             so that the overridden function will work
150              
151             =back
152              
153             =cut
154              
155             my %overrides;
156             sub replace
157             {
158 1     1 1 1 my $class = shift;
159 1 50 33     11 my ($caller) = ref $_[0] && ref $_[0] eq 'SCALAR' ? ${shift()} : caller(0);
  1         3  
160              
161             # now, replace desired methods.
162 1         2 for my $func (@_)
163             {
164 2     2   13 no strict 'refs';
  2         19  
  2         55  
165 2     2   17 no warnings;
  2         3  
  2         472  
166 1         4 local ($^W) = 0; # in case "-w" is used
167 1 50       1 *{"${class}::SUPER_$func"} = \&{"${class}::$func"} if ${"${class}::"}{$func};
  1         6  
  1         3  
  1         6  
168 1         2 *{"${class}::$func"} = \&{"${caller}::$func"};
  1         4  
  1         5  
169             # keep track of what was overridden for reversals.
170 1         7 $overrides{$caller}{$class}{$func}++;
171             }
172             }
173              
174             =item restore
175              
176             Input:
177              
178             =over 4
179              
180             =item 1
181              
182             Package that is overridden
183              
184             =item 2
185              
186             Reference to package that contains the wanted function (optional - defaults
187             to caller's package)
188              
189             =back
190              
191             =cut
192              
193             sub restore
194             {
195 1     1 1 833 my $class = shift;
196 1 50 33     15 my ($caller) = ref $_[0] && ref $_[0] eq 'SCALAR' ? ${shift()} : caller(0);
  1         4  
197              
198 1         4 for my $func (keys %{$overrides{$caller}{$class}})
  1         6  
199             {
200 2     2   10 no strict 'refs';
  2         4  
  2         51  
201 2     2   10 no warnings;
  2         9  
  2         361  
202 1         6 local ($^W) = 0; # in case "-w" is used
203 1 50       2 if (exists ${"${class}::"}{"SUPER_$func"})
  1         6  
204             {
205 1         2 *{"${class}::$func"} = \&{"${class}::SUPER_$func"};
  1         8  
  1         4  
206 1         2 delete ${"${class}::"}{"SUPER_$func"};
  1         10  
207             }
208             else
209             {
210 0         0 delete ${"${class}::"}{$func};
  0         0  
211             }
212             }
213             }
214              
215             =back
216              
217             =head1 AUTHOR
218              
219             Darin McBride, C<< >>
220              
221             =head1 BUGS
222              
223             Please report any bugs or feature requests to C, or through
224             the web interface at L. I will be notified, and then you'll
225             automatically be notified of progress on your bug as I make changes.
226              
227              
228              
229              
230             =head1 SUPPORT
231              
232             You can find documentation for this module with the perldoc command.
233              
234             perldoc Module::Replace
235              
236              
237             You can also look for information at:
238              
239             =over 4
240              
241             =item * RT: CPAN's request tracker
242              
243             L
244              
245             =item * AnnoCPAN: Annotated CPAN documentation
246              
247             L
248              
249             =item * CPAN Ratings
250              
251             L
252              
253             =item * Search CPAN
254              
255             L
256              
257             =back
258              
259              
260             =head1 ACKNOWLEDGEMENTS
261              
262              
263             =head1 COPYRIGHT & LICENSE
264              
265             Copyright 2008 Darin McBride, all rights reserved.
266              
267             This program is free software; you can redistribute it and/or modify it
268             under the same terms as Perl itself.
269              
270              
271             =cut
272              
273             1; # End of Module::Replace