File Coverage

blib/lib/Test/Override/UserAgent/Scope.pm
Criterion Covered Total %
statement 92 93 98.9
branch 10 12 83.3
condition n/a
subroutine 20 20 100.0
pod 2 2 100.0
total 124 127 97.6


line stmt bran cond sub pod time code
1             package Test::Override::UserAgent::Scope;
2              
3 18     18   344 use 5.008001;
  18         149  
  18         767  
4 18     18   110 use strict;
  18         40  
  18         591  
5 18     18   103 use warnings 'all';
  18         34  
  18         1472  
6              
7             ###########################################################################
8             # METADATA
9             our $AUTHORITY = 'cpan:DOUGDUDE';
10             our $VERSION = '0.004001';
11              
12             ###########################################################################
13             # MODULE IMPORTS
14 18     18   114 use Carp qw(croak);
  18         33  
  18         1084  
15 18     18   120 use LWP::Protocol; # Not actually required here, but want it to be loaded
  18         39  
  18         567  
16 18     18   121 use Scalar::Util;
  18         46  
  18         1005  
17 18     18   98 use Sub::Install 0.90;
  18         355  
  18         117  
18 18     18   33054 use Sub::Override;
  18         19342  
  18         793  
19              
20             ###########################################################################
21             # ALL IMPORTS BEFORE THIS WILL BE ERASED
22 18     18   15598 use namespace::clean 0.04 -except => [qw(meta)];
  18         413161  
  18         175  
23              
24             ###########################################################################
25             # METHODS
26             sub scheme_implementor {
27 4     4 1 10 my ($self, $scheme) = @_;
28              
29             # Lower-case scheme
30 4         10 $scheme = lc $scheme;
31              
32 4 100       24 if (!exists $self->{_protocol_classes}->{$scheme}) {
33             # Create a new scheme implementor
34 2         11 $self->_create_scheme_implementor($scheme);
35             }
36              
37             # Return the name of the class to use
38 4         19 return $self->{_protocol_classes}->{$scheme};
39             }
40              
41             ###########################################################################
42             # CONSTRUCTOR
43             sub new {
44 4     4 1 64419 my ($class, @args) = @_;
45              
46             # Get the arguments as a plain hash
47 4 100       21 my %args = @args == 1 ? %{shift @args}
  1         5  
48             : @args
49             ;
50              
51             # Create a hash with configuration information
52 4         25 my %data = (
53             # Attributes
54             override => undef,
55              
56             # Private attributes
57             _original_implementor_lookup => undef,
58             _protocol_classes => {},
59             );
60              
61             # Set attributes
62 4         17 foreach my $arg (grep { m{\A [^_]}msx } keys %data) {
  12         48  
63 4 100       21 if (exists $args{$arg}) {
64 3         15 $data{$arg} = $args{$arg};
65             }
66             }
67              
68 4 100       26 if (!defined $data{override}) {
69 1         216 croak 'Must supply override attribute';
70             }
71              
72             # Bless the hash to this class
73 3         28 my $self = bless \%data, $class;
74              
75             # Set our unique name
76 3         47 $self->{_uniq_name} = $class . '::Number' . Scalar::Util::refaddr($self);
77              
78             # Get the current implementor lookup
79 3         12 $self->{_original_implementor_lookup} = \&LWP::Protocol::implementor;
80              
81             # Store the scope override reference
82 3         17 $self->{_scope_override} = $self->_install_in_scope;
83              
84             # Return our blessed configuration
85 3         17 return $self;
86             }
87              
88             ###########################################################################
89             # DESTRUCTOR
90             sub DESTROY {
91 3     3   2102 my ($self) = @_;
92              
93             # Destroy the override
94 3         12 undef $self->{_scope_override};
95              
96             # Destroy all the created packages
97 3         21 foreach my $scheme (keys %{$self->{_protocol_classes}}) {
  3         83  
98 2         10 $self->_destroy_scheme_implementor($scheme);
99             }
100              
101 3         314 return;
102             }
103              
104             ###########################################################################
105             # PRIVATE METHODS
106             sub _create_scheme_implementor {
107 2     2   9 my ($self, $scheme) = @_;
108              
109             # Calculate a new scheme class name
110 2         14 my $new_scheme_class = sprintf '%s::%s',
111             $self->{_uniq_name}, $scheme;
112              
113             # Install new() into the scheme class
114 2         12 Sub::Install::install_sub({
115             into => $new_scheme_class,
116             as => 'new',
117             code => $self->_generate_scheme_new,
118             });
119              
120             # Install request() into the scheme class
121 2         181 Sub::Install::install_sub({
122             into => $new_scheme_class,
123             as => 'request',
124             code => $self->_generate_scheme_request($scheme),
125             });
126              
127             # Save the name of the new class
128 2         202 $self->{_protocol_classes}->{$scheme} = $new_scheme_class;
129              
130 2         5 return $new_scheme_class;
131             }
132             sub _destroy_scheme_implementor {
133 2     2   6 my ($self, $scheme) = @_;
134              
135             # Get the package name of the scheme
136 2         6 my $package = $self->{_protocol_classes}->{$scheme};
137              
138 2 50       10 if (defined $package) {
139             # Delete new and request methods
140 2         4 undef &{$package . '::new'};
  2         18  
141 2         5 undef &{$package . '::request'};
  2         16  
142             }
143              
144 2         10 return;
145             }
146             sub _generate_scheme_new {
147 2     2   4 my ($self) = @_;
148              
149             return sub {
150 4     4   51 my ($class, $scheme, $ua) = @_;
151              
152 4         24 my $object = bless {
153             scheme => $scheme,
154             ua => $ua,
155             }, $class;
156              
157 4         14 return $object;
158             }
159 2         31 }
160             sub _generate_scheme_request {
161 2     2   7 my ($self, $scheme) = @_;
162              
163             # Copy self
164 2         4 my $weak_self = $self;
165              
166             # Weaken the self reference
167 2         9 Scalar::Util::weaken($weak_self);
168              
169             return sub {
170 4     4   86 my ($proto_self, $request, $proxy, $arg, $size, $timeout) = @_;
171              
172             # Get the override object
173 4         10 my $override = $weak_self->{override};
174              
175             # Process the request by us
176             my $response = $override->handle_request(
177             $request,
178             live_request_handler => sub {
179             # Get the normal implementor
180 1         7 my $implementor_class = $weak_self->{_original_implementor_lookup}->($scheme);
181              
182 1 50       13 if (!defined $implementor_class) {
183 0         0 croak "Protocol scheme '$scheme' is not supported";
184             }
185              
186             # Create a new instance
187 1         20 my $implementor = $implementor_class->new($proto_self->{qw(scheme ua)});
188              
189             # Make the request
190 1         21 my $live_response = $implementor->request($request, $proxy, $arg, $size, $timeout);
191              
192 1         59635 return $live_response;
193             },
194 4         39 );
195              
196 4         37 return $response;
197 2         23 };
198             }
199             sub _install_in_scope {
200 3     3   8 my ($self) = @_;
201              
202             # Get the current implementor lookup
203 3         9 my $implementor_lookup = \&LWP::Protocol::implementor;
204              
205             # Created a weakened self to allow for destruction
206 3         7 my $weak_self = $self;
207 3         13 Scalar::Util::weaken($weak_self);
208              
209             # Create an override for the current scope
210             my $override = Sub::Override->new(
211 4     4   18111 'LWP::Protocol::implementor' => sub { return $weak_self->scheme_implementor(shift); },
212 3         39 );
213              
214 3         233 return $override;
215             }
216              
217             1;
218              
219             __END__