File Coverage

blib/lib/Geo/Region.pm
Criterion Covered Total %
statement 68 68 100.0
branch 10 10 100.0
condition 3 3 100.0
subroutine 16 16 100.0
pod 3 4 75.0
total 100 101 99.0


line stmt bran cond sub pod time code
1             package Geo::Region;
2              
3 2     2   178680 use v5.8.1;
  2         8  
  2         87  
4 2     2   8 use utf8;
  2         5  
  2         26  
5 2     2   60 use Carp qw( carp );
  2         7  
  2         139  
6 2     2   9 use Scalar::Util qw( looks_like_number weaken );
  2         3  
  2         82  
7 2     2   7 use List::Util qw( all any );
  2         3  
  2         183  
8 2     2   1062 use Moo;
  2         24885  
  2         12  
9              
10             our $VERSION = '0.06';
11              
12             my %children_of = (
13             # regions of subregions
14             '001' => [qw( 002 009 019 142 150 )],
15             '002' => [qw( 011 014 015 017 018 )],
16             '003' => [qw( 013 021 029 )],
17             '009' => [qw( 053 054 057 061 QO )],
18             '019' => [qw( 003 005 013 021 029 419 )],
19             '142' => [qw( 030 034 035 143 145 )],
20             '150' => [qw( 039 151 154 155 EU )],
21             '419' => [qw( 005 013 029 )],
22             # regions of countries and territories
23             '005' => [qw( AR BO BR CL CO EC FK GF GY PE PY SR UY VE )],
24             '011' => [qw( BF BJ CI CV GH GM GN GW LR ML MR NE NG SH SL SN TG )],
25             '013' => [qw( BZ CR GT HN MX NI PA SV )],
26             '014' => [qw( BI DJ ER ET KE KM MG MU MW MZ RE RW SC SO TZ UG YT ZM ZW )],
27             '015' => [qw( DZ EA EG EH IC LY MA SD SS TN )],
28             '017' => [qw( AO CD CF CG CM GA GQ ST TD ZR )],
29             '018' => [qw( BW LS NA SZ ZA )],
30             '021' => [qw( BM CA GL PM US )],
31             '029' => [qw( AG AI AN AW BB BL BQ BS CU CW DM DO GD GP HT JM KN KY LC MF MQ MS PR SX TC TT VC VG VI )],
32             '030' => [qw( CN HK JP KP KR MN MO TW )],
33             '034' => [qw( AF BD BT IN IR LK MV NP PK )],
34             '035' => [qw( BN BU ID KH LA MM MY PH SG TH TL TP VN )],
35             '039' => [qw( AD AL BA CS ES GI GR HR IT ME MK MT PT RS SI SM VA XK YU )],
36             '053' => [qw( AU NF NZ )],
37             '054' => [qw( FJ NC PG SB VU )],
38             '057' => [qw( FM GU KI MH MP NR PW )],
39             '061' => [qw( AS CK NU PF PN TK TO TV WF WS )],
40             '143' => [qw( KG KZ TJ TM UZ )],
41             '145' => [qw( AE AM AZ BH CY GE IL IQ JO KW LB NT OM PS QA SA SY TR YD YE )],
42             '151' => [qw( BG BY CZ HU MD PL RO RU SK SU UA )],
43             '154' => [qw( AX DK EE FI FO GB GG IE IM IS JE LT LV NO SE SJ )],
44             '155' => [qw( AT BE CH DD DE FR FX LI LU MC NL )],
45             'EU' => [qw( AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK )],
46             'QO' => [qw( AC AQ BV CC CP CX DG GS HM IO TA TF UM )],
47             );
48              
49             # codes excluded from country list due to being deprecated or grouping container
50             my %noncountries = map { $_ => 1 } qw(
51             AN BU CS DD FX NT SU TP YD YU ZR
52             EU QO
53             );
54              
55             # deprecated aliases
56             my %alias_of = (
57             QU => 'EU',
58             UK => 'GB',
59             );
60              
61             sub coerce_region {
62             my ($region) = @_;
63              
64             return sprintf('%03d', $region)
65             if looks_like_number $region;
66              
67             return $alias_of{uc $region}
68             || uc $region;
69             }
70              
71             sub coerce_regions {
72             my ($regions) = @_;
73              
74             return [
75             map { coerce_region($_) }
76             ref $regions eq 'ARRAY' ? @$regions : $regions
77             ];
78             }
79              
80 2     2   5025 use namespace::clean;
  2         20842  
  2         10  
