File Coverage

blib/lib/Google/Search.pm
Criterion Covered Total %
statement 103 211 48.8
branch 29 98 29.5
condition 13 39 33.3
subroutine 24 38 63.1
pod 8 15 53.3
total 177 401 44.1


line stmt bran cond sub pod time code
1             package Google::Search;
2             {
3             $Google::Search::VERSION = '0.028';
4             }
5             # ABSTRACT: Interface to the Google AJAX Search API and suggestion API (DEPRECATED)
6              
7 5     5   502712 use warnings;
  5         15  
  5         171  
8 5     5   25 use strict;
  5         11  
  5         163  
9              
10              
11 5     5   4513 use Any::Moose;
  5         227636  
  5         40  
12 5     5   6792 use Google::Search::Carp;
  5         19  
  5         36  
13              
14 5     5   4025 use Google::Search::Response;
  5         18  
  5         195  
15 5     5   3024 use Google::Search::Page;
  5         14  
  5         159  
16 5     5   3817 use Google::Search::Result;
  5         20  
  5         317  
17 5     5   5011 use Google::Search::Error;
  5         16  
  5         180  
18 5     5   6680 use LWP::UserAgent;
  5         233997  
  5         235  
19             require HTTP::Request::Common;
20 5     5   64 use JSON;
  5         12  
  5         54  
