File Coverage

blib/lib/IOC/Proxy.pm
Criterion Covered Total %
statement 110 110 100.0
branch 24 26 92.3
condition 3 5 60.0
subroutine 21 21 100.0
pod 4 4 100.0
total 162 166 97.5


line stmt bran cond sub pod time code
1              
2             package IOC::Proxy;
3              
4 10     10   146526 use strict;
  10         23  
  10         343  
5 8     8   44 use warnings;
  8         15  
  8         407  
6              
7             our $VERSION = '0.07';
8              
9 8     8   43 use Scalar::Util qw(blessed);
  8         24  
  8         881  
10              
11 8     8   2952 use IOC::Exceptions;
  8         18  
  8         4034  
12              
13             sub new {
14 12     12 1 13733 my ($_class, $config) = @_;
15 12   33     74 my $class = ref($_class) || $_class;
16 12         24 my $proxy_server = {};
17 12         25 bless($proxy_server, $class);
18 12         257 $proxy_server->_init($config);
19 11         42 return $proxy_server;
20             }
21              
22             sub _init {
23 11     11   45 my ($self, $config) = @_;
24 11         51 $self->{config} = $config;
25             }
26              
27             sub wrap {
28 14     14 1 6509 my ($self, $obj) = @_;
29 14 100       84 (blessed($obj))
30             || throw IOC::InsufficientArguments "You can only wrap other objects";
31 11         84 my $obj_class = $self->_getObjectClass($obj);
32              
33 11         21 my $methods = {};
34 11         47 $self->_collectAllMethods($obj_class, $methods);
35 11 100       19 (keys %{$methods}) || throw IOC::OperationFailed "No methods could be found in '$obj_class'";
  11         55  
36              
37 10         45 my $proxy_package = $self->_createProxyPackageName($obj_class);
38              
39 10     4   36 eval $self->_createPackageString($obj_class, $proxy_package);
  4     3   39  
  4         9  
  4         47  
  22         19460  
  22         764  
  22         145  
  3         23  
  3         13  
  3         42  
  2         1187  
  2         46  
  2         12  
40 10 50       41 throw IOC::OperationFailed "Could not create proxy package '$proxy_package' for object '$obj'" => $@ if $@;
41              
42 10         44 $self->_installMethods($obj_class, $proxy_package, $methods);
43            
44 8         34 $self->onWrap($obj, $proxy_package);
45 8         48 bless $obj, $proxy_package;
46             }
47              
48             sub onMethodCall {
49 26     48 1 63 my ($self, $method_name, $method_full_name, $current_args) = @_;
50 26 100       37 if (exists ${$self->{config}}{on_method_call}) {
  26         118  
51 23         73 $self->{config}->{on_method_call}->($self, $method_name, $method_full_name, $current_args);
52             }
53             }
54              
55             sub onWrap {
56 8     8 1 14 my ($self, $object, $proxy_package) = @_;
57 8 100       11 if (exists ${$self->{config}}{on_wrap}) {
  8         41  
58 1         4 $self->{config}->{on_wrap}->($self, $object, $proxy_package);
59             }
60             }
61              
62             # PRIVATE METHODS
63              
64             sub _collectAllMethods {
65 18     18   37 my ($self, $obj_class, $methods, $cache) = @_;
66 18   100     90 $cache ||= {};
67 18 100       26 return if exists ${$cache}{$obj_class};
  18         53  
68 17         38 $cache->{$obj_class}++;
69            
70 8     8   58 no strict 'refs';
  8         21  
  8         2703  
71 52         695 do {
72 35         241 $methods->{$_} = {
73             full_name => "${obj_class}::$_",
74 37         129 code => \&{"${obj_class}::$_"}
75 37 100       70 } unless exists ${$methods}{$_}
76 17         25 } foreach
77 52         55 grep { defined &{"${obj_class}::$_"} }
  17         72  
78             keys %{"${obj_class}::"};
79            
80 17         35 $self->_collectAllMethods($_, $methods, $cache) foreach @{"${obj_class}::ISA"};
  17         202  
81             }
82              
83             sub _createPackageString {
84 10     10   20 my ($self, $obj_class, $proxy_package) = @_;
85 10         26 my $overload = "";
86 10 100       189 unless ($obj_class->can('(""')) {
87 9         18 $overload = q|use overload '""' => sub {
88             my $real = overload::StrVal($_[0]);
89             $real =~ s/\:\:\_\:\:Proxy//;
90             return $real;
91             }, fallback => 1|;
92             }
93 10         1195 return qq|
94             package $proxy_package;
95             our \@ISA = ('$obj_class');
96             $overload;
97             |;
98             }
99              
100             sub _installMethods {
101 7     7   15 my ($self, $obj_class, $proxy_package, $methods) = @_;
102 8     8   74 no strict 'refs';
  8         15  
  8         3912  
103 7         13 while (my ($method_name, $method) = each %{$methods}) {
  29         104  
104 22 50       25 next if defined &{"${proxy_package}::$method_name"};
  22         132  
105 22 100       45 next if $method_name eq '()'; # this is the overloaded indicator, we shouldn't proxy this
106 21 100       54 if ($method_name eq 'AUTOLOAD') {
    100          
107 1         5 *{"${proxy_package}::$method_name"} = sub {
108 2     2   1478 my $a = our $AUTOLOAD;
109             # we cannot call this here as it will create a new
110             # reference to the object (in the \@_) and that will
111             # defeat the use of DESTORY, so we just ignore this
112             # if we get called by destroy
113 2 100       10 if ($a =~ /DESTROY/) {
114 1         4 $self->onMethodCall($method_name, $method->{full_name}, []);
115             }
116             else {
117 1         9 $self->onMethodCall($method_name, $method->{full_name}, [ @_ ]);
118             }
119 2         43 $a =~ s/\:\:\_\:\:Proxy//;
120 2         4 ${"${obj_class}::AUTOLOAD"} = $a;
  2         7  
121 2         4 goto &{$method->{code}};
  2         9  
122 1         5 };
123             }
124             elsif ($method_name eq 'DESTROY') {
125 1         6 *{"${proxy_package}::$method_name"} = sub {
126             # we cannot call onMethodCall this here as it will create a new
127             # reference to the object (in the \@_) and that will
128             # defeat the use of DESTORY, so we just ignore this
129             # if we get called by destroy
130 1     1   1978 $self->onMethodCall($method_name, $method->{full_name}, []);
131 1         6 goto &{$method->{code}};
  1         8  
132 1         3 };
133             }
134             else {
135 19         111 *{"${proxy_package}::$method_name"} = sub {
136 21     21   28483 $self->onMethodCall($method_name, $method->{full_name}, [ @_ ]);
137 21         95 goto &{$method->{code}};
  21         90  
138 19         70 };
139             }
140             }
141             }
142              
143             sub _createProxyPackageName {
144 10     10   17 my ($self, $obj_class) = @_;
145 10         29 return "${obj_class}::_::Proxy"
146             }
147              
148             sub _getObjectClass {
149 11     11   19 my ($self, $obj) = @_;
150 11         29 return ref($obj);
151             }
152              
153             1;
154              
155             __END__