File Coverage

blib/lib/Business/DK/Postalcode.pm
Criterion Covered Total %
statement 146 146 100.0
branch 26 26 100.0
condition n/a
subroutine 25 25 100.0
pod 8 8 100.0
total 205 205 100.0


line stmt bran cond sub pod time code
1             package Business::DK::Postalcode;
2              
3 3     3   195768 use strict;
  3         24  
  3         92  
4 3     3   15 use warnings;
  3         6  
  3         79  
5 3     3   1195 use Tree::Simple;
  3         7164  
  3         15  
6 3     3   97 use base qw(Exporter);
  3         7  
  3         380  
7 3     3   1631 use Params::Validate qw(validate_pos SCALAR ARRAYREF OBJECT);
  3         24750  
  3         221  
8 3     3   1207 use utf8;
  3         34  
  3         15  
9 3     3   127 use 5.010; #5.10.0
  3         11  
10              
11 3     3   14 use constant TRUE => 1;
  3         7  
  3         184  
12 3     3   17 use constant FALSE => 0;
  3         12  
  3         149  
13 3     3   25 use constant NUM_OF_DATA_ELEMENTS => 6;
  3         7  
  3         135  
14 3     3   16 use constant NUM_OF_DIGITS_IN_POSTALCODE => 4;
  3         6  
  3         239  
15              
16             ## no critic (Variables::ProhibitPackageVars)
17             our @postal_data = ;
18              
19 3     3   21 no strict 'refs';
  3         5  
  3         5166  
