File Coverage

blib/lib/Geo/Coder/Many.pm
Criterion Covered Total %
statement 201 263 76.4
branch 51 88 57.9
condition 16 41 39.0
subroutine 35 39 89.7
pod 6 6 100.0
total 309 437 70.7


line stmt bran cond sub pod time code
1             package Geo::Coder::Many;
2              
3 2     2   227431 use strict;
  2         6  
  2         72  
4 2     2   11 use warnings;
  2         5  
  2         56  
5 2     2   19 use Carp;
  2         4  
  2         177  
6 2     2   2251 use List::MoreUtils qw(any);
  2         2802  
  2         174  
7 2     2   2515 use Sort::Versions;
  2         1533  
  2         246  
8 2     2   949 use Time::HiRes;
  2         1892  
  2         26  
9              
10             our $VERSION = '0.46';
11              
12             # note - also update lists far below in pod
13 2     2   1586 use Geo::Coder::Many::Bing;
  2         4  
  2         49  
14 2     2   1147 use Geo::Coder::Many::Google;
  2         6  
  2         54  
15 2     2   1463 use Geo::Coder::Many::Googlev3;
  2         4  
  2         46  
16 2     2   1089 use Geo::Coder::Many::Mapquest;
  2         5  
  2         42  
17 2     2   1112 use Geo::Coder::Many::OpenCage;
  2         4  
  2         45  
18 2     2   1340 use Geo::Coder::Many::OSM;
  2         6  
  2         57  
19 2     2   1505 use Geo::Coder::Many::Ovi;
  2         5  
  2         51  
20 2     2   1516 use Geo::Coder::Many::PlaceFinder;
  2         5  
  2         62  
21              
22 2         147 use Geo::Coder::Many::Util qw(
23             min_precision_filter
24             max_precision_picker
25             consensus_picker
26             country_filter
27 2     2   12 );
  2         4  
28              
29 2     2   1426 use Geo::Coder::Many::Scheduler::Selective;
  2         6  
  2         114  
30 2     2   1527 use Geo::Coder::Many::Scheduler::OrderedList;
  2         5  
  2         100  
31 2     2   1495 use Geo::Coder::Many::Scheduler::UniquenessScheduler::WRR;
  2         6  
  2         58  
32 2     2   1314 use Geo::Coder::Many::Scheduler::UniquenessScheduler::WeightedRandom;
  2         5  
  2         5089  