21              
22             my $json = JSON->new;
23              
24             BEGIN {
25 5     5   1019 use vars qw/ $Base %Service2URI /;
  5         15  
  5         701  
26 5     5   14 $Base = 'http://ajax.googleapis.com/ajax/services/search';
27 40         6478 %Service2URI = (
28             videos => "$Base/video",
29             blog => "$Base/blogs",
30             book => "$Base/books",
31             image => "$Base/images",
32             patents => "$Base/patent",
33 5         24 map { $_ => "$Base/$_" } qw/ web local video blogs news books images patent /,
34             );
35             }
36              
37              
38             sub _inflate_query (@) {
39 7     7   11 my @query;
40 7         17 for (@_) {
41 8 50       54 if ( ref eq 'HASH' ) { push @query, %$_ }
  0 100       0  
    50          
42 2         6 elsif ( ref eq 'ARRAY' ) { push @query, @$_ }
43 6         20 elsif ( ! ref ) { push @query, q => $_ }
44 0         0 else { croak "Invalid query ($_)" }
45             }
46 7         27 return \@query;
47             }
48              
49             {
50             my $agent_;
51             sub suggest {
52 0     0 1 0 my $self = shift; # Could be class or object
53              
54 0 0 0     0 my $agent = blessed $self ? $self->agent : ( $agent_ ||= LWP::UserAgent->new );
55 0         0 my ( $term, $query, $uri ) = ( undef, [], {} );
56              
57 0         0 for( @_ ) {
58 0 0       0 next unless defined $_;
59 0 0       0 if ( ! ref ) { $term = $_ }
  0 0       0  
    0          
60 0         0 elsif ( ref eq 'ARRAY' ) { $query = $_ }
61 0         0 elsif ( ref eq 'HASH' ) { $uri = $_ }
62 0         0 else { croak "Invalid parameter ($_)" }
63             }
64              
65 0 0       0 croak "Missing term" unless defined $term;
66 0         0 my @query = @$query;
67 0 0       0 croak "Uneven query ($#query): ", @query if @query % 2;
68              
69 0         0 my %query = @query;
70 0 0       0 unshift @query, q => $term unless exists $query{q};
71              
72 0         0 my @uri = ( map { $_ => $uri->{$_} } grep { exists $uri->{$_} }
  0         0  
  0         0  
73             qw/ host port host_port scheme path userinfo authority agent / );
74 0         0 unshift @uri, qw{ scheme http host clients1.google.com path complete/search };
75 0         0 my %uri = @uri;
76 0         0 my $user_agent = delete $uri{agent};
77              
78 0         0 $uri = URI->new;
79 0         0 while ( my ($k, $v) = each %uri ) { $uri->$k( $v ) }
  0         0  
80 0         0 $uri->query_form( @query );
81              
82 0         0 my @header;
83 0 0       0 @header = ( 'User-Agent' => $user_agent ) if defined $user_agent;
84              
85 0         0 my $response = $agent->get( $uri, @header );
86 0 0       0 croak "Failed response: ", $response->status_line unless $response->is_success;
87 0         0 my $content = $response->decoded_content;
88 0 0       0 croak "Malformed content: $content" unless $content =~ s/^.*?\(\[(.*)\]\)$/[$1]/g;
89 0         0 my $data = $json->decode( $content );
90 0 0 0     0 croak "Malformed content: $content" unless ref $data eq 'ARRAY' && $data->[1];
91 0         0 return $data->[1];
92             }
93             }
94              
95             sub service2uri {
96 6     6 0 10 my $class = shift;
97 6         8 my $service = shift;
98 6 50       20 croak "Missing service" unless $service;
99 6         13 $service = lc $service;
100 6 50       27 return unless my $uri = $Service2URI{$service};
101 6         14 return $uri;
102             }
103              
104             sub BUILDARGS {
105 13     13 1 316 my $class = shift;
106            
107 13         23 my $given;
108 13 50 33     131 if ( 1 == @_ && ref $_[0] eq 'HASH' ) {
    100 66        
    100 66        
    50 33        
      33        
109 0         0 $given = $_[0];
110             }
111             elsif ( 3 == @_ && $_[0] eq 'service' && ! ref $_[2] && defined $_[2] ) {
112 1         1 my $query = pop;
113 1         4 $given = { @_, query => $query };
114             }
115             elsif ( 0 == @_ % 2 ) {
116 11         43 $given = { @_ };
117             }
118             elsif ( @_ > 3 && $_[0] eq 'service' ) {
119 1         4 my @given = splice @_, 0, 2;
120 1         3 push @given, query => shift @_;
121 1         6 $given = { @given, @_ };
122             }
123             else {
124 0         0 croak "Odd number of arguments: @_";
125             }
126              
127 13         29 my $query = delete $given->{q};
128 13 100 66     80 $given->{query} = $query if defined $query && ! defined $given->{query};
129              
130 13         22 my $version = delete $given->{v};
131 13 100 66     48 $given->{version} = $version if defined $version && ! defined $given->{version};
132              
133 13         23 my $referrer = delete $given->{referrer};
134 13 50 33     38 $given->{referer} = $referrer if defined $referrer && ! defined $given->{referer};
135              
136 13         22 $query = $given->{query};
137 13         18 my @query;
138              
139 13         49 while( my( $key, $value ) = each %$given ) {
140 32 100       194 next if $key =~ m/^(?:agent|service|uri|query|version|hl|referer|
141             key|start|rsz|rsz2number|current|error)$/x;
142 1         9 carp "Including unknown parameter \"$key\" with query";
143 1         107 push @query, $key => $value;
144             }
145              
146 13 100       36 $given->{query} = _inflate_query \@query, $query if @query;
147 13         183 return $given;
148             }
149              
150             for my $service ( keys %Service2URI ) {
151 5     5   34 no strict 'refs';
  5         10  
  5         7846  
152             my $umethod = ucfirst $service;
153             my $lmethod = lc $service;
154             *$umethod = *$lmethod = sub {
155 13     13   20635 my $class = shift;
156 13         105 return $class->new( service => $service, @_ );
157             };
158             }
159              
160             has agent => qw/ is ro lazy_build 1 isa LWP::UserAgent /;
161             sub _build_agent {
162 0     0   0 my $self = shift;
163 0         0 my $agent = LWP::UserAgent->new;
164 0         0 $agent->env_proxy;
165 0         0 return $agent;
166             }
167              
168             has service => qw/ is ro lazy_build 1 /;
169 0     0   0 sub _build_service { 'web' }
170              
171             has uri => qw/ is ro lazy_build 1 isa URI /;
172             sub _build_uri {
173 6     6   10 my $self = shift;
174 6         19 my $service = $self->service;
175 6         22 my $uri = $self->service2uri( $service );
176 6 50       16 croak "Invalid service ($service)" unless $uri;
177 6         40 return URI->new( $uri );
178             }
179              
180             has query => qw/ is ro required 1 /;
181 2     2 0 1245 sub q { return shift->query( @_ ) }
182              
183             has version => qw/ is ro lazy_build 1 isa Str /;
184 4     4   37 sub _build_version { '1.0' }
185 2     2 0 22 sub v { return shift->version( @_ ) }
186              
187             has hl => qw/ is rw predicate has_hl /;
188              
189             has referer => qw/ is ro isa Str /;
190 2     2 0 15 sub referrer { return shift->referer( @_ ) }
191              
192             has key => qw/ is ro isa Str /;
193              
194             has start => qw/ is ro lazy_build 1 isa Int /;
195 0     0   0 sub _build_start { 0 }
196              
197             has rsz => qw/ is ro lazy_build 1 isa Str /;
198 6     6   70 sub _build_rsz { 'large' }
199             has rsz2number => qw/ is ro lazy_build 1 isa Int /;
200             sub _build_rsz2number {
201 0     0   0 my $self = shift;
202 0         0 my $rsz = $self->rsz;
203 0 0       0 return 4 if $rsz eq "small";
204 0 0       0 return 8 if $rsz eq "large";
205 0         0 croak "Don't understand rsz ($rsz)";
206             }
207              
208             has _page => qw/ is ro required 1 /, default => sub { [] };
209             has _result => qw/ is ro required 1 /, default => sub { [] };
210             has current => qw/ is ro lazy_build 1 /;
211             sub _build_current {
212 0     0   0 return shift->first;
213             }
214             has error => qw/ is rw /;
215              
216             sub request {
217 0     0 0 0 my $self = shift;
218 0         0 my $http_request = $self->build( @_ );
219 0 0       0 return unless my $http_response = $self->agent->request( $http_request );
220 0         0 return Google::Search::Response->new( http_response => $http_response );
221             }
222              
223             sub build {
224 6     6 0 352 my $self = shift;
225              
226 6         11 my ( @query_form, @header_supplement );
227              
228             {
229 6         8 my $referer = $self->referer;
  6         26  
230 6         19 my $key = $self->key;
231              
232 6 100       21 push @header_supplement, Referer => $referer if $referer;
233 6 50       21 push @query_form, key => $key if $key;
234             }
235              
236 6         20 my $query = $self->query;
237             # TODO Check for query instead of q?
238 6         9 push @query_form, @{ _inflate_query $query };
  6         21  
239 6 100       55 push @query_form, hl => $self->hl if $self->has_hl;
240              
241 6         49 my $uri = $self->uri->clone;
242 6         21269 $uri->query_form({ v => $self->version, rsz => $self->rsz, @query_form, @_ });
243              
244 6 50       608 if ( $ENV{GS_TRACE} ) {
245 0         0 warn $uri->as_string, "\n";
246             }
247              
248 6         33 my $request = HTTP::Request::Common::GET( $uri => @header_supplement );
249              
250 6 50 33     673 if ( $ENV{GS_TRACE} && $request ) {
251 0         0 warn $request->as_string, "\n";
252             }
253              
254 6         50 return $request;
255             }
256              
257             sub page {
258 0     0 0   my $self = shift;
259 0           my $number = shift;
260              
261 0           $self->error( undef );
262              
263 0   0       my $page = $self->_page->[$number] ||=
264             Google::Search::Page->new( search => $self, number => $number );
265              
266 0 0         $self->error( $page->error ) if $page->error;
267              
268 0           return $page;
269             }
270              
271              
272             sub first {
273 0     0 1   my $self = shift;
274 0           return $self->result( $self->start );
275             }
276              
277              
278             sub next {
279 0     0 1   my $self = shift;
280 0 0         return $self->current unless $self->{current};
281 0           return $self->{current} = $self->current->next;
282             }
283              
284              
285             sub result {
286 0     0 1   my $self = shift;
287 0           my $number = shift;
288              
289 0           $self->error( undef );
290              
291 0 0         return $self->_result->[$number] if $self->_result->[$number];
292 0           my $result = do {
293 0           my $result_number = $number % $self->rsz2number;
294 0           my $page_number = int( $number / $self->rsz2number );
295 0           my $page = $self->page( $page_number );
296 0           my $content = $page->result( $result_number );
297 0 0         if ( $content ) {
298 0           Google::Search::Result->parse( $content,
299             page => $page, search => $self, number => $number);
300             }
301             else {
302 0           undef;
303             }
304             };
305 0 0         return undef unless $result;
306 0           return $self->_result->[$number] = $result;
307             }
308              
309              
310             sub all {
311 0     0 1   my $self = shift;
312              
313 0           my $result = $self->first;
314 0   0       1 while $result && ( $result = $result->next ); # Fetch everything
315 0 0         if ($self->error) {
316 0 0         die $self->error->reason unless $self->error->message eq "out of range start";
317             }
318              
319 0           my @results = @{ $self->_result };
  0            
320 0 0         return wantarray ? @results : \@results;
321             }
322              
323              
324             sub match {
325 0     0 1   my $self = shift;
326 0           my $matcher = shift;
327              
328 0           my @matched;
329 0           my $result = $self->first;
330 0           while ($result) {
331 0 0         push @matched, $result if $matcher->($result);
332 0           $result = $result->next;
333             }
334 0 0         if ($self->error) {
335 0 0         die $self->error->reason unless $self->error->message eq "out of range start";
336             }
337 0           return @matched;
338             }
339              
340              
341             sub first_match {
342 0     0 1   my $self = shift;
343 0           my $matcher = shift;
344              
345 0           my $result = $self->first;
346 0           while ($result) {
347 0 0         return $result if $matcher->($result);
348 0           $result = $result->next;
349             }
350 0 0         if ($self->error) {
351 0 0         die $self->error->reason unless $self->error->message eq "out of range start";
352             }
353 0           return undef;
354             }
355              
356             $_->meta->make_immutable for qw/
357             Google::Search
358             Google::Search::Response
359             Google::Search::Page
360             Google::Search::Result
361             Google::Search::Error
362             /;
363              
364              
365             1;
366              
367             __END__