File Coverage

blib/lib/GeoIP2/Database/Reader.pm
Criterion Covered Total %
statement 98 98 100.0
branch 12 12 100.0
condition 2 2 100.0
subroutine 28 28 100.0
pod 8 8 100.0
total 148 148 100.0


line stmt bran cond sub pod time code
1             package GeoIP2::Database::Reader;
2              
3 7     7   1031500 use strict;
  7         32  
  7         171  
4 7     7   31 use warnings;
  7         14  
  7         246  
5              
6             our $VERSION = '2.006002';
7              
8 7     7   3134 use Moo;
  7         60605  
  7         33  
9              
10 7     7   11670 use Data::Validate::IP 0.25 qw( is_private_ip );
  7         214609  
  7         550  
11 7     7   3041 use GeoIP2::Error::Generic;
  7         23  
  7         215  
12 7     7   2584 use GeoIP2::Error::IPAddressNotFound;
  7         21  
  7         218  
13 7     7   2630 use GeoIP2::Model::ASN;
  7         23  
  7         207  
14 7     7   2587 use GeoIP2::Model::AnonymousIP;
  7         21  
  7         201  
15 7     7   2408 use GeoIP2::Model::City;
  7         24  
  7         246  
16 7     7   3122 use GeoIP2::Model::ConnectionType;
  7         23  
  7         206  
17 7     7   2595 use GeoIP2::Model::Country;
  7         24  
  7         238  
18 7     7   3180 use GeoIP2::Model::Domain;
  7         22  
  7         208  
19 7     7   2936 use GeoIP2::Model::Enterprise;
  7         23  
  7         307  
20 7     7   3142 use GeoIP2::Model::Insights;
  7         24  
  7         266  
21 7     7   3126 use GeoIP2::Model::ISP;
  7         25  
  7         312  
22 7     7   52 use GeoIP2::Types qw( Str );
  7         15  
  7         351  
23 7     7   3138 use MaxMind::DB::Reader 1.000000;
  7         1158320  
  7         304  
24              
25 7     7   53 use namespace::clean -except => 'meta';
  7         15  
  7         39  
