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