File Coverage

blib/lib/Class/Business/GL/Postalcode.pm
Criterion Covered Total %
statement 101 101 100.0
branch 12 14 85.7
condition n/a
subroutine 22 22 100.0
pod 8 8 100.0
total 143 145 98.6


line stmt bran cond sub pod time code
1             package Class::Business::GL::Postalcode;
2              
3 2     2   8 use strict;
  2         3  
  2         51  
4 2     2   5 use warnings;
  2         3  
  2         39  
5 2     2   539 use utf8;
  2         12  
  2         8  
6 2     2   875 use Data::Handle;
  2         52428  
  2         130  
7 2     2   15 use List::Util qw(first);
  2         2  
  2         176  
8 2     2   481 use Readonly;
  2         2021  
  2         108  
9 2     2   30 use 5.010; #5.10.0
  2         3  
10              
11 2     2   7 use constant NUM_OF_DIGITS_IN_POSTALCODE => 4;
  2         2  
  2         139  
12 2     2   7 use constant NUM_OF_DATA_ELEMENTS => 6;
  2         2  
  2         78  
13 2     2   7 use constant TRUE => 1;
  2         2  
  2         76  
14 2     2   7 use constant FALSE => 0;
  2         2  
  2         1368  
15              
16             Readonly::Scalar my $SEPARATOR => ';';
17              
18             our $VERSION = '0.05';
19              
20             sub new {
21 20007     20007 1 33448 my $class = shift;
22              
23 20007         39881 my $self = bless ({}, $class);
24              
25             #seek DATA, 0, 0;
26             #my @postal_data = ;
27              
28 20007         58915 my $handle = Data::Handle->new( __PACKAGE__ );
29 20007         5244977 my @postal_data = $handle->getlines();
30              
31 20007         2755840 $self->postal_data(\@postal_data);
32              
33 20007         37621 $self->num_of_digits_in_postalcode(NUM_OF_DIGITS_IN_POSTALCODE);
34              
35 20007         56573 return $self;
36             }
37              
38             sub num_of_digits_in_postalcode {
39 1010139     1010139 1 1043237 my ($self, $number) = @_;
40              
41 1010139 100       1407087 if ($number) {
42 20007         28197 $self->{num_of_digits_in_postalcode} = $number;
43              
44 20007         28977 return TRUE;
45             } else {
46 990132         1661176 return $self->{num_of_digits_in_postalcode};
47             }
48             }
49              
50             sub postal_data {
51 50019     50019 1 60226 my ($self, $postal_data) = @_;
52              
53 50019 100       80830 if ($postal_data) {
54 20007         38696 $self->{postal_data} = $postal_data;
55              
56 20007         32717 return TRUE;
57             } else {
58 30012         90333 return $self->{postal_data};
59             }
60             }
61              
62             sub get_all_postalcodes {
63 30002     30002 1 40992 my $self = shift;
64              
65 30002         40010 my @postalcodes = ();
66              
67 30002         26674 foreach my $line ( @{$self->postal_data} ) {
  30002         48898  
68 990066         1567639 $self->_retrieve_postalcode( \@postalcodes, $line );
69             }
70              
71 30002         60880 return \@postalcodes;
72             }
73              
74             sub get_all_cities {
75 2     2 1 455 my ($self) = @_;
76              
77 2         3 my @cities = ();
78              
79 2         4 foreach my $line ( @{$self->postal_data} ) {
  2         3  
80 66         104 $self->_retrieve_city( \@cities, $line );
81             }
82              
83 2         21 return \@cities;
84             }
85              
86             sub _retrieve_postalcode {
87 990066     990066   1071077 my ( $self, $postalcodes, $string ) = @_;
88              
89             ## no critic qw(RegularExpressions::RequireLineBoundaryMatching RegularExpressions::RequireExtendedFormatting RegularExpressions::RequireDotMatchAnything)
90 990066         3471130 my @entries = split /$SEPARATOR/x, $string, NUM_OF_DATA_ELEMENTS;
91              
92 990066         1443659 my $num_of_digits_in_postalcode = $self->num_of_digits_in_postalcode();
93              
94 990066 50       4399247 if ($entries[0] =~ m{
95             ^ #beginning of string
96             \d{$num_of_digits_in_postalcode} #digits in postalcode
97             $ #end of string
98             }xsm
99             )
100             {
101 990066         875331 push @{$postalcodes}, $entries[0];
  990066         1770721  
102             }
103              
104 990066         2262903 return;
105             }
106              
107             sub _retrieve_city {
108 66     66   66 my ( $self, $postalcodes, $string ) = @_;
109              
110             ## no critic qw(RegularExpressions::RequireLineBoundaryMatching RegularExpressions::RequireExtendedFormatting RegularExpressions::RequireDotMatchAnything)
111 66         234 my @entries = split /$SEPARATOR/x, $string, NUM_OF_DATA_ELEMENTS;
112              
113 66         92 my $num_of_digits_in_postalcode = $self->num_of_digits_in_postalcode();
114              
115 66 50       307 if ($entries[0] =~ m{
116             ^ #beginning of string
117             \d{$num_of_digits_in_postalcode} #digits in postalcode
118             $ #end of string
119             }xsm
120             )
121             {
122 66         52 push @{$postalcodes}, $entries[1];
  66         124  
123             }
124              
125 66         173 return;
126             }
127              
128             sub validate {
129 30000     30000 1 161173 my ($self, $number) = @_;
130              
131 30000         49353 my $postalcodes = $self->get_all_postalcodes();
132              
133 30000 100   988416   106373 if (first { $number == $_ } @{$postalcodes}) {
  988416         1052298  
  30000         102269  
134 99         681 return TRUE;
135             } else {
136 29901         178166 return FALSE;
137             }
138             }
139              
140             sub get_city_from_postalcode {
141 2     2 1 682 my ($self, $postalcode) = @_;
142              
143             #validate( @_, {
144             # zipcode => { type => SCALAR, regex => qr/^\d+$/, }, });
145              
146 2         6 my $postaldata = $self->postal_data();
147              
148 2         2 my $city = '';
149 2         3 foreach my $line (@{$postaldata}) {
  2         5  
150 4         26 my @entries = split /$SEPARATOR/x, $line, NUM_OF_DATA_ELEMENTS;
151              
152 4 100       11 if ($entries[0] eq $postalcode) {
153 2         2 $city = $entries[1];
154 2         4 last;
155             }
156             }
157              
158 2         11 return $city;
159             }
160              
161             sub get_postalcode_from_city {
162 2     2 1 706 my ($self, $city) = @_;
163              
164             #validate( @_, {
165             # city => { type => SCALAR, regex => qr/^[\w ]+$/, }, });
166              
167 2         5 my $postaldata = $self->postal_data();
168              
169 2         4 my @postalcodes;
170 2         3 foreach my $line (@{$postaldata}) {
  2         6  
171 66         174 my @entries = split /$SEPARATOR/x, $line, NUM_OF_DATA_ELEMENTS;
172              
173 66 100       217 if ($entries[1] =~ m/$city$/i) {
174 2         7 push @postalcodes, $entries[0];
175             }
176             }
177              
178 2         18 return \@postalcodes;
179             }
180              
181             1;
182              
183             =pod
184              
185             =begin HTML
186              
187            
188              
189             =end HTML
190              
191             =head1 NAME
192              
193             Class::Business::GL::Postalcode - OO interface to validation and listing of Greenland postal codes
194              
195             =head1 VERSION
196              
197             This documentation describes version 0.02
198              
199             =head1 SYNOPSIS
200              
201             # construction
202             my $validator = Business::GL::Postalcode->new();
203              
204             # basic validation of string
205             if ($validator->validate($postalcode)) {
206             print "We have a valid Greenland postal code\n";
207             } else {
208             warn "Not a valid Greenland postal code\n";
209             }
210              
211              
212             # All postal codes for use outside this module
213             my @postalcodes = @{$validator->get_all_postalcodes()};
214              
215              
216             # All postal codes and data for use outside this module
217             my $postalcodes = $validator->get_all_data();
218              
219             foreach (@{postalcodes}) {
220             printf
221             'postal code: %s city: %s street/desc: %s company: %s province: %d country: %d', split /;/, $_, 6;
222             }
223              
224             =head1 FEATURES
225              
226             =over
227              
228             =item * Providing list of Greenland postal codes and related area names
229              
230             =item * Look up methods for Greenland postal codes for web applications and the like
231              
232             =item * The postal code from Santa Claus (father Christmas)
233              
234             =back
235              
236             =head1 DESCRIPTION
237              
238             This distribution is not the original resource for the included data, but simply
239             acts as a simple distribution for Perl use. The central source is monitored so this
240             distribution can contain the newest data. The monitor script (F) is
241             included in the distribution: L.
242              
243             The data are converted for inclusion in this module. You can use different extraction
244             subroutines depending on your needs:
245              
246             =over
247              
248             =item * L, to retrieve all data, data description below in L.
249              
250             =item * L, to retrieve all postal codes
251              
252             =item * L, to retieve all cities
253              
254             =item * L, to retrieve one or more postal codes from a city name
255              
256             =item * L, to retieve a city name from a postal code
257              
258             =back
259              
260             =head2 Data
261              
262             Here follows a description of the included data, based on the description from
263             the original source and the authors interpretation of the data, including
264             details on the distribution of the data.
265              
266             =head3 city name
267              
268             A non-unique, case-sensitive representation of a city name in Greenlandic or Danish.
269              
270             =head3 street/description
271              
272             This field is unused for this dataset.
273              
274             =head3 company name
275              
276             This field is unused for this dataset.
277              
278             =head3 province
279              
280             This field is a bit special and it's use is expected to be related to distribution
281             all entries are marked as 'False'. The data are included since they are a part of
282             the original data.
283              
284             =head3 country
285              
286             Since the original source contains data on 3 different countries:
287              
288             =over
289              
290             =item * Denmark (1)
291              
292             =item * Greenland (2)
293              
294             =item * Faroe Islands (3)
295              
296             =back
297              
298             Only the data representing Greenland has been included in this distribution, so this
299             field is always containing a '2'.
300              
301             For access to the data on Denmark or Faroe Islands please refer to: L
302             and L respectfully.
303              
304             =head2 Encoding
305              
306             The data distributed are in Greenlandic and Danish for descriptions and names and these are encoded in UTF-8.
307              
308             =head1 SUBROUTINES AND METHODS
309              
310             =head2 new
311              
312             Basic contructor, takes no arguments. Load the dataset and returns
313             a Class::Business::GL::Postalcode object.
314              
315             =head2 validate
316              
317             A simple validator for Greenlandic postal codes.
318              
319             Takes a string representing a possible Greenlandic postal code and returns either
320             B<1> or B<0> indicating either validity or invalidity.
321              
322             my $validator = Business::GL::Postalcode->new();
323              
324             my $rv = $validator->validate(3900);
325              
326             if ($rv == 1) {
327             print "We have a valid Greenlandic postal code\n";
328             } ($rv == 0) {
329             print "Not a valid Greenlandic postal code\n";
330             }
331              
332             =head2 get_all_postalcodes
333              
334             Takes no parameters.
335              
336             Returns a reference to an array containing all valid Danish postal codes.
337              
338             my $validator = Business::GL::Postalcode->new();
339              
340             my $postalcodes = $validator->get_all_postalcodes;
341              
342             foreach my $postalcode (@{$postalcodes}) { ... }
343              
344             =head2 get_all_cities
345              
346             Takes no parameters.
347              
348             Returns a reference to an array containing all Danish city names having a postal code.
349              
350             my $validator = Business::GL::Postalcode->new();
351              
352             my $cities = $validator->get_all_cities;
353              
354             foreach my $city (@{$cities}) { ... }
355              
356             Please note that this data source used in this distribution by no means is authorative
357             when it comes to cities located in Denmark, it might have all cities listed, but
358             unfortunately also other post distribution data.
359              
360             =head2 get_city_from_postalcode
361              
362             Takes a string representing a Danish postal code.
363              
364             Returns a single string representing the related city name or an empty string indicating nothing was found.
365              
366             my $validator = Business::GL::Postalcode->new();
367              
368             my $zipcode = '3900';
369              
370             my $city = $validator->get_city_from_postalcode($zipcode);
371              
372             if ($city) {
373             print "We found a city for $zipcode\n";
374             } else {
375             warn "No city found for $zipcode";
376             }
377              
378             =head2 get_postalcode_from_city
379              
380             Takes a string representing a Danish/Greenlandic city name.
381              
382             Returns a reference to an array containing zero or more postal codes related to that city name. Zero indicates nothing was found.
383              
384             Please note that city names are not unique, hence the possibility of a list of postal codes.
385              
386             my $validator = Business::GL::Postalcode->new();
387              
388             my $city = 'Nuuk';
389              
390             my $postalcodes = $validator->get_postalcode_from_city($city);
391              
392             if (scalar @{$postalcodes} == 1) {
393             print "$city is unique\n";
394             } elsif (scalar @{$postalcodes} > 1) {
395             warn "$city is NOT unique\n";
396             } else {
397             die "$city not found\n";
398             }
399              
400             =head2 num_of_digits_in_postalcode
401              
402             Mutator to get/set the number of digits used to compose a Greenlandic postal code
403              
404             my $validator = Business::GL::Postalcode->new();
405              
406             my $rv = $validator->num_of_digits_in_postalcode(4);
407              
408             my $digits = $validator->num_of_digits_in_postalcode();
409              
410             =head2 postal_data
411              
412             Mutator to get/set the reference to the array comprising the main data structure
413              
414             my $validator = Business::GL::Postalcode->new();
415              
416             my $rv = $validator->postal_data(\@postal_data);
417              
418             my $postal_data = $validator->postal_data();
419              
420             =head1 DIAGNOSTICS
421              
422             There are not special diagnostics apart from the ones related to the different
423             subroutines.
424              
425             =head1 CONFIGURATION AND ENVIRONMENT
426              
427             This distribution requires no special configuration or environment.
428              
429             =head1 DEPENDENCIES
430              
431             =over
432              
433             =item * L (core)
434              
435             =item * L (core)
436              
437             =item * L
438              
439             =item * L
440              
441             =item * L
442              
443             =back
444              
445             =head2 TEST
446              
447             Please note that the above list does not reflect requirements for:
448              
449             =over
450              
451             =item * Additional components in this distribution, see F. Additional
452             components list own requirements
453              
454             =item * Test and build system, please see: F for details
455              
456             =back
457              
458             =head1 BUGS AND LIMITATIONS
459              
460             There are no known bugs at this time.
461              
462             The data source used in this distribution by no means is authorative when it
463             comes to cities located in Denmark, it might have all cities listed, but
464             unfortunately also other post distribution data.
465              
466             =head1 BUG REPORTING
467              
468             Please report issues via CPAN RT:
469              
470             =over
471              
472             =item * Web (RT): L
473              
474             =item * Web (Github): L
475              
476             =item * Email (RT): L
477              
478             =back
479              
480             =head1 SEE ALSO
481              
482             =over
483              
484             =item L
485              
486             =item L
487              
488             =back
489              
490             =head1 MOTIVATION
491              
492             Postdanmark the largest danish postal and formerly stateowned postal service, maintain the
493             postalcode mapping for Greenland and the Faroe Islands. Since I am using this resource to
494             maintain the danish postalcodes I decided to release distributions of these two other countries.
495              
496             =head1 AUTHOR
497              
498             Jonas B. Nielsen, (jonasbn) - C<< >>
499              
500             =head1 COPYRIGHT
501              
502             Class-Business-GL-Postalcode is (C) by Jonas B. Nielsen, (jonasbn) 2014-2015
503              
504             Class-Business-GL-Postalcode is released under the artistic license 2.0
505              
506             =cut
507              
508             __DATA__