20              
21             my $regex;
22              
23             our $VERSION = '0.12';
24             our @EXPORT_OK
25             = qw(get_all_postalcodes get_all_cities get_all_data create_regex validate_postalcode validate get_city_from_postalcode get_postalcode_from_city);
26              
27             # TODO: we have to disable this policy here for some reason?
28             ## no critic (Subroutines::RequireArgUnpacking)
29             sub validate_postalcode {
30 10013     10013 1 80759 my ($postalcode) = @_;
31              
32             #loose check since we are doing actual validation, so we just need a scalar
33 10013         69212 validate_pos( @_, { type => SCALAR }, );
34              
35 10011 100       27453 if ( not $regex ) {
36 1         1 $regex = ${ create_regex() };
  1         3  
37             }
38              
39 10011 100       69983 if (my ($untainted_postalcode)
40             = $postalcode =~ m{
41             \A #beginning of string
42             ($regex) #generated regular expression, capturing
43             \z #end of string
44             }xsm
45             )
46             {
47 1199         9238 return $untainted_postalcode;
48             } else {
49 8812         23548 return FALSE;
50             }
51             }
52              
53             ## no critic (Subroutines::RequireArgUnpacking)
54             sub validate {
55              
56             #validation happens in next step (see: validate_postalcode)
57 14     14 1 34 return validate_postalcode( $_[0] );
58             }
59              
60             sub get_all_data {
61 4     4 1 9066 return \@postal_data;
62             }
63              
64             sub get_all_cities {
65 1     1 1 2069609 my @cities = ();
66              
67 1         30 _retrieve_cities( \@cities );
68              
69 1         9 return \@cities;
70              
71             }
72              
73             sub get_city_from_postalcode {
74 3     3 1 8739 my ($parameter_data) = @_;
75 3         7 my $city = '';
76              
77 3         15 validate( @_, {
78             zipcode => { type => SCALAR }, });
79              
80 2         12 my $postaldata = get_all_data();
81              
82 2         4 foreach my $line (@{$postaldata}) {
  2         5  
83 1960         5001 my @entries = split /\t/x, $line, NUM_OF_DATA_ELEMENTS;
84              
85 1960 100       4196 if ($entries[0] eq $parameter_data) {
86 1         2 $city = $entries[1];
87 1         4 last;
88             }
89             }
90              
91 2         14 return $city;
92             }
93              
94             sub get_postalcode_from_city {
95 2     2 1 10189 my ($parameter_data) = @_;
96 2         5 my @postalcodes;
97              
98 2         13 validate( @_, {
99             city => { type => SCALAR }, });
100              
101 1         5 my $postaldata = get_all_data();
102              
103 1         2 foreach my $line (@{$postaldata}) {
  1         3  
104 1285         3432 my @entries = split /\t/x, $line, NUM_OF_DATA_ELEMENTS;
105              
106 1285 100       3642 if ($entries[1] =~ m/$parameter_data$/i) {
107 1         6 push @postalcodes, $entries[0];
108             }
109             }
110              
111 1         10 return @postalcodes;
112             }
113              
114             sub get_all_postalcodes {
115 6     6 1 27647 my ($parameter_data) = @_;
116 6         17 my @postalcodes = ();
117              
118 6         81 validate_pos( @_, { type => ARRAYREF, optional => TRUE }, );
119              
120 6 100       29 if ( not $parameter_data ) {
121 5         28 @{$parameter_data} = @postal_data;
  5         1360  
122             }
123              
124 6         23 foreach my $zipcode ( @{$parameter_data} ) {
  6         17  
125 6427         10737 _retrieve_postalcode( \@postalcodes, $zipcode );
126             }
127              
128 6         403 return \@postalcodes;
129             }
130              
131             sub _retrieve_cities {
132 1     1   3 my ( $cities ) = @_;
133              
134             #this is used internally, but we stick it in here just to make sure we
135             #get what we want
136 1         28 validate_pos( @_, { type => ARRAYREF }, );
137              
138 1         17 foreach my $line (@postal_data) {
139 1285         3451 my @entries = split /\t/x, $line, NUM_OF_DATA_ELEMENTS;
140              
141 1285         1456 push @{$cities}, $entries[1];
  1285         2434  
142             }
143              
144 1         3 return;
145             }
146              
147             sub _retrieve_postalcode {
148 6431     6431   47427 my ( $postalcodes, $string ) = @_;
149              
150             #this is used internally, but we stick it in here just to make sure we
151             #get what we want
152 6431         63459 validate_pos( @_, { type => ARRAYREF }, { type => SCALAR, regex => qr/[\w\t]+/, }, );
153              
154             ## no critic qw(RegularExpressions::RequireLineBoundaryMatching RegularExpressions::RequireExtendedFormatting RegularExpressions::RequireDotMatchAnything)
155 6429         66394 my @entries = split /\t/x, $string, NUM_OF_DATA_ELEMENTS;
156              
157 6429 100       9330 if ($entries[0] =~ m{
158             ^ #beginning of string
159 6429         23475 \d{${\NUM_OF_DIGITS_IN_POSTALCODE}} #digits in postalcode
160             $ #end of string
161             }xsm
162             )
163             {
164 6428         8197 push @{$postalcodes}, $entries[0];
  6428         11167  
165             }
166              
167 6429         14148 return 1;
168             }
169              
170             sub create_regex {
171 3     3 1 17 my ($postalcodes) = @_;
172              
173 3         30 validate_pos( @_, { type => ARRAYREF, optional => 1 } );
174              
175 3 100       15 if ( not $postalcodes ) {
176 1         2 $postalcodes = get_all_postalcodes();
177             }
178              
179 3         51 my $tree = Tree::Simple->new( 'ROOT', Tree::Simple->ROOT );
180              
181 3         248 foreach my $postalcode ( @{$postalcodes} ) {
  3         13  
182 3855         6348 _build_tree( $tree, $postalcode );
183             }
184              
185 3         8 my $generated_regex = [];
186              
187 3         19 my $no_of_children = $tree->getChildCount();
188              
189 3         20 foreach my $child ( $tree->getAllChildren() ) {
190 30 100       3701 if ( $child->getIndex() < ( $tree->getChildCount() - 1 ) ) {
191 27         1065 $child->insertSibling( $child->getIndex() + 1,
192             Tree::Simple->new(q{|}) );
193             }
194             }
195 3         200 $tree->insertChild( 0, Tree::Simple->new('(') );
196 3         270 $tree->addChild( Tree::Simple->new(')') );
197              
198             $tree->traverse(
199             sub {
200 10623     10623   286861 my ($_tree) = shift;
201              
202             #DEBUG section - outputs tree to STDERR
203             # warn "\n";
204             # $tree->traverse(
205             # sub {
206             # my ($traversal_tree) = @_;
207             # warn( "\t" x $traversal_tree->getDepth() )
208             # . $traversal_tree->getNodeValue() . "\n";
209             # }
210             # );
211              
212 10623         16716 my $no_of_children = $_tree->getChildCount();
213              
214 10623 100       53593 if ( $no_of_children > 1 ) {
215              
216 735         1180 foreach my $child ( $_tree->getAllChildren() ) {
217 4278 100       529597 if ($child->getIndex() < ( $_tree->getChildCount() - 1 ) )
218             {
219 3543         115348 $child->insertSibling( $child->getIndex() + 1,
220             Tree::Simple->new(q{|}) );
221             }
222             }
223              
224             #first element
225 735         31783 $_tree->insertChild( 0, Tree::Simple->new('(') );
226              
227             #last element
228 735         82203 $_tree->addChild( Tree::Simple->new(')') );
229             }
230             }
231 3         313 );
232              
233             #traverses the tree and creates a flat list of the tree
234             $tree->traverse(
235             sub {
236 10623     10623   217995 my ($traversal_tree) = shift;
237 10623         11647 push @{$generated_regex}, $traversal_tree->getNodeValue();
  10623         16969  
238             }
239 3         118 );
240              
241             #stringifies the flat list so we have a string representation of the
242             #generated regular expression
243 3         95 my $result = join q{}, @{$generated_regex};
  3         470  
244              
245 3         552 return \$result;
246             }
247              
248             sub _build_tree {
249 5146     5146   467008 my ( $tree, $postalcode ) = @_;
250              
251             ## no critic qw(RegularExpressions::RequireLineBoundaryMatching RegularExpressions::RequireExtendedFormatting RegularExpressions::RequireDotMatchAnything)
252 5146         15618 validate_pos(
253             @_,
254             { type => OBJECT, isa => 'Tree::Simple' },
255 5146         89067 { type => SCALAR, regex => qr/\d{${\NUM_OF_DIGITS_IN_POSTALCODE}}/, },
256             );
257              
258 5145         55293 my $oldtree = $tree;
259              
260 5145         13337 my @digits = split //xsm, $postalcode, NUM_OF_DIGITS_IN_POSTALCODE;
261 5145         12690 for ( my $i = 0; $i < scalar @digits; $i++ ) {
262              
263 20580 100       150328 if ( $i == 0 ) {
264 5145         6448 $tree = $oldtree;
265             }
266              
267 20580         43231 my $subtree = Tree::Simple->new( $digits[$i] );
268              
269 20580         624490 my @children = $tree->getAllChildren();
270 20580         123233 my $child = undef;
271 20580         30336 foreach my $c (@children) {
272 82613 100       375269 if ( $c->getNodeValue() == $subtree->getNodeValue() ) {
273 13128         61628 $child = $c;
274 13128         17722 last;
275             }
276             }
277              
278 20580 100       52681 if ($child) {
279 13128         32126 $tree = $child;
280             } else {
281 7452         15860 $tree->addChild($subtree);
282 7452         733762 $tree = $subtree;
283             }
284             }
285 5145         10472 $tree = $oldtree;
286              
287 5145         13090 return 1;
288             }
289              
290             1;
291              
292             =encoding UTF-8
293              
294             =pod
295              
296             =begin markdown
297              
298             [![CPAN version](https://badge.fury.io/pl/Business-DK-Postalcode.svg)](http://badge.fury.io/pl/Business-DK-Postalcode)
299             [![Build Status](https://travis-ci.org/jonasbn/bdkpst.svg?branch=master)](https://travis-ci.org/jonasbn/bdkpst)
300             [![Coverage Status](https://coveralls.io/repos/jonasbn/bdkpst/badge.png?branch=master)](https://coveralls.io/r/jonasbn/bdkpst?branch=master)
301              
302             =end markdown
303              
304             =head1 NAME
305              
306             Business::DK::Postalcode - Danish postal code validator and container
307              
308             =head1 VERSION
309              
310             This documentation describes version 0.08
311              
312             =head1 SYNOPSIS
313              
314             # basic validation of string
315             use Business::DK::Postalcode qw(validate);
316              
317             if (validate($postalcode)) {
318             print "We have a valid Danish postalcode\n";
319             } else {
320             warn "Not a valid Danish postalcode\n";
321             }
322              
323              
324             # basic validation of string, using less intrusive subroutine
325             use Business::DK::Postalcode qw(validate_postalcode);
326              
327             if (validate_postalcode($postalcode)) {
328             print "We have a valid Danish postal code\n";
329             } else {
330             warn "Not a valid Danish postal code\n";
331             }
332              
333              
334             # using the untainted return value
335             use Business::DK::Postalcode qw(validate_postalcode);
336              
337             if (my $untainted = validate_postalcode($postalcode)) {
338             print "We have a valid Danish postal code: $untainted\n";
339             } else {
340             warn "Not a valid Danish postal code\n";
341             }
342              
343              
344             # extracting a regex for validation of Danish postal codes
345             use Business::DK::Postalcode qw(create_regex);
346              
347             my $regex_ref = ${create_regex()};
348              
349             if ($postalcode =~ m/$regex/) {
350             print "We have a valid Danish postal code\n";
351             } else {
352             warn "Not a valid Danish postal code\n";
353             }
354              
355              
356             # All postal codes for use outside this module
357             use Business::DK::Postalcode qw(get_all_postalcodes);
358              
359             my @postalcodes = @{get_all_postalcodes()};
360              
361              
362             # All postal codes and data for use outside this module
363             use Business::DK::Postalcode qw(get_all_data);
364              
365             my $postalcodes = get_all_data();
366              
367             foreach (@{postalcodes}) {
368             printf
369             'postal code: %s city: %s street/desc: %s company: %s province: %d country: %d', split /\t/, $_, 6;
370             }
371              
372             =head1 FEATURES
373              
374             =over
375              
376             =item * Providing list of Danish postal codes and related area names
377              
378             =item * Look up methods for Danish postal codes for web applications and the like
379              
380             =back
381              
382             =head1 DESCRIPTION
383              
384             This distribution is not the original resource for the included data, but simply
385             acts as a simple distribution for Perl use. The central source is monitored so this
386             distribution can contain the newest data. The monitor script (F) is
387             included in the distribution.
388              
389             The data are converted for inclusion in this module. You can use different extraction
390             subroutines depending on your needs:
391              
392             =over
393              
394             =item * L, to retrieve all data, data description below in L.
395              
396             =item * L, to retrieve all postal codes
397              
398             =item * L, to retieve all cities
399              
400             =item * L, to retrieve one or more postal codes from a city name
401              
402             =item * L, to retieve a city name from a postal code
403              
404             =back
405              
406             =head2 Data
407              
408             Here follows a description of the included data, based on the description from
409             the original source and the authors interpretation of the data, including
410             details on the distribution of the data.
411              
412             =head3 city name
413              
414             A non-unique, case-sensitive representation of a city name in Danish.
415              
416             =head3 street/description
417              
418             This field is either a streetname or a description, is it only provided for
419             a few special records.
420              
421             =head3 company name
422              
423             This field is only provided for a few special records.
424              
425             =head3 province
426              
427             This field is a bit special and it's use is expected to be related to distribution
428             all entries inside Copenhagen are marked as 'False' in this column and 'True' for
429             all entries outside Copenhagen - and this of course with exceptions. The data are
430             included since they are a part of the original data.
431              
432             =head3 country
433              
434             Since the original source contains data on 3 different countries:
435              
436             =over
437              
438             =item * Denmark
439              
440             =item * Greenland
441              
442             =item * Faroe Islands
443              
444             =back
445              
446             Only the data representing Denmark has been included in this distribtion, so this
447             field is always containing a one.
448              
449             For access to the data on Greenland or Faroe Islands please refer to: L
450             and L respectfully.
451              
452             =head2 Encoding
453              
454             The data distributed are in Danish for descriptions and names and these are encoded in UTF-8.
455              
456             =head1 EXAMPLES
457              
458             A web application example is included in the examples directory following this distribution
459             or available at L.
460              
461             =head1 SUBROUTINES AND METHODS
462              
463             =head2 validate
464              
465             A simple validator for Danish postal codes.
466              
467             Takes a string representing a possible Danish postal code and returns either
468             B<1> or B<0> indicating either validity or invalidity.
469              
470             my $rv = validate(2665);
471              
472             if ($rv == 1) {
473             print "We have a valid Danish postal code\n";
474             } ($rv == 0) {
475             print "Not a valid Danish postal code\n";
476             }
477              
478             =head2 validate_postalcode
479              
480             A less intrusive subroutine for import. Acts as a wrapper of L.
481              
482             my $rv = validate_postalcode(2300);
483              
484             if ($rv) {
485             print "We have a valid Danish postal code\n";
486             } else {
487             print "Not a valid Danish postal code\n";
488             }
489              
490             =head2 get_all_data
491              
492             Returns a reference to a a list of strings, separated by tab characters. See
493             L for a description of the fields.
494              
495             use Business::DK::Postalcode qw(get_all_data);
496              
497             my $postalcodes = get_all_data();
498              
499             foreach (@{postalcodes}) {
500             printf
501             'postalcode: %s city: %s street/desc: %s company: %s province: %d country: %d', split /\t/, $_, 6;
502             }
503              
504             =head2 get_all_postalcodes
505              
506             Takes no parameters.
507              
508             Returns a reference to an array containing all valid Danish postal codes.
509              
510             use Business::DK::Postalcode qw(get_all_postalcodes);
511              
512             my $postalcodes = get_all_postalcodes;
513              
514             foreach my $postalcode (@{$postalcodes}) { ... }
515              
516             =head2 get_all_cities
517              
518             Takes no parameters.
519              
520             Returns a reference to an array containing all Danish city names having a postal code.
521              
522             use Business::DK::Postalcode qw(get_all_cities);
523              
524             my $cities = get_all_cities;
525              
526             foreach my $city (@{$cities}) { ... }
527              
528             Please note that this data source used in this distribution by no means is authorative
529             when it comes to cities located in Denmark, it might have all cities listed, but
530             unfortunately also other post distribution data.
531              
532             =head2 get_city_from_postalcode
533              
534             Takes a string representing a Danish postal code.
535              
536             Returns a single string representing the related city name or an empty string indicating nothing was found.
537              
538             use Business::DK::Postalcode qw(get_city_from_postalcode);
539              
540             my $zipcode = '2300';
541              
542             my $city = get_city_from_postalcode($zipcode);
543              
544             if ($city) {
545             print "We found a city for $zipcode\n";
546             } else {
547             warn "No city found for $zipcode";
548             }
549              
550             =head2 get_postalcode_from_city
551              
552             Takes a string representing a Danish city name.
553              
554             Returns a reference to an array containing zero or more postal codes related to that city name. Zero indicates nothing was found.
555              
556             Please note that city names are not unique, hence the possibility of a list of postal codes.
557              
558             use Business::DK::Postalcode qw(get_postalcode_from_city);
559              
560             my $city = 'København K';
561              
562             my $postalcodes = get_postalcode_from_city($city);
563              
564             if (scalar @{$postalcodes} == 1) {
565             print "$city is unique\n";
566             } elsif (scalar @{$postalcodes} > 1) {
567             warn "$city is NOT unique\n";
568             } else {
569             die "$city not found\n";
570             }
571              
572             =head2 create_regex
573              
574             This method returns a generated regular expression for validation of a string
575             representing a possible Danish postal code.
576              
577             use Business::DK::Postalcode qw(create_regex);
578              
579             my $regex_ref = ${create_regex()};
580              
581             if ($postalcode =~ m/$regex/) {
582             print "We have a valid Danish postalcode\n";
583             } else {
584             print "Not a valid Danish postalcode\n";
585             }
586              
587             =head1 PRIVATE SUBROUTINES AND METHODS
588              
589             =head2 _retrieve_cities
590              
591             Takes a reference to an array based on the DATA section and return a reference
592             to an array containing only city names.
593              
594             =head3 _retrieve_postalcode
595              
596             Takes a reference to an array based on the DATA section and return a reference
597             to an array containing only postal codes.
598              
599             =head3 _build_tree
600              
601             Internal method to assist L in generating the regular expression.
602              
603             Takes a L object and a reference to an array of data elements.
604              
605             =head1 DIAGNOSTICS
606              
607             There are not special diagnostics apart from the ones related to the different
608             subroutines.
609              
610             =head1 CONFIGURATION AND ENVIRONMENT
611              
612             This distribution requires no special configuration or environment.
613              
614             =head1 DEPENDENCIES
615              
616             =over
617              
618             =item * L (core)
619              
620             =item * L (core)
621              
622             =item * L
623              
624             =item * L
625              
626             =back
627              
628             =head2 TEST
629              
630             Please note that the above list does not reflect requirements for:
631              
632             =over
633              
634             =item * Additional components in this distribution, see F. Additional
635             components list own requirements
636              
637             =item * Test and build system, please see: F for details
638              
639             =item * Requirements for scripts in the F directory
640              
641             =item * Requirements for examples in the F directory
642              
643             =back
644              
645             =head1 BUGS AND LIMITATIONS
646              
647             There are no known bugs at this time.
648              
649             The data source used in this distribution by no means is authorative when it
650             comes to cities located in Denmark, it might have all cities listed, but
651             unfortunately also other post distribution data.
652              
653             =head1 BUG REPORTING
654              
655             Please report issues via CPAN RT:
656              
657             =over
658              
659             =item * Web (RT): L
660              
661             =item * Web (Github): L
662              
663             =item * Email (RT): L
664              
665             =back
666              
667             =head1 INCOMPATIBILITIES
668              
669             There are no known incompatibilities at this time.
670              
671             =head1 TEST AND QUALITY
672              
673             =head2 Perl::Critic
674              
675             This version of the code is complying with L a severity: 1
676              
677             The following policies have been disabled.
678              
679             =over
680              
681             =item * L
682              
683             Disabled locally using 'no critic' pragma.
684              
685             The module uses a package variable as a cache, this might not prove usefull in
686             the long term, so when this is adressed and this might address this policy.
687              
688             =item * L
689              
690             Disabled locally using 'no critic' pragma.
691              
692             This policy is violated when using L at some point this will
693             be investigated further, this might be an issue due to referral to @_.
694              
695             =item * L
696              
697             Disabled locally using 'no critic' pragma.
698              
699             This is disabled for some two basic regular expressions.
700              
701             =item * L
702              
703             Disabled locally using 'no critic' pragma.
704              
705             This is disabled for some two basic regular expressions.
706              
707             =item * L
708              
709             Disabled locally using 'no critic' pragma.
710              
711             This is disabled for some two basic regular expressions.
712              
713             =item * L
714              
715             Constants are good, - see the link below.
716              
717             =item * L
718              
719             =item * L
720              
721             This one interfers with our DATA section, perhaps DATA should go before POD,
722             well it is not important so I have disabled the policy.
723              
724             =item * L
725              
726             This would require a re-write of part of the code. Currently I rely on use of the iterator in the F loop, so it would require significant
727             changes.
728              
729             =item * L
730              
731             Temporarily disabled, marked for follow-up
732              
733             =back
734              
735             Please see F for details.
736              
737             =head2 TEST COVERAGE
738              
739             Test coverage report is generated using L via L,
740             for the version described in this documentation (See L).
741              
742             ---------------------------- ------ ------ ------ ------ ------ ------ ------
743             File stmt bran cond sub pod time total
744             ---------------------------- ------ ------ ------ ------ ------ ------ ------
745             ...Business/DK/Postalcode.pm 100.0 100.0 n/a 100.0 100.0 98.7 100.0
746             ...Business/DK/Postalcode.pm 100.0 100.0 n/a 100.0 100.0 1.2 100.0
747             Total 100.0 100.0 n/a 100.0 100.0 100.0 100.0
748             ---------------------------- ------ ------ ------ ------ ------ ------ ------
749              
750             $ ./Build testcover
751              
752             =head1 SEE ALSO
753              
754             =over
755              
756             =item * Main data source: L
757              
758             =item * Information resource on data source: L
759              
760             =item * Alternative implementation: L
761              
762             =item * Alternative validation: L
763              
764             =item * Related complementary implementation: L
765              
766             =item * Related complementary implementation: L
767              
768             =item * Related implementation, same author: L
769              
770             =item * Related implementation, same author: L
771              
772             =item * Related implementation, same author: L
773              
774             =item * Related implementation, same author: L
775              
776             =back
777              
778             =head1 RESOURCES
779              
780             =over
781              
782             =item * MetaCPAN: L
783              
784             =item * Website: L
785              
786             =item * Bugtracker: L
787              
788             =item * Git repository: L
789              
790             =back
791              
792             =head1 TODO
793              
794             Please see the project F file, or the bugtracker (RT), website or issues resource at Github.
795              
796             =head1 AUTHOR
797              
798             =over
799              
800             =item * Jonas B. Nielsen, (jonasbn) - C<< >>
801              
802             =back
803              
804             =head1 ACKNOWLEDGEMENTS
805              
806             =over
807              
808             =item * Mohammad S Anwar, POD corrections PR #6
809              
810             =back
811              
812             =head1 MOTIVATION
813              
814             Back in 2006 I was working on a project where I needed to do some presentation
815             and validation of Danish postal codes. I looked at L
816              
817             The implementation at the time of writing looked as follows:
818              
819             Denmark => "(?k:(?k:[1-9])(?k:[0-9])(?k:[0-9]{2}))",
820             # Postal codes of the form: 'DDDD', with the first
821             # digit representing the distribution region, the
822             # second digit the distribution district. Postal
823             # codes do not start with a zero. Postal codes
824             # starting with '39' are in Greenland.
825              
826             This pattern holds some issues:
827              
828             =over
829              
830             =item * Doing some fast math you can see that you will allow 9000 valid postal
831             codes where the number should be about 1254
832              
833             =item * 0 is actually allowed for a set of postal codes used by the postal service
834             in Denmark, in some situations these should perhaps be allowed as valid data
835              
836             =item * Greenland specified as starting with '39' is not a part of Denmark, but
837             should be under Greenland and the ISO code 'GL', see also:
838              
839             =over
840              
841             =item * L
842              
843             =back
844              
845             =back
846              
847             So I decided to write a regular expression, which would be better than the one
848             above, but I did not want to maintain it I wanted to write a piece of software,
849             which could generate the pattern for me based on a finite data set.
850              
851             =head1 COPYRIGHT
852              
853             Business-DK-Postalcode is (C) by Jonas B. Nielsen, (jonasbn) 2006-2019
854              
855             =head1 LICENSE
856              
857             Business-DK-Postalcode and related is released under the Artistic License 2.0
858              
859             =over
860              
861             =item * L
862              
863             =back
864              
865             =cut
866              
867             __DATA__