File Coverage

blib/lib/Google/Directions/Client.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Google::Directions::Client;
2 1     1   20464 use Carp;
  1         1  
  1         70  
3 1     1   2408 use Digest::SHA qw/sha256_hex/;
  1         4409  
  1         85  
4 1     1   19024 use Encode qw/encode_utf8/;
  1         14566  
  1         95  
5 1     1   610 use Google::Directions::Response;
  0            
  0            
6             use JSON qw/decode_json/;
7             use LWP::UserAgent;
8             use Moose;
9             use MooseX::Params::Validate;
10             use MooseX::WithCache;
11             use Try::Tiny;
12             use URL::Encode qw/url_encode/;
13              
14             with 'MooseX::WithCache' => {
15             backend => 'Cache::FastMmap',
16             };
17              
18              
19             =head1 NAME
20              
21             Google::Directions - Query directions from the google maps directions API
22              
23             =head1 VERSION
24              
25             Version 0.09
26              
27             =cut
28              
29             our $VERSION = '0.09';
30              
31             =head1 DESCRIPTION
32              
33             An interface to Google Maps Directions API V3.
34              
35             More details about what the API can do can be found on the L<API website|http://code.google.com/apis/maps/documentation/directions/>
36              
37             =head1 SYNOPSIS
38              
39             use Google::Directions::Client;
40              
41             my $goog = Google::Directions::Client->new();
42             my $response = $goog->directions(
43             origin => '25 Thompson Street, New York, NY, United States',
44             destination => '34 Lafayette Street, New York, NY, United States',
45             );
46              
47             =head1 ATTRIBUTES
48              
49             =over 4
50              
51             =item I<keep_alive> Enable keep_alive for the user agent.
52              
53             B<Warning:> This causes occasional errors due to partial content being returned... I'm not sure
54             what the root cause for this is... :(
55              
56             =item I<user_agent> Define a custom L<LWP::UserAgent> if you like.
57              
58             =item I<cache> Define a Cache::FastMmap if you would like to have results cached for better performance
59              
60             =item I<base_url> Default: C<https://maps.googleapis.com>
61              
62             =item I<api_path> Default: C</maps/api/directions/json>
63              
64             =item I<limit_path_length> limit is documented at 2048, but errors occur at 2047.. Default: 2046
65              
66              
67             =back
68              
69             =cut
70              
71             has 'keep_alive' => ( is => 'ro', isa => 'Int', required => 1, default => 0 );
72              
73             has 'user_agent' => (
74             is => 'ro',
75             isa => 'LWP::UserAgent',
76             writer => '_set_user_agent',
77             predicate => '_has_user_agent',
78             );
79              
80             has 'base_url' => ( is => 'ro', isa => 'Str',
81             default => 'https://maps.googleapis.com' );
82              
83             has 'api_path' => ( is => 'ro', isa => 'Str',
84             default => '/maps/api/directions/json' );
85              
86             has 'limit_path_length' => ( is => 'ro', isa => 'Int', default => 2046 );
87              
88             # Create a LWP::UserAgent if necessary
89             around 'user_agent' => sub {
90             my $orig = shift;
91             my $self = shift;
92             unless( $self->_has_user_agent ){
93             if( $self->keep_alive and not $ENV{NO_WARN_KEEPALIVE} ){
94             carp( "Warning - keep_alive gives unreliable results - partial JSON returned\n" .
95             "Set the enviroment variable NO_WARN_KEEPALIVE to hide this warning\n" );
96             }
97             my $ua = LWP::UserAgent->new(
98             'keep_alive' => $self->keep_alive,
99             );
100             $self->_set_user_agent( $ua );
101             }
102             return $self->$orig;
103             };
104              
105             =head1 METHODS
106              
107             =head2 directions
108              
109             Returns a L<Google::Directions::Response>
110              
111             =head3 params
112              
113             See the API documentation L<here|http://code.google.com/apis/maps/documentation/directions/#RequestParameters> for details
114              
115             =over 4
116              
117             =item I<origin> $string
118              
119             =item I<destination> $string
120              
121             =item I<mode> $string (Default: 'driving')
122              
123             =item I<waypoints> ArrayRef[$string] (optional)
124              
125             =item I<alternatives> $boolean (Default: 0)
126              
127             =item I<avoid> ArrayRef[$string] (optional)
128              
129             =item I<region> $string (optional)
130              
131             =item I<sensor> $boolean (Default: 0)
132              
133             =back
134              
135             =cut
136              
137             sub directions {
138             my ( $self, %params ) = validated_hash(
139             \@_,
140             origin => { isa => 'Str' },
141             destination => { isa => 'Str' },
142             mode => { isa => 'Str', default => 'driving' },
143             waypoints => { isa => 'ArrayRef[Str]', optional => 1 },
144             alternatives => { isa => 'Bool', optional => 1 },
145             avoid => { isa => 'ArrayRef[Str]', optional => 1 },
146             #units => { isa => 'Str', default => 'metric' }, # value is always in meters, only affects text, so irrelevant for exact computation
147             region => { isa => 'Str', optional => 1 },
148             sensor => { isa => 'Bool', default => 0 },
149             );
150              
151             my @query_params;
152             foreach( qw/origin destination mode units region/ ){
153             if( defined( $params{$_} ) ){
154             push( @query_params, sprintf( "%s=%s",
155             $_, url_encode( $params{$_} ) ) );
156             }
157             }
158              
159             foreach( qw/alternatives sensor/ ){
160             if( $params{$_} ){
161             push( @query_params, sprintf( "%s=true", $_ ) );
162             }else{
163             push( @query_params, sprintf( "%s=false", $_ ) );
164             }
165             }
166              
167             foreach my $key( qw/waypoints avoid/ ){
168             if( defined( $params{$key} ) ){
169             my $joined = join( '|', @{ $params{$key} } );
170             push( @query_params, sprintf( "%s=%s", $key, url_encode( $joined ) ) );
171             }
172             }
173              
174             my $path = $self->api_path . '?' . join( '&', @query_params );
175            
176             # Make sure path is not too long
177             if( length( $path ) > $self->limit_path_length ){
178             croak( printf( "Path may not be more than %u characters but is %u\n",
179             $self->limit_path_length,
180             length( $path ),
181             ) );
182             }
183              
184             my $url = $self->base_url . $path;
185             my $cache_key = sha256_hex( $url );
186             my $google_response = $self->cache_get( $cache_key );
187             if( $google_response ){
188             $google_response->set_cached( 1 );
189             }
190              
191             if( not $google_response ){
192             my $response = $self->user_agent->get( $url );
193              
194             if( not $response->is_success ){
195             croak( "Query failed: " . $response->status_line );
196             }
197              
198             my $data = try{
199             return decode_json( encode_utf8( $response->decoded_content ) );
200             }catch{
201             croak( $_ );
202             };
203             $google_response = Google::Directions::Response->new( $data );
204             if( $self->cache and not $self->cache_set( $cache_key, $google_response ) ){
205             carp( "Response not saved in cache - too big\n" );
206             }
207             }
208              
209             return $google_response;
210             }
211              
212              
213             =head1 AUTHOR
214              
215             Robin Clarke, C<< <perl at robinclarke.net> >>
216              
217             =head1 BUGS
218              
219             Please report any bugs or feature requests to C<bug-google-directions at rt.cpan.org>, or through
220             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Google-Directions>. I will be notified, and then you'll
221             automatically be notified of progress on your bug as I make changes.
222              
223              
224              
225              
226             =head1 SUPPORT
227              
228             You can find documentation for this module with the perldoc command.
229              
230             perldoc Google::Directions::Client
231              
232              
233             You can also look for information at:
234              
235             =over 4
236              
237             =item * RT: CPAN's request tracker (report bugs here)
238              
239             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Google-Directions>
240              
241             =item * AnnoCPAN: Annotated CPAN documentation
242              
243             L<http://annocpan.org/dist/Google-Directions>
244              
245             =item * CPAN Ratings
246              
247             L<http://cpanratings.perl.org/d/Google-Directions>
248              
249             =item * Search CPAN
250              
251             L<http://search.cpan.org/dist/Google-Directions/>
252              
253             =back
254              
255              
256             =head1 ACKNOWLEDGEMENTS
257              
258              
259             =head1 LICENSE AND COPYRIGHT
260              
261             Copyright 2012 Robin Clarke.
262              
263             This program is free software; you can redistribute it and/or modify it
264             under the terms of either: the GNU General Public License as published
265             by the Free Software Foundation; or the Artistic License.
266              
267             See http://dev.perl.org/licenses/ for more information.
268              
269              
270             =cut
271              
272             1; # End of Google::Directions