File Coverage

blib/lib/Rose/URI.pm
Criterion Covered Total %
statement 185 214 86.4
branch 103 132 78.0
condition 18 45 40.0
subroutine 31 34 91.1
pod 17 20 85.0
total 354 445 79.5


line stmt bran cond sub pod time code
1             package Rose::URI;
2              
3 2     2   44858 use strict;
  2         4  
  2         74  
4              
5 2     2   10 use Carp();
  2         5  
  2         57  
6 2     2   1602 use URI::Escape();
  2         3024  
  2         51  
7              
8 2     2   1336 use Rose::Object;
  2         344  
  2         159  
9             our @ISA = qw(Rose::Object);
10              
11             use overload
12             (
13 17     17   2243 '""' => sub { shift->as_string },
14 0     0   0 'bool' => sub { length shift->as_string },
15 2         21 fallback => 1,
16 2     2   3109 );
  2         2215  
17              
18             our $Make_URI;
19              
20             our $SCHEME_RE = '[a-zA-Z][a-zA-Z0-9.+\-]*';
21              
22             our $VERSION = '1.00';
23              
24             # Class data
25             use Rose::Class::MakeMethods::Generic
26             (
27 2         27 inheritable_scalar =>
28             [
29             'default_query_param_separator',
30             'default_omit_empty_query_params',
31             ],
32 2     2   2536 );
  2         19841  
33              
34             # Object data
35             use Rose::Object::MakeMethods::Generic
36             (
37 2         39 scalar =>
38             [
39             'username',
40             'password',
41             'scheme',
42             'host',
43             'port',
44             'path',
45             'fragment',
46             'query_param_separator' => { interface => 'get_set_init' },
47             ],
48 2     2   2799 );
  2         15323  
