File Coverage

blib/lib/Class/MethodFilter.pm
Criterion Covered Total %
statement 38 38 100.0
branch 9 10 90.0
condition n/a
subroutine 10 10 100.0
pod 0 1 0.0
total 57 59 96.6


line stmt bran cond sub pod time code
1             package Class::MethodFilter;
2              
3 1     1   172341 use strict;
  1         3  
  1         63  
4 1     1   8 use warnings;
  1         2  
  1         38  
5 1     1   5 use vars qw/$VERSION/;
  1         7  
  1         53  
6 1     1   6 use Carp;
  1         2  
  1         207  
7              
8             $VERSION = "0.02";
9              
10             =head1 NAME
11              
12             Class::MethodFilter - add filters to class methods
13              
14             =head1 SYNOPSIS
15              
16             package SomeClass;
17              
18             sub foo {
19             return "foo!\n";
20             }
21              
22             __PACKAGE__->add_method_filter('foo', sub { $_[1] =~ tr/a-z/A-Z/; $_[1]; });
23              
24             # Meanwhile, in another piece of code ...
25              
26             my $obj = new SomeClass;
27             print $obj->foo(); # Prints "foo!"
28             print $obj->foo_filtered(); # Prints "FOO!"
29              
30             =head1 DESCRIPTION
31              
32             Class::MethodFilter is designed for situations where you want a filtered
33             version of the return values of a method that's separate from the method
34             itself; it provides a convenient API for installing filters written different
35             ways while leaving the eventual interface consistent.
36              
37             =head1 DETAILS
38              
39             A single additional class method is added to your package, add_method_filter.
40             It can be called with (so far) three different signatures -
41              
42             __PACKAGE__->add_method_filter('method', sub { ... } );
43             __PACKAGE__->add_method_filter('method', 'filter_method');
44             __PACKAGE__->add_method_filter('method');
45              
46             The first form installs the supplied sub as __PACKAGE__::method_filter, the
47             second creates an __PACKAGE__::method_filter that proxies the call to the named
48             method, and the third assumes that __PACKAGE__::method_filter already exists.
49              
50             If __PACKAGE__::method_filtered does not exist, one is created that calls
51              
52             $_[0]->method_filter($_[0]->method(@_));
53              
54             and returns the result. If it *does* exist, it is assumed to have accessor-like
55             behaviour (as from e.g. use of Class::Accessor) and Class::MethodFilter
56             replaces __PACKAGE__::method with a sub that calls $_[0]->method_filtered(@_)
57             and then returns the result of invoking the original method. This is designed
58             to allow __PACKAGE__::method_filtered to act as a cache for the filtered
59             result which is automatically updated every time the method it's filtering is
60             called.
61              
62             =head1 AUTHOR
63              
64             Matt S Trout
65              
66             =head1 LICENSE
67              
68             This library is free software; you can redistribute it and/or modify
69             it under the same terms as Perl itself.
70              
71             =cut
72              
73             sub add_method_filter {
74 4     4 0 4157 my ($package, $method, $filter) = @_;
75 4 50       30 croak "Can't filter nonexistant method $method on $package!"
76             unless $package->can($method);
77 4         12 my $m_filter = "${method}_filter";
78 4         6 my $m_filtered = "${method}_filtered";
79 1     1   5 no strict 'refs';
  1         3  
  1         1333  
80 4 100       12 if (defined $filter) {
81 3         17 *{"${package}::${m_filter}"} =
82             (ref $filter eq 'CODE'
83             ? $filter
84 3 100   1   16 : sub { $_[0]->$filter(@_[1..$#_]); } );
  1         10  
85             }
86 4 100       31 if ($package->can($m_filtered)) {
87 1         2 my $cr = *{"${package}::${method}"}{CODE};
  1         4  
88 1     1   9 no warnings qw/redefine/;
  1         1  
  1         468  
89 1         8 *{"${package}::${method}"} =
90 3     3   143 sub { my @args = @_;
91 3 100       9 if ($#args > 0) {
92 1         6 $args[0]->$m_filtered($args[0]->$m_filter(@args[1..$#args]));
93             }
94 3         16 return &$cr;
95 1         137 };
96             } else {
97 3         14340 *{"${package}::${m_filtered}"} =
98 3     3   13 sub { $_[0]->$m_filter($_[0]->$method(@_)); };
  3         283  
99             }
100             }
101              
102             1;
103