File Coverage

lib/Geo/Coder/Many.pm
Criterion Covered Total %
statement 198 260 76.1
branch 51 88 57.9
condition 16 41 39.0
subroutine 34 38 89.4
pod 6 6 100.0
total 305 433 70.4


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