33              
34             =head1 NAME
35              
36             Geo::Coder::Many - Module to tie together multiple Geo::Coder::* modules
37              
38             =head1 DESCRIPTION
39              
40             Geo::Coder::Many provides a single interface to different remote
41             (ie HTTP based) geocoding modules
42              
43             Amongst other things, Geo::Coder::Many adds geocoder precision information,
44             alternative scheduling methods (weighted random, and ordered list), timeouts
45             for geocoders which are failing, and optional callbacks for result filtering
46             and picking.
47              
48             =head1 SYNOPSIS
49              
50             General steps for using Geo::Coder::Many:
51              
52             =over
53              
54             =item 1. Create Geo::Coder::* objects for the geocoders you want to use, using
55             their various individual setup procedures.
56              
57             =item 2. Create the Geo::Coder::Many object with C
58              
59             =item 3. Call C for each of the geocoders you want to use
60              
61             =item 4. Set any filter or picker callbacks you require (optional)
62              
63             =item 5. Use the C method to do all of your geocoding
64              
65             =back
66              
67             =head1 EXAMPLE
68              
69             Suppose the geocoders we want to use are called 'Locatorize' and 'WhereIzIt'.
70              
71             use Geo::Coder::Locatorize;
72             use Geo::Coder::WhereIzIt;
73             use Geo::Coder::Many;
74             use Geo::Coder::Many::Util qw( country_filter );
75            
76             # Create the Geo::Coder::Many object, telling it to use a 'weighted random'
77             # scheduling method
78             my $options = {
79             cache => $cache_object,
80             scheduler_type => 'WRR',
81             };
82             my $geocoder_many = Geo::Coder::Many->new( $options );
83            
84             # Create and add a geocoder
85             my $Locatorize = Geo::Coder::Locatorize->new( appid => 'mY_loCat0r1Ze_iD' );
86             my $Locatorize_options = {
87             geocoder => $Locatorize,
88             daily_limit => 2500,
89             };
90             $geocoder_many->add_geocoder( $Locatorize_options );
91            
92             # Create and add a second geocoder
93             my $WhereIzIt = Geo::Coder::WhereIzIt->new( apikey => 'mY_WhERiz1t_kEy' );
94             my $WhereIzIt_options = {
95             geocoder => $WhereIzIt,
96             daily_limit => 4000,
97             };
98             $geocoder_many->add_geocoder( $WhereIzIt_options );
99            
100             # Use a filter callback from Geo::Coder::Many::Util
101             $geocoder_many->set_filter_callback(country_filter('United Kingdom'));
102            
103             # Use a built-in picker callback
104             $geocoder_many->set_picker_callback('max_precision');
105            
106             my $result = $geocoder_many->geocode(
107             {
108             location => '82 Clerkenwell Road, London'
109             }
110             );
111            
112             if (defined $result) {
113             print "Country: ", $result->{country}, "\n";
114             print "Longitude: ", $result->{longitude}, "\n";
115             print "Latitude: ", $result->{latitude}, "\n";
116             print "Location: ", $result->{location}, "\n";
117             print "Response code: ", $result->{response_code}, "\n";
118             print "Address: ", $result->{address}, "\n";
119             print "Precision: ", $result->{precision}, "\n";
120             print "Geocoder: ", $result->{geocoder}, "\n";
121             }
122             else {
123             print "Failed to geocode!\n";
124             }
125            
126             =head1 METHODS
127              
128             =head2 new
129              
130             Constructs a new Geo::Coder::Many object and returns it. Options should be
131             provided as the entries of a hash reference, as follows:
132              
133             KEY VALUE
134             ----------- --------------------
135             cache Cache object reference (optional)
136             normalize_code_ref A normalization code ref (optional)
137             scheduler_type Name of the scheduler type to use (default: WRR)
138             use_timeouts Whether to time out failing geocoders (default: false)
139              
140             If no C option is specified, no caching will be done for the geocoding
141             results.
142              
143             C is a code reference which is used to normalize location
144             strings to ensure that all cache keys are normalized for correct lookup.
145              
146             C specifies how load balancing should be done.
147              
148             Scheduling schemes currently available are:
149              
150             =over
151              
152             =item WRR (Weighted round-robin)
153              
154             Round-robin scheduling, weighted by the daily_limit values for the geocoders
155             (The same behaviour as Geo::Coder::Multiple)
156              
157             =item OrderedList
158              
159             A strict preferential ordering by daily_limit - the geocoder with the
160             highest limit will always be used. If that fails, the next highest will be
161             used, and so on.
162              
163             =item WeightedRandom
164              
165             Geocoders will be picked at random, each with probability proportional to
166             its specified daily_limit.
167              
168             =back
169              
170             Other scheduling schemes can be implemented by sub-classing
171             Geo::Coder::Many::Scheduler or Geo::Coder::Many::UniquenessScheduler.
172              
173             If C is true, geocoders that are unsuccessful will not be queried
174             again for a set amount of time. The timeout period will increase exponentially
175             for every successive consecutive failure.
176              
177             =cut
178              
179             sub new {
180 210     210 1 22689 my $class = shift;
181 210         369 my $args = shift;
182              
183 210         1749 my $self = {
184             cache => undef,
185             geocoders => {},
186             scheduler => undef,
187             normalize_code_ref => $args->{normalize_code_ref},
188             filter_callback => undef,
189             picker_callback => undef,
190             scheduler_type => $args->{scheduler_type},
191             use_timeouts => $args->{use_timeouts},
192             };
193              
194 210 50       712 if ( !defined $args->{scheduler_type} ){
195 0         0 $self->{scheduler_type} = 'WRR';
196             }
197 210 50       1958 if ( $self->{scheduler_type} !~ /OrderedList|WRR|WeightedRandom/x ) {
198 0         0 carp "Unsupported scheduler type: should be OrderedList or WRR or
199             WeightedRandom.";
200             }
201              
202 210         884 bless $self, $class;
203              
204 210 50       556 if ( $args->{cache} ) {
205 0         0 $self->_set_caching_object( $args->{cache} );
206             }
207 210         544 return $self;
208             }
209              
210             =head2 add_geocoder
211              
212             This method adds a geocoder to the list of possibilities.
213              
214             Before any geocoding can be performed, at least one geocoder must be added
215             to the list of available geocoders.
216              
217             If the same geocoder is added twice, only the instance added first will be
218             used. All other additions will be ignored.
219              
220             KEY VALUE
221             ----------- --------------------
222             geocoder geocoder object reference (required)
223             daily_limit geocoder source limit per 24 hour period (required)
224              
225             C should be a reference to a Geo::Coder::Something object, where
226             'Something' is a supported geocoder type. For a geocoder to be supported, it
227             needs to have a corresponding Geo::Coder::Many::Something adapter module.
228              
229             Note that C is just treated as guideline for the chosen scheduler,
230             and will not necessarily be strictly obeyed.
231              
232             =cut
233              
234             sub add_geocoder {
235 420     420 1 3842 my ($self, $args) = @_;
236              
237 420         1007 my $module = ref $args->{geocoder};
238 420         1850 (my $plugin = $module) =~ s/Geo::Coder::/Geo::Coder::Many::/x;
239              
240             # Check that the geocoder module is compatabible with our plugin.
241 420 50       1253 if (!$self->_geocoder_module_is_compatible_with_plugin($module, $plugin)) {
242 0         0 carp "Can't add $module due to version incompatibility";
243 0         0 return 0;
244             }
245              
246 420         573 eval {
247 420         1876 my $geocoder = $plugin->new($args);
248 420 50       3107 if (exists $self->{geocoders}->{$geocoder->get_name()}) {
249 0         0 carp "Warning: duplicate geocoder (" . $geocoder->get_name() .")";
250             }
251 420         25449 $self->{geocoders}->{$geocoder->get_name()} = $geocoder;
252             };
253            
254 420 50       22040 if ($@) {
255 0         0 carp "Geocoder not supported - $module\n";
256 0         0 return 0;
257             }
258              
259 420         1160 $self->_recalculate_geocoder_stats();
260 420         1135 return 1;
261             }
262              
263             =head2 set_filter_callback
264              
265             Sets the callback used for filtering results. By default, all results are
266             passed through. If a callback is set, only results for which the callback
267             returns true are passed through. The callback takes one argument: a Response
268             object to be judged for fitness. It should return true or false, depending on
269             whether that Response is deemed suitable for consideration by the picker.
270              
271             =cut
272              
273             sub set_filter_callback {
274 210     210 1 1327 my ($self, $filter_callback) = @_;
275              
276             # If given a scalar, look up the name
277 210 100       744 if (ref($filter_callback) eq '') {
278 60         438 my %callback_names = (
279              
280             # Accepting all results is the default behaviour
281             qr/(all)?/x => undef,
282              
283             );
284 60         312 $filter_callback = $self->_lookup_callback(
285             $filter_callback,
286             \%callback_names
287             );
288             }
289              
290             # We should now have a code reference
291 210 50 66     1253 if (defined $filter_callback && ref($filter_callback) ne 'CODE') {
292 0         0 croak "set_filter_callback requires a scalar or a code reference\n";
293             }
294              
295 210         400 $self->{filter_callback} = $filter_callback;
296 210         487 return;
297             }
298              
299             =head2 set_picker_callback
300              
301             Sets the callback used for result picking. This determines which single result
302             will actually be returned by the geocode method. By default, the first valid
303             result (that has passed the filter callback, if one was set) is returned.
304              
305             As an alternative to passing a subroutine reference, you can pass a scalar with
306             a name that refers to one of the built-in callbacks. An empty string or 'first'
307             sets the behaviour back to the default: accept the first result that is
308             offered. 'max_precision' fetches all results and chooses the one with the
309             greatest precision value.
310              
311             The picker callback has two arguments: a reference to an array of the valid
312             results that have been collected so far, and a value that is true if there are
313             more results available and false otherwise. The callback should return a single
314             result from the list, if one is acceptable. If none are acceptable, the
315             callback may return undef, indicating that more results to pick from are
316             desired. If these are available, the picker will be called again once they have
317             been added to the results array.
318              
319             Note that since geocoders are not (currently) queried in parallel, a picker
320             that requires lots of results to make a decision may take longer to return a
321             value.
322              
323             =cut
324              
325             sub set_picker_callback {
326 210     210 1 1016 my ($self, $picker_callback) = @_;
327              
328             # If given a scalar, look up the name
329 210 100       608 if (ref($picker_callback) eq '') {
330 84         827 my %callback_names = (
331             qr/(first)?/x => undef,
332             qr/max_precision/x => \&max_precision_picker,
333             );
334 84         412 $picker_callback = $self->_lookup_callback(
335             $picker_callback,
336             \%callback_names,
337             );
338             }
339              
340             # We should now have a code reference
341 210 50 66     1401 if (defined $picker_callback && ref($picker_callback) ne 'CODE') {
342 0         0 croak "set_picker_callback requires a scalar or a code reference\n";
343             }
344              
345 210         448 $self->{picker_callback} = $picker_callback;
346 210         429 return;
347             }
348              
349             =head2 geocode
350              
351             my $options = {
352             location => $location,
353             results_cache => $cache,
354             };
355              
356             my $found_location = $geocoder_many->geocode( $options );
357              
358             Arguments should be provided in a hash reference with the following entries:
359              
360             KEY VALUE
361             ----------- --------------------
362             location location string to pass to geocoder
363              
364             results_cache reference to a cache object; will override the default
365              
366             no_cache if set, the result will not be retrieved or set in
367             cache (off by default)
368              
369             wait_for_retries if set, the method will wait until it's sure all
370             geocoders have been tried (off by default)
371              
372             This method is the basis for the class, it will retrieve result from cache
373             first, and return if cache hit.
374              
375             If the cache is missed, the C method is called, with the location as
376             the argument, on the next available geocoder object in the sequence.
377              
378             If called in an array context all the matching results will be returned,
379             otherwise the first result will be returned.
380              
381             A matching address will have the following keys in the hash reference.
382              
383             KEY VALUE
384             ----------- --------------------
385             response_code integer response code (see below)
386              
387             address matched address
388              
389             latitude latitude of matched address
390              
391             longitude longitude of matched address
392              
393             country country of matched address (not available for all
394             geocoders)
395              
396             geocoder source used to lookup address
397              
398             location the original query string
399              
400             precision scalar ranging from 0.0 to 1.0, denoting the
401             granularity of the result (undef if not known)
402              
403             The C key will contain a string denoting which geocoder returned the
404             results (eg, 'locatorize').
405              
406             The C key will contain the response code. The possible values
407             are:
408              
409             200 Success
410             210 Success (from cache)
411             401 Unable to find location
412             402 All geocoder limits reached (not yet implemented)
413              
414             C will return undef if none of the geocoders that were tried produced
415             a result that satisfied the filter and picker callbacks.
416              
417             =cut
418              
419             sub geocode {
420 2100     2100 1 157247 my ($self, $args) = @_;
421              
422 2100 50       4655 if ( !exists $args->{location} ) {
423 0         0 croak "Geo::Coder::Many::geocode method requires a location!\n";
424             }
425              
426             # If using cache, check that first
427 2100 50       4581 if ( !$args->{no_cache} ){
428 2100         6479 my $response = $self->_get_from_cache(
429             $args->{location},
430             $args->{cache},
431             );
432 2100 50       5346 if ( defined $response ){
433 0         0 return $response
434             }
435             }
436              
437 2100 50       1978 if ( !keys %{$self->{geocoders}} ){
  2100         5996  
438 0         0 carp "Warning: geocode called, but no geocoders have been added!\n";
439 0         0 return;
440             }
441              
442 2100         2875 my $previous_geocoder_name = '';
443 2100         3397 my $ra_valid_results = [];
444 2100         2242 my $waiting_time = 0;
445 2100         2240 my $accepted_response = undef;
446              
447             # We have not yet tried any geocoders for this query - tell the scheduler.
448 2100         30515 $self->{scheduler}->reset_available();
449              
450 2100         4068 while ( !defined $accepted_response ) {
451              
452             # Check whether we have geocoders to try
453             # (next_available gives us the minimum length of time until there may
454             # be a working geocoder, or undef if this is infinite)
455 4176         68520 $waiting_time = $self->{scheduler}->next_available();
456 4176 100       9708 if (!defined $waiting_time) {
457             # Run out of geocoders.
458 572         1077 last;
459             }
460              
461             # If wait_for_retries is set, wait here until the time we were told
462 3604 50 66     12387 if ( $waiting_time > 0 && $args->{ wait_for_retries } ) {
463 0         0 Time::HiRes::sleep($waiting_time);
464             }
465              
466 3604         7014 my $geocoder = $self->_get_next_geocoder();
467              
468             # No more geocoders? We'll return undef later
469 3604 100       7929 last if (!defined $geocoder);
470              
471             # Check the geocoder has an OK name
472 2411         11211 my $geocoder_name = $geocoder->get_name();
473              
474 2411 50       137592 if ( $geocoder_name eq $previous_geocoder_name ) {
475 0         0 carp "The scheduler is bad - it returned two geocoders with the "
476             ."same name, between calls to reset_available!";
477             }
478 2411 50   0   7385 next if ( any { $geocoder_name eq $_ } @{$args->{geocoders_to_skip} || []} );
  0 50       0  
  2411         14033  
479              
480             # Use the current geocoder to geocode the requested location
481 2411         14744 my $Response = $geocoder->geocode( $args->{location} );
482              
483             # Tell the scheduler about how successful the geocoder was
484 2411 50       16763 if (defined $Response) {
485              
486 2411         6837 my $feedback = {
487             response_code => $Response->get_response_code(),
488             };
489 2411         9638 $self->{scheduler}->process_feedback($geocoder_name, $feedback);
490             }
491             else {
492 0         0 carp "Geocoder $geocoder_name returned undef.";
493             }
494              
495 2411         2915 $previous_geocoder_name = $geocoder_name;
496              
497             # If our response has a valid code
498 2411 100       4761 if ( $self->_response_valid($Response) ) {
499            
500             # Apply the filter callback to the response entries
501 1205         2361 my @passed_responses = grep {
502 1205         52791 $self->_passes_filter($_)
503             } $Response->get_responses();
504              
505             # If none passed, this whole response is no good.
506 1205 100       4907 if (@passed_responses == 0) {
507 269         1711 next;
508             }
509              
510 936 100       1825 if ( defined ($self->{picker_callback}) ) {
511              
512             # Add any results that pass the filter to the array of valid
513             # results to be picked from
514 773         1468 for my $result (@passed_responses) {
515 773         1874 unshift (
516             @$ra_valid_results,
517             $self->_form_response( $result, $Response )
518             );
519             }
520              
521             # See whether this is good enough for the picker
522 773         1417 my $pc = $self->{picker_callback};
523              
524 773         2538 my $more_available =
525             defined $self->{scheduler}->next_available();
526              
527 773         2171 my $picked = $pc->( $ra_valid_results, $more_available );
528              
529             # Found an agreeable response! Use that.
530 773 100       6698 if (defined $picked) {
531 172         1044 $accepted_response = $picked;
532             }
533             }
534             else {
535             # No picker? Just accept the first valid response.
536 163         344 $accepted_response = $self->_form_response(
537             $passed_responses[0],
538             $Response
539             );
540             }
541              
542             }
543             };
544              
545             # Definitely run out of geocoders - let's give the picker one last chance,
546             # just in case.
547 2100 100 100     8876 if (defined ($self->{picker_callback}) && !defined $accepted_response ) {
548 1508         4919 $accepted_response = $self->{picker_callback}->( $ra_valid_results, 0 );
549             }
550              
551             # If we're using a cache and we have a good response, let's cache it.
552 2100 50       8209 if ( !$args->{no_cache} ) {
553 2100         14259 $self->_set_in_cache(
554             $args->{location},
555             $accepted_response,
556             $args->{cache}
557             );
558             }
559              
560 2100         9875 return $accepted_response;
561             }
562              
563             =head2 get_geocoders
564              
565             Returns a reference to a list of the geocoders that have been added to
566             the Many instance
567              
568             =cut
569              
570             sub get_geocoders {
571 420     420 1 6318 my $self = shift;
572              
573 420         590 my $ra_geocoders = [];
574 420         518 foreach my $key ( sort keys %{$self->{geocoders}} ) {
  420         2535  
575 630         692 push @{$ra_geocoders}, $self->{geocoders}->{$key};
  630         1794  
576             }
577 420         955 return $ra_geocoders;
578             }
579              
580              
581             ### INTERNAL METHODS
582              
583             # _geocoder_module_is_compatible_with_plugin
584             #
585             # Check that the installed Geo::Coder module is compatible
586             # with the Geo::Coder::Many plugin, based on a minimum version
587             sub _geocoder_module_is_compatible_with_plugin {
588 420     420   764 my ($self, $module, $plugin) = @_;
589              
590 420 50       3774 if ($plugin->can("_MIN_MODULE_VERSION")) {
591 0         0 my ($have_version, $min_version) = (
592             $module->VERSION,
593             $plugin->_MIN_MODULE_VERSION,
594             );
595              
596 0 0       0 if (versioncmp($have_version, $min_version) < 0) {
597 0         0 carp "$plugin requires $module $min_version or above";
598 0         0 return 0;
599             }
600             }
601              
602 420         1312 return 1;
603             }
604              
605              
606             # _form_response
607             #
608             # Takes a result hash and a Response object and mashes them into a single flat
609             # hash, allowing results from different geocoders to be more easily assimilated
610             #
611             sub _form_response {
612 936     936   1584 my ($self, $rh_result, $response) = @_;
613 936         1949 $rh_result->{location} = $response->{location};
614 936         1714 $rh_result->{geocoder} = $response->{geocoder};
615 936         2557 $rh_result->{response_code} = $response->{response_code};
616 936         3877 return $rh_result;
617             }
618              
619             # _lookup_callback
620             #
621             # Given a name and a list of mappings from names to code references, do a fuzzy
622             # lookup of the name and return the appropriate subroutine.
623             #
624             sub _lookup_callback {
625 144     144   304 my ($self, $name, $rh_callbacks) = @_;
626            
627 144 50       376 ref($name) eq ''
628             or croak( "Trying to look up something which isn't a name!\n" );
629              
630 144         182 while (my ($name_regex, $callback) = each %{$rh_callbacks}) {
  187         710  
631 187         3669 my $regex = qr/^\s*$name_regex\s*$/msx;
632              
633 187 100       1658 if ($name =~ $regex) {
634 144         544 return $callback;
635             }
636             }
637              
638 0         0 carp( "\'$name\' is not a built-in callback.\n" );
639 0         0 return;
640             }
641              
642             # _response_valid
643             #
644             # Checks that a response is defined and has a valid response code,
645             #
646             sub _response_valid {
647 2411     2411   3122 my $self = shift;
648 2411         2601 my $response = shift;
649 2411 50       5009 if ( !defined($response) ) {
650 0         0 return 0;
651             }
652 2411         6177 return HTTP::Response->new( $response->get_response_code )->is_success;
653             }
654              
655             # _passes_filter
656             #
657             # Check a response passes the filter callback (if one is set).
658             #
659             sub _passes_filter {
660 1205     1205   1643 my ($self, $response) = @_;
661 1205 100       2804 if ( !defined $self->{filter_callback} ) {
662 332         970 return 1;
663             }
664 873         2657 return $self->{filter_callback}->( $response );
665             }
666              
667             # _get_next_geocoder
668             #
669             # Requests the next geocoder from the scheduler and looks it up in the geocoders
670             # hash.
671             #
672             sub _get_next_geocoder {
673 3604     3604   3966 my $self = shift;
674              
675 3604         10247 my $next = $self->{scheduler}->get_next_unique();
676 3604 100 66     14735 return if ( (!defined $next) || $next eq '');
677              
678 2411         5677 return $self->{geocoders}{$next};
679             }
680              
681             # _recalculate_geocoder_stats
682             #
683             # Assigns weights to the current geocoders, and initialises the scheduler as
684             # appropriate.
685             #
686             sub _recalculate_geocoder_stats {
687 420     420   574 my $self = shift;
688            
689 420         887 my $ra_geocoders = $self->get_geocoders();
690 420         739 my $ra_slim_geocoders = [];
691              
692 420         546 foreach my $geocoder ( @{$ra_geocoders} ) {
  420         792  
693              
694 630   50     3046 my $tmp = {
695             weight => $geocoder->get_daily_limit() || 1,
696             name => $geocoder->get_name(),
697             };
698 630         66383 push @{$ra_slim_geocoders}, $tmp;
  630         1789  
699             }
700 420         1505 $self->{scheduler} = $self->_new_scheduler($ra_slim_geocoders);
701 420         3307 return;
702             }
703              
704             # _new_scheduler
705             #
706             # Returns an instance of the currently-set scheduler, with the specified
707             # geocoders.
708             #
709             sub _new_scheduler {
710 420     420   629 my $self = shift;
711 420         511 my $geocoders = shift;
712              
713 420         651 my $base_scheduler_name = "Geo::Coder::Many::Scheduler::";
714 420 100       1886 if ($self->{scheduler_type} =~ m/^(WRR|WeightedRandom)$/msx) {
715 280         471 $base_scheduler_name .= "UniquenessScheduler::";
716             }
717 420         675 $base_scheduler_name .= $self->{scheduler_type};
718 420 100       966 if ($self->{use_timeouts}) {
719 210         1015 return Geo::Coder::Many::Scheduler::Selective->new(
720             $geocoders,
721             $base_scheduler_name
722             );
723             }
724 210         1015 return $base_scheduler_name->new($geocoders);
725             }
726              
727             # _set_caching_object
728             #
729             # Set the list of cache objects
730             #
731             sub _set_caching_object {
732 0     0   0 my $self = shift;
733 0         0 my $cache_obj = shift;
734              
735 0         0 $self->_test_cache_object( $cache_obj );
736 0         0 $self->{cache} = $cache_obj;
737 0         0 $self->{cache_enabled} = 1;
738 0         0 return;
739             }
740              
741             # _test_cache_object
742             #
743             # Test the cache to ensure it has 'get', 'set' and 'remove' methods
744             #
745             sub _test_cache_object {
746 0     0   0 my $self = shift;
747 0         0 my $cache_object = shift;
748              
749             # Test to ensure the cache works
750             {
751 0         0 my $result = eval {
  0         0  
752 0         0 $cache_object->set( '1234', 'test' );
753 0 0       0 croak unless( $cache_object->get('1234') eq 'test' );
754 0         0 1;
755             };
756 0 0 0     0 if ( (!$result) || $@ ) {
757 0         0 croak "Unable to use user provided cache object: ". ref($cache_object);
758             }
759             }
760              
761             # Test to ensure the cache supports references
762             {
763 0         0 my $result = eval {
  0         0  
764 0         0 $cache_object->set( 'abc', { a => 1, b => 2, c => 3 });
765 0 0       0 croak unless ( $cache_object->get('abc')->{'b'} == 2 );
766 0         0 1;
767             };
768 0 0 0     0 if ( (!$result) || $@ ) {
769 0         0 croak "Unable to use user provided cache object "
770             . "(references not stored safely): ", ref($cache_object);
771             }
772             }
773              
774 0         0 return;
775             }
776              
777             # _set_in_cache
778             #
779             # Store the result in the cache
780             #
781             sub _set_in_cache {
782 2100     2100   3084 my $self = shift;
783 2100         2837 my $location = shift;
784 2100         2417 my $Response = shift;
785 2100   33     9916 my $cache = shift || $self->{cache};
786              
787 2100 50 33     7952 if ($location && $cache){
788 0   0     0 my $key = $self->_normalize_cache_key( $location ) || $location;
789 0         0 $cache->set( $key, $Response );
790 0         0 return 1;
791             }
792 2100         3288 return 0;
793             }
794              
795             # _get_from_cache
796             #
797             # Check the cache to see if the data is available
798             #
799             sub _get_from_cache {
800 2100     2100   2341 my $self = shift;
801 2100         2640 my $location = shift;
802 2100   33     8983 my $cache = shift || $self->{cache};
803              
804 2100 50 33     4925 if ( $cache && $location ) {
805 0   0     0 my $key = $self->_normalize_cache_key($location) || $location;
806 0         0 my $Response = $cache->get( $key );
807 0 0       0 if ( $Response ) {
808 0         0 $Response->{response_code} = 210;
809 0         0 return $Response;
810             }
811             }
812 2100         3514 return;
813             }
814              
815             # _normalize_cache_key
816             #
817             # Use the provided normalize_code_ref callback (if one is set) to return a
818             # normalized string to use as a cache key.
819             #
820             sub _normalize_cache_key {
821 0     0     my $self = shift;
822 0           my $location = shift;
823              
824 0 0         if ( $self->{normalize_code_ref} ) {
825 0           my $code_ref = $self->{normalize_code_ref};
826 0           return $code_ref->( $location );
827             }
828 0           return $location;
829             }
830              
831             1;
832              
833             __END__