File Coverage

blib/lib/Geography/States.pm
Criterion Covered Total %
statement 49 49 100.0
branch 24 26 92.3
condition 4 6 66.6
subroutine 8 8 100.0
pod 1 2 50.0
total 86 91 94.5


line stmt bran cond sub pod time code
1             package Geography::States;
2              
3 1     1   9340 use 5.006;
  1         5  
  1         47  
4              
5 1     1   6 use strict;
  1         2  
  1         40  
6 1     1   7 use warnings;
  1         16  
  1         45  
7 1     1   6 no warnings 'syntax';
  1         2  
  1         1729  
8              
9             our $VERSION = '2009040901';
10              
11             my %states;
12              
13             sub _c_length ($) {
14 254 100   254   742 lc $_ [0] eq "australia" ? 3 : 2
15             }
16              
17             sub _norm ($$) {
18 254     254   331 my ($str, $country) = @_;
19 254 100       536 if (_c_length ($country) >= length $str) {
20 131         193 $str = uc $str;
21             }
22             else {
23 123         300 $str = join " " => map {ucfirst lc} split /\s+/ => $str;
  182         662  
24 123 100       345 $str =~ s/\bOf\b/of/ if $country eq lc 'USA';
25 123 100       287 $str =~ s/\bD([eo])\b/d$1/ if $country eq lc 'Brazil';
26             }
27              
28 254         1151 $str;
29             }
30              
31             # This was originally wrapped in an INIT block, to avoid having it
32             # run when only compilation was wanted. However, that seems to fail
33             # when used in combination with mod_perl.
34             my $country;
35             while () {
36             chomp;
37             last if $_ eq '__END__';
38             s/#.*//;
39             next unless /\S/;
40             if (/^<(.*)>/) {
41             $country = lc $1;
42             $country =~ s/\s+/ /g;
43             next;
44             }
45             my ($code, $state) = split /\s+/ => $_, 2;
46             next unless defined $state;
47             my $fake = $code =~ s/\*$//;
48             my $info = [$code, $state, $fake];
49             $states {$country} -> {_norm ($code, $country)} = $info;
50             $states {$country} -> {_norm ($state, $country)} = $info;
51             }
52              
53             sub new {
54 7 50   7 0 868 die "Not enough arguments for Geography::States -> new ()\n" unless @_ > 1;
55              
56 7         12 my $proto = shift;
57 7   33     29 my $class = ref $proto || $proto;
58              
59 7         13 my $country = lc shift;
60 7         20 $country =~ s/\s+/ /g;
61              
62 7 50       25 die "No such country $country\n" unless $states {$country};
63              
64 7         10 my $strict = shift;
65              
66 7         8 my $self;
67 7         8 my ($cs, $info);
68 7         8 while (($cs, $info) = each %{$states {$country}}) {
  395         1121  
69 388 100       1252 next unless $cs eq $info -> [0];
70 195 100 100     510 next if $strict && $info -> [2];
71 184         399 my $inf = [@$info [0, 1]];
72 184         251 foreach my $i (0 .. 1) {
73 368 100       1335 $self -> {cs} -> {$info -> [$i]} = $inf unless
74             exists $self -> {cs} -> {$info -> [$i]};
75             }
76             }
77 7         16 $self -> {country} = $country;
78              
79 7         26 bless $self => $class;
80             }
81              
82              
83             sub state {
84 14     14 1 2209 my $self = shift;
85 14 100       54 unless (@_) {
86 2         4 my %h;
87 2         2 return grep {!$h {$_} ++} values %{$self -> {cs}};
  200         970  
  2         17  
88             }
89 12         30 my $query = _norm shift, $self -> {country};
90 12 100       40 my $answer = $self -> {cs} -> {$query} or return;
91 10 100       22 return @$answer if wantarray;
92 9 100       33 $answer -> [$answer -> [0] eq $query ? 1 : 0];
93             }
94              
95              
96             1;
97            
98             =pod
99              
100             =head1 NAME
101              
102             Geography::States - Map states and provinces to their codes, and vica versa.
103              
104             =head1 SYNOPSIS
105              
106             use Geography::States;
107              
108             my $obj = Geography::States -> new (COUNTRY [, STRICT]);
109              
110              
111             =head1 EXAMPLES
112              
113             my $canada = Geography::States -> new ('Canada');
114              
115             my $name = $canada -> state ('NF'); # Newfoundland.
116             my $code = $canada -> state ('Ontario'); # ON.
117             my ($code, $name) = $canada -> state ('BC'); # BC, British Columbia.
118             my @all_states = $canada -> state; # List code/name pairs.
119              
120              
121             =head1 DESCRIPTION
122              
123             This module lets you map states and provinces to their codes, and codes
124             to names of provinces and states.
125              
126             The C new ()> call takes 1 or 2 arguments. The
127             first, required, argument is the country we are interested in. Current
128             supported countries are I, I, I, I,
129             and I. If a second non-false argument is given, we use I
130             mode>. In non-strict mode, we will map territories and alternative codes
131             as well, while we do not do that in strict mode. For example, if the
132             country is B, in non-strict mode, we will map B to B,
133             while in strict mode, neither B and B will be found.
134              
135             =head2 The state() method
136              
137             All queries are done by calling the C method in the object. This method
138             takes an optional argument. If an argument is given, then in scalar context,
139             it will return the name of the state if a code of a state is given, and the
140             code of a state, if the argument of the method is a name of a state. In list
141             context, both the code and the state will be returned.
142              
143             If no argument is given, then the C method in list context will return
144             a list of all code/name pairs for that country. In scalar context, it will
145             return the number of code/name pairs. Each code/name pair is a 2 element
146             anonymous array.
147              
148             Arguments can be given in a case insensitive way; if a name consists of
149             multiple parts, the number of spaces does not matter, as long as there is
150             some whitespace. (That is "NewYork" is wrong, but S<"new YORK"> is fine.)
151              
152             =head1 ODDITIES AND OPEN QUESTIONS
153              
154             I found conflicting abbreviations for the US I,
155             listed as I and I. I picked I from the USPS site.
156              
157             One site listed I as having code I. It is not listed by
158             the USPS site, and because it conflicts with I, it is not put in
159             this listing.
160              
161             The USPS also has so-called I, with non-unique codes.
162             Those are not listed here.
163              
164             Canada's I has two codes, the older I and the modern I. Both
165             I and I will map to I, but I will only map to I.
166             With strict mode, I will not be listed. Similary, Newfoundland has an
167             old code I, and a new code I (the province is now called
168             I).
169              
170             =head1 DEVELOPMENT
171            
172             The current sources of this module are found on github,
173             L<< git://github.com/Abigail/geography--states.git >>.
174              
175             =head1 AUTHOR
176              
177             Abigail L<< mailto:geography-states@abigail.be >>.
178              
179             =head1 COPYRIGHT and LICENSE
180              
181             Copyright (C) 1999 - 2001, 2009 by Abigail.
182              
183             Permission is hereby granted, free of charge, to any person obtaining a
184             copy of this software and associated documentation files (the "Software"),
185             to deal in the Software without restriction, including without limitation
186             the rights to use, copy, modify, merge, publish, distribute, sublicense,
187             and/or sell copies of the Software, and to permit persons to whom the
188             Software is furnished to do so, subject to the following conditions:
189              
190             The above copyright notice and this permission notice shall be included
191             in all copies or substantial portions of the Software.
192            
193             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
194             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
195             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
196             THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
197             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
198             OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
199             THE SOFTWARE.
200              
201             =cut
202              
203             __DATA__