81              
82             has _includes => (
83             is => 'ro',
84             coerce => sub { coerce_regions(shift) },
85             default => sub { [] },
86             init_arg => 'include',
87             );
88              
89             has _excludes => (
90             is => 'ro',
91             coerce => sub { coerce_regions(shift) },
92             default => sub { [] },
93             init_arg => 'exclude',
94             );
95              
96             has _children => (
97             is => 'lazy',
98             builder => sub {
99 11     11   401 my $self = shift;
100 11         14 my $build_children;
101              
102 50         92 $build_children = sub { map {
103 72     72   75 $_, exists $children_of{$_}
104 596 100       773 ? $build_children->(@{$children_of{$_}})
105             : ()
106 11         60 } @_ };
107              
108 29         42 my %excludes = map { $_ => 1 }
  11         46  
109 11         22 $build_children->(@{$self->_excludes});
110              
111 511         648 my %children = map { $_ => 1 }
  567         515  
112 11         33 grep { !exists $excludes{$_} }
113 11         22 $build_children->(@{$self->_includes});
114              
115 11         100 weaken $build_children;
116 11         66 return \%children;
117             },
118             );
119              
120             has _parents => (
121             is => 'lazy',
122             builder => sub {
123 7     7   377 my @regions = @{shift->_includes};
  7         32  
124 7         13 my ($build_parents, %count);
125              
126 28         27 $build_parents = sub { map {
127 35     35   100 my $region = $_;
128             $region, $build_parents->(grep {
129 28         125 any { $_ eq $region } @{$children_of{$_}}
  896         1308  
  9240         6453  
  896         1381  
130             } keys %children_of);
131 7         43 } @_ };
132              
133 16         28 my %parents = map { $_ => 1 }
  28         50  
134 7         19 grep { ++$count{$_} == @regions }
135             $build_parents->(@regions);
136              
137 7         49 weaken $build_parents;
138 7         43 return \%parents;
139             },
140             );
141              
142             has _countries => (
143             is => 'lazy',
144             builder => sub { [
145             sort
146 355 100       1212 grep { /^[A-Z]{2}$/ && !exists $noncountries{$_} }
  10         159  
147 10     10   452 keys %{shift->_children}
148             ] },
149             );
150              
151             sub BUILDARGS {
152 12     12 0 27305 my ($class, @args) = @_;
153              
154             # the `include` key is optional for the first argument
155 12 100 100     121 my %args = @args == 1 && ref $args[0] eq 'HASH' ? %{$args[0]} :
  1 100       4  
156             @args % 2 ? (include => @args) :
157             @args ;
158              
159 12 100       38 if (exists $args{region}) {
160 1         26 carp 'Argument "region" is deprecated; use "include" instead';
161 1         469 $args{include} = delete $args{region};
162             }
163              
164 12         277 return \%args;
165             }
166              
167             sub contains {
168 37     37 1 8406 my ($self, $region) = @_;
169 37         897 return exists $self->_children->{ coerce_region($region) };
170             }
171              
172             sub is_within {
173 17     17 1 81 my ($self, $region) = @_;
174 17         359 return exists $self->_parents->{ coerce_region($region) };
175             }
176              
177             sub countries {
178 10     10 1 491 my ($self) = @_;
179 10         29 return @{$self->_countries};
  10         216  
180             }
181              
182             1;
183              
184             __END__