49              
50             __PACKAGE__->default_query_param_separator('&');
51             __PACKAGE__->default_omit_empty_query_params(0);
52              
53 23     23 0 306 sub init_query_param_separator { ref(shift)->default_query_param_separator }
54              
55             sub new
56             {
57 23     23 1 1927 my($class) = shift;
58              
59 23         352 my $self =
60             {
61             username => '',
62             password => '',
63             scheme => '',
64             host => '',
65             port => '',
66             path => '',
67             query => {},
68             fragment => '',
69             };
70              
71 23         142 bless $self, $class;
72              
73 23         69 $self->init(@_);
74              
75 23         120 return $self;
76             }
77              
78             sub init
79             {
80 23     23 1 36 my($self) = shift;
81              
82 23 50       53 if(@_ == 1)
83             {
84 23         65 $self->init_with_uri(@_);
85             }
86             else
87             {
88 0         0 $self->SUPER::init(@_);
89             }
90             }
91              
92             sub init_with_uri
93             {
94 23     23 0 35 my($self) = shift;
95              
96 23         67 $self->$Make_URI($_[0]);
97             }
98              
99             sub clone
100             {
101 1     1 1 3 my($self) = shift;
102 1         3 return bless _deep_copy($self), ref($self);
103             }
104              
105             sub parse_query
106             {
107 27     27 0 278 my($self, $query) = @_;
108              
109 27         52 $self->{'query_string'} = undef;
110              
111 27 50 33     184 unless(defined $query && $query =~ /\S/)
112             {
113 0         0 $self->{'query'} = { };
114 0         0 return 1;
115             }
116              
117 27         34 my @params;
118              
119 27 100       94 if(index($query, '&') >= 0)
    100          
    50          
120             {
121 22         110 @params = split(/&/, $query);
122             }
123             elsif(index($query, ';') >= 0)
124             {
125 3         12 @params = split(/;/, $query);
126             }
127             elsif(index($query, '=') < 0)
128             {
129 2         5 $self->{'query_string'} = __unescape_uri($query);
130 2         8 $self->{'query'} = { $self->{'query_string'} => undef };
131 2         7 return 1;
132             }
133              
134 25 50       63 @params = ($query) unless(@params);
135              
136 25         37 my %query;
137              
138 25         50 foreach my $item (@params)
139             {
140 78         169 my($param, $value) = map { __unescape_uri($_) } split(/=/, $item);
  154         258  
141              
142 78 50       186 $param = __unescape_uri($item) unless(defined($param));
143              
144 78 100       288 if(exists $query{$param})
145             {
146 25 50       71 if(ref $query{$param})
147             {
148 0         0 push(@{$query{$param}}, $value);
  0         0  
149             }
150             else
151             {
152 25         101 $query{$param} = [ $query{$param}, $value ];
153             }
154             }
155             else
156             {
157 53         294 $query{$param} = $value;
158             }
159             }
160              
161 25         61 $self->{'query'} = \%query;
162              
163 25         73 return 1;
164             }
165              
166             sub query_hash
167             {
168 2     2 1 487 my($self) = shift;
169              
170 2 100       6 return (wantarray) ? %{$self->{'query'}} : { %{$self->{'query'}} };
  1         5  
  1         7  
171             }
172              
173             sub omit_empty_query_params
174             {
175 6     6 1 689 my($self) = shift;
176              
177 6 100       15 if(@_)
178             {
179 1 50       5 return $self->{'omit_empty_query_params'} = $_[0] ? 1 : 0;
180             }
181              
182 5 100       22 return defined $self->{'omit_empty_query_params'} ?
183             $self->{'omit_empty_query_params'} : ref($self)->default_omit_empty_query_params;
184             }
185              
186             sub query_param
187             {
188 132     132 1 990 my($self) = shift;
189              
190 132 100       311 if(@_ == 1)
    50          
191             {
192 117 100       3029 return $self->{'query'}{$_[0]} if(exists $self->{'query'}{$_[0]});
193 2         3 return;
194             }
195             elsif(@_ == 2)
196             {
197 15         22 $self->{'query_string'} = undef;
198              
199 15 100       36 if(ref $_[1])
200             {
201 6         8 return $self->{'query'}{$_[0]} = [ @{$_[1]} ];
  6         22  
202             }
203              
204 9         30 return $self->{'query'}{$_[0]} = $_[1];
205             }
206              
207 0         0 Carp::croak "query_param() takes either one or two arguments";
208             }
209              
210             sub query_params
211             {
212 119     119 1 713 my($self) = shift;
213              
214 119 100       221 return sort keys %{$self->{'query'}} unless(@_);
  1         8  
215              
216 118         204 my $params = $self->query_param(@_);
217              
218 118 100       484 $params = (ref $params) ? [ @$params ] :
    100          
219             (defined $params) ? [ $params ] : [];
220              
221 118 100       475 return (wantarray) ? @$params : $params;
222             }
223              
224             sub query_param_add
225             {
226 5     5 1 481 my($self, $name, $value) = @_;
227              
228 5 50       11 Carp::croak "query_add_param() takes two arguments" unless(@_ == 3);
229              
230 5         8 my $params = $self->query_params($name);
231              
232 5 100       13 push(@$params, (ref $value) ? @$value : $value);
233              
234 5 100       14 $self->query_param($name => (@$params > 1) ? $params : $params->[0]);
235              
236 5 50       18 return (wantarray) ? @$params : $params;
237             }
238              
239             sub query_param_exists
240             {
241 7     7 1 860 my($self, $param) = @_;
242              
243 7 50       15 Carp::croak "Missing query param argument" unless(defined $param);
244              
245 7         28 return exists $self->{'query'}{$param};
246             }
247              
248             sub query_param_delete
249             {
250 3     3 1 5 my($self) = shift;
251              
252 3 50       9 Carp::croak "query_param_delete() takes one or more arguments" unless(@_);
253              
254 3         5 foreach my $param (@_)
255             {
256 3 100 100     15 if(defined $self->{'query_string'} && $param eq $self->{'query_string'})
257             {
258 1         1 $self->{'query_string'} = undef;
259             }
260              
261 3         13 delete $self->{'query'}{$param};
262             }
263             }
264              
265             sub as_string
266             {
267 31     31 1 105 my($self) = shift;
268              
269 31         95 my $scheme = $self->scheme;
270 31         63 my $user = $self->userinfo_escaped;
271 31         82 my $port = $self->port;
272 31         73 my $query = $self->query;
273 31         701 my $frag = __escape_uri($self->fragment);
274              
275 31 100       836 return ((length $scheme) ? "$scheme://" : '') .
    100          
    100          
    50          
    100          
276             ((length $user) ? "$user\@" : '') .
277             $self->host .
278             ((length $port) ? ":$port" : '') .
279             __escape_uri_whole($self->path) .
280             ((length $query) ? "?$query" : '') .
281             ((length $frag) ? "#$frag" : '');
282             }
283              
284             sub query
285             {
286 54     54 1 3392 my($self) = shift;
287              
288 54 100       191 if(@_ == 1)
    100          
289             {
290 6 100       15 if(ref $_[0])
291             {
292 2         8 $self->{'query'} = _deep_copy($_[0])
293             }
294             else
295             {
296 4         11 $self->parse_query($_[0]);
297             }
298             }
299             elsif(@_)
300             {
301 1         5 $self->{'query'} = _deep_copy({ @_ });
302             }
303              
304 54         83 my $want = wantarray;
305              
306 54 100       117 return unless(defined wantarray);
307              
308 47 100       129 if(defined $self->{'query_string'})
309             {
310 3         10 return __escape_uri($self->{'query_string'});
311             }
312              
313 44         50 my(@query, $omit_empty);
314              
315 44         45 foreach my $param (sort keys %{$self->{'query'}})
  44         3666  
316             {
317 103         1351 my @values = $self->query_params($param);
318              
319             # Contortions to avoid calling this method in the common(?) case where
320             # every query parameter has at least one value.
321 103 100 66     236 if(!@values && !(defined $omit_empty ? $omit_empty : ($omit_empty = $self->omit_empty_query_params)))
    100          
322             {
323 4         57 @values = ('');
324             }
325              
326 103         159 foreach my $value (@values)
327             {
328 143         1330 push(@query, __escape_uri($param) . '=' . __escape_uri($value));
329             }
330             }
331              
332 44         5720 return join($self->query_param_separator, @query);
333             }
334              
335             sub query_form
336             {
337 2     2 1 254 my($self) = shift;
338              
339 2 100       7 if(@_)
340             {
341 1         2 $self->{'query_string'} = undef;
342 1         2 $self->{'query'} = { };
343              
344 1         5 for(my $i = 0; $i < $#_; $i += 2)
345             {
346 3         9 $self->query_param_add($_[$i] => $_[$i + 1]);
347             }
348             }
349              
350 2 100       6 return unless(defined(wantarray));
351              
352 1         1 my @query;
353              
354 1         4 foreach my $param ($self->query_params)
355             {
356 2         6 foreach my $value ($self->query_params($param))
357             {
358 3         8 push(@query, $param, $value);
359             }
360             }
361              
362 1         5 return @query;
363             }
364              
365             sub abs
366             {
367 5     5 1 36 my($self, $base) = @_;
368              
369 5 100 66     33 return $self unless($base && !length $self->scheme);
370              
371 4         10 my $new = $self->as_string;
372              
373 4         160 $new =~ s{^/}{};
374 4         10 $base =~ s{/$}{};
375              
376 4 50       60 return Rose::URI->new("/$new") unless($base =~ m{^$SCHEME_RE://}o);
377 4         18 return Rose::URI->new("$base/$new");
378             }
379              
380             sub rel
381             {
382 6     6 1 64 my($self, $base) = @_;
383              
384 6 100       25 return $self unless($base);
385              
386 5         15 my $uri = $self->as_string;
387              
388 5 100       507 if($uri =~ m{^$base/?})
389             {
390 2         32 $uri =~ s{^$base/?}{};
391              
392 2         8 return Rose::URI->new($uri);
393             }
394              
395 3         19 return $self;
396             }
397              
398             sub userinfo
399             {
400 0     0 1 0 my($self) = shift;
401              
402 0         0 my $user = $self->username;
403 0         0 my $pass = $self->password;
404              
405 0 0 0     0 if(length $user && length $pass)
406             {
407 0         0 return join(':', $user, $pass);
408             }
409              
410 0 0       0 return $user if(length $user);
411 0         0 return '';
412             }
413              
414             sub userinfo_escaped
415             {
416 31     31 1 48 my($self) = shift;
417              
418 31         239 my $user = __escape_uri($self->username);
419 31         468 my $pass = __escape_uri($self->password);
420              
421 31 100 66     383 if(length $user && length $pass)
422             {
423 20         68 return join(':', $user, $pass);
424             }
425              
426 11 50       24 return $user if(length $user);
427 11         21 return '';
428             }
429              
430             sub __uri_from_apache_uri
431             {
432 0     0   0 my($self) = shift;
433              
434 0         0 my $uri = Apache::URI->parse(Apache->request, @_);
435              
436 0   0     0 $self->{'username'} = $uri->user || '';
437 0   0     0 $self->{'password'} = $uri->password || '';
438 0   0     0 $self->{'scheme'} = $uri->scheme || '';
439 0   0     0 $self->{'host'} = $uri->hostname || '';
440 0   0     0 $self->{'port'} = $uri->port || '';
441 0   0     0 $self->{'path'} = $uri->path || '';
442 0   0     0 $self->{'fragment'} = $uri->fragment || '';
443              
444 0         0 $self->parse_query($uri->query);
445              
446 0         0 return $uri;
447             }
448              
449             sub __uri_from_uri
450             {
451 23     23   35 my($self) = shift;
452              
453 23         105 my $uri = URI->new(@_);
454              
455 23 50       18086 if($uri->can('user'))
    100          
456             {
457 0         0 $self->{'username'} = $uri->user;
458             }
459             elsif($uri->can('userinfo'))
460             {
461 14 100       46 if(my $userinfo = $uri->userinfo)
462             {
463 10 50       464 if(my($user, $pass) = split(':', $userinfo))
464             {
465 10         29 $self->{'username'} = __unescape_uri($user);
466 10         23 $self->{'password'} = __unescape_uri($pass);
467             }
468             }
469             }
470              
471 23   100     157 $self->{'scheme'} = __unescape_uri($uri->scheme || '');
472 23 100 50     135 $self->{'host'} = __unescape_uri($uri->host || '') if($uri->can('host'));
473 23 100 50     254 $self->{'port'} = __unescape_uri($uri->_port || '') if($uri->can('_port'));
474 23 50 50     140 $self->{'path'} = __unescape_uri($uri->path || '') if($uri->can('path'));
475 23   100     89 $self->{'fragment'} = __unescape_uri($uri->fragment || '');
476              
477 23         93 $self->parse_query($uri->query);
478              
479 23         98 return $uri;
480             }
481              
482             if(exists $ENV{'MOD_PERL'} && require mod_perl && $mod_perl::VERSION < 1.99)
483             {
484             require Apache;
485             require Apache::URI;
486             require Apache::Util;
487              
488             *__escape_uri = \&Apache::Util::escape_uri;
489             *__unescape_uri = \&Apache::Util::unescape_uri_info;
490              
491             $Make_URI = \&__uri_from_apache_uri;
492             }
493             else
494             {
495             *__escape_uri = \&URI::Escape::uri_escape;
496             *__unescape_uri = sub
497             {
498 273     273   3828 my $e = URI::Escape::uri_unescape(@_);
499              
500 273         1971 $e =~ s/\+/ /g;
501              
502 273         1439 return $e;
503             };
504              
505             require URI;
506             $Make_URI = \&__uri_from_uri;
507             }
508              
509             sub __escape_uri_whole
510             {
511 31 0   31   121 URI::Escape::uri_escape($_[0],
    50          
512             (@_ > 1) ? (defined $_[1] ? $_[1] : ()) : q(^A-Za-z0-9\-_.,'!~*#?&()/?@\:\[\]=));
513             }
514              
515             # Based on code from Clone::PP
516             sub _deep_copy
517             {
518 10     10   17 my($data) = shift;
519              
520 10 50       27 my $ref_type = ref $data or return $data;
521              
522 10         11 my $copy;
523              
524 10 100 33     503 if($ref_type eq 'HASH')
    100          
    50          
    50          
525             {
526 5         8 $copy = {};
527 5 100       16 %$copy = map { !ref($_) ? $_ : _deep_copy($_) } %$data;
  36         95  
528             }
529             elsif($ref_type eq 'ARRAY')
530             {
531 4         7 $copy = [];
532 4 50       9 @$copy = map { !ref($_) ? $_ : _deep_copy($_) } @$data;
  8         22  
533             }
534             elsif($ref_type eq 'REF' or $ref_type eq 'SCALAR')
535             {
536 0         0 $copy = \(my $var = '');
537 0         0 $$copy = _deep_copy($$data);
538             }
539             elsif($ref_type->isa(__PACKAGE__)) # cloning
540             {
541 1         2 $copy = _deep_copy({ %{$data} });
  1         15  
542             }
543             else
544             {
545 0         0 $copy = $data;
546             }
547              
548 10         36 return $copy;
549             }
550              
551             1;
552              
553             __END__