26              
27             with 'GeoIP2::Role::HasLocales';
28              
29             has file => (
30             is => 'ro',
31             isa => Str,
32             required => 1,
33             coerce => sub { "$_[0]" },
34             );
35              
36             has _reader => (
37             is => 'ro',
38             does => 'MaxMind::DB::Reader::Role::Reader',
39             lazy => 1,
40             builder => '_build_reader',
41             handles => ['metadata'],
42             );
43              
44             sub _build_reader {
45 10     10   257 my $self = shift;
46 10         108 return MaxMind::DB::Reader->new( file => $self->file );
47             }
48              
49             sub _model_for_address {
50 158     158   254 my $self = shift;
51 158         249 my $class = shift;
52 158         480 my %args = @_;
53 158         271 my $ip = $args{ip};
54              
55 158 100       361 unless ( defined $ip ) {
56 3         19 my ($method) = ( caller(1) )[3];
57 3         34 GeoIP2::Error::Generic->throw( message =>
58             "Required param (ip) was missing when calling $method on "
59             . __PACKAGE__ );
60             }
61              
62 155 100       3217 unless ( $self->metadata->database_type =~ $args{type_check} ) {
63 108         14712 ( my $method = ( caller(1) )[3] ) =~ s/.+:://;
64 108         1794 GeoIP2::Error::Generic->throw( message => 'The '
65             . ( ref $self )
66             . "->$method()"
67             . ' method cannot be called with a '
68             . $self->metadata->database_type
69             . ' database' );
70             }
71              
72 47 100       178760 if ( $ip eq 'me' ) {
73 3         24 my ($method) = ( caller(1) )[3];
74 3         35 GeoIP2::Error::Generic->throw(
75             message => "me is not a valid IP when calling $method on "
76             . __PACKAGE__ );
77             }
78              
79 44 100       804 if ( is_private_ip($ip) ) {
80 3         240 my ($method) = ( caller(1) )[3];
81 3         20 GeoIP2::Error::Generic->throw(
82             message => "The IP address you provided ($ip) is not a "
83             . "public IP address when calling $method on "
84             . __PACKAGE__ );
85             }
86              
87 41         5163 my $geoip_record = $self->_reader->record_for_address($ip);
88 38 100       600258 unless ($geoip_record) {
89 24         187 GeoIP2::Error::IPAddressNotFound->throw(
90             message => "No record found for IP address $ip",
91             ip_address => $ip,
92             );
93             }
94              
95 14         64 my $model_class = 'GeoIP2::Model::' . $class;
96              
97 14 100       64 if ( $args{is_flat} ) {
98 10         36 $geoip_record->{ip_address} = $ip;
99             }
100             else {
101 4   100     34 $geoip_record->{traits} ||= {};
102 4         15 $geoip_record->{traits}{ip_address} = $ip;
103             }
104              
105             return $model_class->new(
106 14         33 %{$geoip_record},
  14         328  
107             locales => $self->locales,
108             );
109             }
110              
111             sub asn {
112 1     1 1 127 my $self = shift;
113 1         9 return $self->_model_for_address(
114             'ASN',
115             type_check => qr/^GeoLite2-ASN$/,
116             is_flat => 1,
117             @_
118             );
119             }
120              
121             sub city {
122 25     25 1 71038 my $self = shift;
123 25         144 return $self->_model_for_address(
124             'City',
125             type_check =>
126             qr/^(?:GeoLite2|GeoIP2)-(?:Precision-|)?City(-[a-zA-Z\-]+)?$/,
127             @_
128             );
129             }
130              
131             sub country {
132 25     25 1 84884 my $self = shift;
133 25         131 return $self->_model_for_address(
134             'Country',
135             type_check => qr/^(?:GeoLite2|GeoIP2)-(?:Precision-)?Country$/,
136             @_
137             );
138             }
139              
140             sub connection_type {
141 19     19 1 52895 my $self = shift;
142 19         137 return $self->_model_for_address(
143             'ConnectionType',
144             type_check => qr/^GeoIP2-(?:Precision-)?Connection-Type$/,
145             is_flat => 1,
146             @_
147             );
148             }
149              
150             sub domain {
151 19     19 1 53727 my $self = shift;
152 19         128 return $self->_model_for_address(
153             'Domain',
154             type_check => qr/^GeoIP2-(?:Precision-)?Domain$/,
155             is_flat => 1,
156             @_
157             );
158             }
159              
160             sub enterprise {
161 26     26 1 73532 my $self = shift;
162 26         143 return $self->_model_for_address(
163             'Enterprise',
164             type_check => qr/^GeoIP2-(?:Precision-)?Enterprise$/,
165             @_
166             );
167             }
168              
169             sub isp {
170 19     19 1 52866 my $self = shift;
171 19         109 return $self->_model_for_address(
172             'ISP',
173             type_check => qr/^GeoIP2-(?:Precision-)?ISP$/,
174             is_flat => 1,
175             @_
176             );
177             }
178              
179             sub anonymous_ip {
180 24     24 1 62845 my $self = shift;
181 24         170 return $self->_model_for_address(
182             'AnonymousIP',
183             type_check => qr/^GeoIP2-(?:Precision-)?Anonymous-IP$/,
184             is_flat => 1,
185             @_,
186             );
187             }
188              
189             1;
190              
191             # ABSTRACT: Perl API for GeoIP2 databases
192              
193             __END__
194              
195             =pod
196              
197             =encoding UTF-8
198              
199             =head1 NAME
200              
201             GeoIP2::Database::Reader - Perl API for GeoIP2 databases
202              
203             =head1 VERSION
204              
205             version 2.006002
206              
207             =head1 SYNOPSIS
208              
209             use 5.008;
210              
211             use GeoIP2::Database::Reader;
212              
213             my $reader = GeoIP2::Database::Reader->new(
214             file => '/path/to/database', # e.g. /home/maxmind/db/GeoIP2-Country.mmdb
215             locales => [ 'en', 'de', ]
216             );
217              
218             my $city = $reader->city( ip => '24.24.24.24' );
219             my $country = $city->country();
220             print $country->iso_code(), "\n";
221              
222             =head1 DESCRIPTION
223              
224             This class provides a reader API for all GeoIP2 databases. Each method returns
225             a different model class.
226              
227             If the database does not return a particular piece of data for an IP address,
228             the associated attribute is not populated.
229              
230             =head1 USAGE
231              
232             The basic API for this class is the same for all database types. First you
233             create a database reader object with your C<file> and C<locale> params.
234             Then you call the method corresponding to your database type, passing it the
235             IP address you want to look up.
236              
237             If the request succeeds, the method call will return a model class for the
238             method point you called.
239              
240             If the database cannot be read, the reader class throws an exception.
241              
242             =head1 IP GEOLOCATION USAGE
243              
244             IP geolocation is inherently imprecise. Locations are often near the center of
245             the population. Any location provided by a GeoIP2 database should not be used
246             to identify a particular address or household.
247              
248             =head1 CONSTRUCTOR
249              
250             This class has a single constructor method:
251              
252             =head2 GeoIP2::Database::Reader->new()
253              
254             This method creates a new object. It accepts the following arguments:
255              
256             =over 4
257              
258             =item * file
259              
260             This is the path to the GeoIP2 database which you'd like to query. The path
261             should include the filename.
262              
263             =item * locales
264              
265             This is an array reference where each value is a string indicating a locale.
266             This argument will be passed on to record classes to use when their C<name()>
267             methods are called.
268              
269             The order of the locales is significant. When a record class has multiple
270             names (country, city, etc.), its C<name()> method will look at each element of
271             this array ref and return the first locale for which it has a name.
272              
273             Note that the only locale which is always present in the GeoIP2 data in "en".
274             If you do not include this locale, the C<name()> method may end up returning
275             C<undef> even when the record in question has an English name.
276              
277             Currently, the valid list of locale codes is:
278              
279             =over 8
280              
281             =item * de - German
282              
283             =item * en - English
284              
285             English names may still include accented characters if that is the accepted
286             spelling in English. In other words, English does not mean ASCII.
287              
288             =item * es - Spanish
289              
290             =item * fr - French
291              
292             =item * ja - Japanese
293              
294             =item * pt-BR - Brazilian Portuguese
295              
296             =item * ru - Russian
297              
298             =item * zh-CN - simplified Chinese
299              
300             =back
301              
302             Passing any other locale code will result in an error.
303              
304             The default value for this argument is C<['en']>.
305              
306             =back
307              
308             =head1 REQUEST METHODS
309              
310             All of the request methods accept a single argument:
311              
312             =over 4
313              
314             =item * ip
315              
316             This must be a valid IPv4 or IPv6 address. This is the address that you want to
317             look up using the GeoIP2 web service.
318              
319             Unlike the web service client class, you cannot pass the string "me" as your ip
320             address.
321              
322             =back
323              
324             =head2 $reader->asn()
325              
326             This method returns a L<GeoIP2::Model::ASN> object.
327              
328             =head2 $reader->connection_type()
329              
330             This method returns a L<GeoIP2::Model::ConnectionType> object.
331              
332             =head2 $reader->country()
333              
334             This method returns a L<GeoIP2::Model::Country> object.
335              
336             =head2 $reader->city()
337              
338             This method returns a L<GeoIP2::Model::City> object.
339              
340             =head2 $reader->domain()
341              
342             This method returns a L<GeoIP2::Model::Domain> object.
343              
344             =head2 $reader->isp()
345              
346             This method returns a L<GeoIP2::Model::ISP> object.
347              
348             =head2 $reader->enterprise()
349              
350             This method returns a L<GeoIP2::Model::Enterprise> object.
351              
352             =head2 $reader->anonymous_ip()
353              
354             This method returns a L<GeoIP2::Model::AnonymousIP> object.
355              
356             =head1 OTHER METHODS
357              
358             =head2 $reader->metadata()
359              
360             This method returns a L<MaxMind::DB::Metadata> object containing information
361             about the database.
362              
363             =head1 EXCEPTIONS
364              
365             In the case of a fatal error, the reader will throw a
366             L<GeoIP2::Error::Generic> or L<GeoIP2::Error::IPAddressNotFound> exception
367             object.
368              
369             This error class has an C<< $error->message() >> method and overload
370             stringification to show that message. This means that if you don't explicitly
371             catch errors they will ultimately be sent to C<STDERR> with some sort of
372             (hopefully) useful error message.
373              
374             =head1 WHAT DATA IS RETURNED?
375              
376             While many of the databases return the same basic records, the attributes which
377             can be populated vary between model classes. In addition, while a database may
378             offer a particular piece of data, MaxMind does not always have every piece of
379             data for any given IP address.
380              
381             Because of these factors, it is possible for any model class to return a record
382             where some or all of the attributes are unpopulated.
383              
384             See L<http://dev.maxmind.com/geoip/geoip2/web-services> for details on what
385             data each end point I<may> return.
386              
387             Every record class attribute has a corresponding predicate method so you can
388             check to see if the attribute is set.
389              
390             =head1 SUPPORT
391              
392             Bugs may be submitted through L<https://github.com/maxmind/GeoIP2-perl/issues>.
393              
394             =head1 AUTHORS
395              
396             =over 4
397              
398             =item *
399              
400             Dave Rolsky <drolsky@maxmind.com>
401              
402             =item *
403              
404             Greg Oschwald <goschwald@maxmind.com>
405              
406             =item *
407              
408             Mark Fowler <mfowler@maxmind.com>
409              
410             =item *
411              
412             Olaf Alders <oalders@maxmind.com>
413              
414             =back
415              
416             =head1 COPYRIGHT AND LICENSE
417              
418             This software is copyright (c) 2013 - 2019 by MaxMind, Inc.
419              
420             This is free software; you can redistribute it and/or modify it under
421             the same terms as the Perl 5 programming language system itself.
422              
423             =cut