maxmind-geoip2 0.1.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/Gemfile +1 -5
  4. data/README.dev.md +1 -1
  5. data/README.md +82 -8
  6. data/lib/maxmind/geoip2.rb +1 -0
  7. data/lib/maxmind/geoip2/client.rb +333 -0
  8. data/lib/maxmind/geoip2/errors.rb +37 -3
  9. data/lib/maxmind/geoip2/model/abstract.rb +19 -15
  10. data/lib/maxmind/geoip2/model/anonymous_ip.rb +62 -50
  11. data/lib/maxmind/geoip2/model/asn.rb +33 -29
  12. data/lib/maxmind/geoip2/model/city.rb +59 -55
  13. data/lib/maxmind/geoip2/model/connection_type.rb +27 -23
  14. data/lib/maxmind/geoip2/model/country.rb +64 -53
  15. data/lib/maxmind/geoip2/model/domain.rb +27 -23
  16. data/lib/maxmind/geoip2/model/enterprise.rb +13 -9
  17. data/lib/maxmind/geoip2/model/insights.rb +18 -0
  18. data/lib/maxmind/geoip2/model/isp.rb +45 -41
  19. data/lib/maxmind/geoip2/reader.rb +260 -233
  20. data/lib/maxmind/geoip2/record/abstract.rb +17 -13
  21. data/lib/maxmind/geoip2/record/city.rb +33 -29
  22. data/lib/maxmind/geoip2/record/continent.rb +32 -28
  23. data/lib/maxmind/geoip2/record/country.rb +47 -43
  24. data/lib/maxmind/geoip2/record/location.rb +64 -60
  25. data/lib/maxmind/geoip2/record/maxmind.rb +21 -0
  26. data/lib/maxmind/geoip2/record/place.rb +22 -18
  27. data/lib/maxmind/geoip2/record/postal.rb +26 -22
  28. data/lib/maxmind/geoip2/record/represented_country.rb +20 -16
  29. data/lib/maxmind/geoip2/record/subdivision.rb +42 -38
  30. data/lib/maxmind/geoip2/record/traits.rb +204 -191
  31. data/maxmind-geoip2.gemspec +11 -3
  32. data/test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
  33. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +1 -0
  34. data/test/data/source-data/GeoIP2-ISP-Test.json +3 -1
  35. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +87 -0
  36. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  37. data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
  38. data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
  39. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  40. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  41. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  42. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  43. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  44. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  45. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  46. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  47. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  48. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  49. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  50. data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  51. data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  52. data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
  53. data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
  54. data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  55. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  56. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  57. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  58. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  59. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  60. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  61. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  62. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  63. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  64. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  65. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  66. data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
  67. data/test/data/test-data/write-test-data.pl +68 -18
  68. data/test/test_client.rb +426 -0
  69. data/test/test_model_country.rb +16 -0
  70. data/test/test_reader.rb +59 -0
  71. metadata +113 -10
  72. data/Gemfile.lock +0 -38
@@ -1,7 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MaxMind::GeoIP2
4
- # An AddressNotFoundError means the IP address was not found in the database.
5
- class AddressNotFoundError < RuntimeError
3
+ module MaxMind
4
+ module GeoIP2
5
+ # An AddressNotFoundError means the IP address was not found in the
6
+ # database or the web service said the IP address was not found.
7
+ class AddressNotFoundError < RuntimeError
8
+ end
9
+
10
+ # An HTTPError means there was an unexpected HTTP status or response.
11
+ class HTTPError < RuntimeError
12
+ end
13
+
14
+ # An AddressInvalidError means the IP address was invalid.
15
+ class AddressInvalidError < RuntimeError
16
+ end
17
+
18
+ # An AddressReservedError means the IP address is reserved.
19
+ class AddressReservedError < RuntimeError
20
+ end
21
+
22
+ # An AuthenticationError means there was a problem authenticating to the
23
+ # web service.
24
+ class AuthenticationError < RuntimeError
25
+ end
26
+
27
+ # An InsufficientFundsError means the account is out of credits.
28
+ class InsufficientFundsError < RuntimeError
29
+ end
30
+
31
+ # A PermissionRequiredError means the account does not have permission to
32
+ # use the requested service.
33
+ class PermissionRequiredError < RuntimeError
34
+ end
35
+
36
+ # An InvalidRequestError means the web service returned an error and there
37
+ # is no more specific error class.
38
+ class InvalidRequestError < RuntimeError
39
+ end
6
40
  end
7
41
  end
@@ -2,26 +2,30 @@
2
2
 
3
3
  require 'ipaddr'
4
4
 
5
- module MaxMind::GeoIP2::Model
6
- # @!visibility private
7
- class Abstract
8
- def initialize(record)
9
- @record = record
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # @!visibility private
9
+ class Abstract
10
+ def initialize(record)
11
+ @record = record
10
12
 
11
- ip = IPAddr.new(record['ip_address']).mask(record['prefix_length'])
12
- record['network'] = format('%s/%d', ip.to_s, record['prefix_length'])
13
- end
13
+ ip = IPAddr.new(record['ip_address']).mask(record['prefix_length'])
14
+ record['network'] = format('%s/%d', ip.to_s, record['prefix_length'])
15
+ end
14
16
 
15
- protected
17
+ protected
16
18
 
17
- def get(key)
18
- if @record.nil? || !@record.key?(key)
19
- return false if key.start_with?('is_')
19
+ def get(key)
20
+ if @record.nil? || !@record.key?(key)
21
+ return false if key.start_with?('is_')
20
22
 
21
- return nil
22
- end
23
+ return nil
24
+ end
23
25
 
24
- @record[key]
26
+ @record[key]
27
+ end
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -2,62 +2,74 @@
2
2
 
3
3
  require 'maxmind/geoip2/model/abstract'
4
4
 
5
- module MaxMind::GeoIP2::Model
6
- # Model class for the Anonymous IP database.
7
- class AnonymousIP < Abstract
8
- # This is true if the IP address belongs to any sort of anonymous network.
9
- #
10
- # @return [Boolean]
11
- def anonymous?
12
- get('is_anonymous')
13
- end
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the Anonymous IP database.
9
+ class AnonymousIP < Abstract
10
+ # This is true if the IP address belongs to any sort of anonymous network.
11
+ #
12
+ # @return [Boolean]
13
+ def anonymous?
14
+ get('is_anonymous')
15
+ end
14
16
 
15
- # This is true if the IP address is registered to an anonymous VPN
16
- # provider. If a VPN provider does not register subnets under names
17
- # associated with them, we will likely only flag their IP ranges using the
18
- # is_hosting_provider attribute.
19
- #
20
- # @return [Boolean]
21
- def anonymous_vpn?
22
- get('is_anonymous_vpn')
23
- end
17
+ # This is true if the IP address is registered to an anonymous VPN
18
+ # provider. If a VPN provider does not register subnets under names
19
+ # associated with them, we will likely only flag their IP ranges using the
20
+ # hosting_provider? method.
21
+ #
22
+ # @return [Boolean]
23
+ def anonymous_vpn?
24
+ get('is_anonymous_vpn')
25
+ end
24
26
 
25
- # This is true if the IP address belongs to a hosting or VPN provider (see
26
- # description of the is_anonymous_vpn attribute).
27
- #
28
- # @return [Boolean]
29
- def hosting_provider?
30
- get('is_hosting_provider')
31
- end
27
+ # This is true if the IP address belongs to a hosting or VPN provider (see
28
+ # description of the anonymous_vpn? method).
29
+ #
30
+ # @return [Boolean]
31
+ def hosting_provider?
32
+ get('is_hosting_provider')
33
+ end
32
34
 
33
- # The IP address that the data in the model is for.
34
- #
35
- # @return [String]
36
- def ip_address
37
- get('ip_address')
38
- end
35
+ # The IP address that the data in the model is for.
36
+ #
37
+ # @return [String]
38
+ def ip_address
39
+ get('ip_address')
40
+ end
39
41
 
40
- # The network in CIDR notation associated with the record. In particular,
41
- # this is the largest network where all of the fields besides ip_address
42
- # have the same value.
43
- #
44
- # @return [String]
45
- def network
46
- get('network')
47
- end
42
+ # The network in CIDR notation associated with the record. In particular,
43
+ # this is the largest network where all of the fields besides ip_address
44
+ # have the same value.
45
+ #
46
+ # @return [String]
47
+ def network
48
+ get('network')
49
+ end
48
50
 
49
- # This is true if the IP address belongs to a public proxy.
50
- #
51
- # @return [Boolean]
52
- def public_proxy?
53
- get('is_public_proxy')
54
- end
51
+ # This is true if the IP address belongs to a public proxy.
52
+ #
53
+ # @return [Boolean]
54
+ def public_proxy?
55
+ get('is_public_proxy')
56
+ end
57
+
58
+ # This is true if the IP address is on a suspected anonymizing network
59
+ # and belongs to a residential ISP.
60
+ #
61
+ # @return [Boolean]
62
+ def residential_proxy?
63
+ get('is_residential_proxy')
64
+ end
55
65
 
56
- # This is true if the IP address is a Tor exit node.
57
- #
58
- # @return [Boolean]
59
- def tor_exit_node?
60
- get('is_tor_exit_node')
66
+ # This is true if the IP address is a Tor exit node.
67
+ #
68
+ # @return [Boolean]
69
+ def tor_exit_node?
70
+ get('is_tor_exit_node')
71
+ end
72
+ end
61
73
  end
62
74
  end
63
75
  end
@@ -2,38 +2,42 @@
2
2
 
3
3
  require 'maxmind/geoip2/model/abstract'
4
4
 
5
- module MaxMind::GeoIP2::Model
6
- # Model class for the GeoLite2 ASN database.
7
- class ASN < Abstract
8
- # The autonomous system number associated with the IP address.
9
- #
10
- # @return [Integer, nil]
11
- def autonomous_system_number
12
- get('autonomous_system_number')
13
- end
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoLite2 ASN database.
9
+ class ASN < Abstract
10
+ # The autonomous system number associated with the IP address.
11
+ #
12
+ # @return [Integer, nil]
13
+ def autonomous_system_number
14
+ get('autonomous_system_number')
15
+ end
14
16
 
15
- # The organization associated with the registered autonomous system number
16
- # for the IP address.
17
- #
18
- # @return [String, nil]
19
- def autonomous_system_organization
20
- get('autonomous_system_organization')
21
- end
17
+ # The organization associated with the registered autonomous system number
18
+ # for the IP address.
19
+ #
20
+ # @return [String, nil]
21
+ def autonomous_system_organization
22
+ get('autonomous_system_organization')
23
+ end
22
24
 
23
- # The IP address that the data in the model is for.
24
- #
25
- # @return [String]
26
- def ip_address
27
- get('ip_address')
28
- end
25
+ # The IP address that the data in the model is for.
26
+ #
27
+ # @return [String]
28
+ def ip_address
29
+ get('ip_address')
30
+ end
29
31
 
30
- # The network in CIDR notation associated with the record. In particular,
31
- # this is the largest network where all of the fields besides ip_address
32
- # have the same value.
33
- #
34
- # @return [String]
35
- def network
36
- get('network')
32
+ # The network in CIDR notation associated with the record. In particular,
33
+ # this is the largest network where all of the fields besides ip_address
34
+ # have the same value.
35
+ #
36
+ # @return [String]
37
+ def network
38
+ get('network')
39
+ end
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -6,69 +6,73 @@ require 'maxmind/geoip2/record/location'
6
6
  require 'maxmind/geoip2/record/postal'
7
7
  require 'maxmind/geoip2/record/subdivision'
8
8
 
9
- module MaxMind::GeoIP2::Model
10
- # Model class for the data returned by the GeoIP2 City web service and
11
- # database. It is also used for GeoLite2 City lookups.
12
- #
13
- # The only difference between the City and Insights model classes is which
14
- # fields in each record may be populated. See
15
- # https://dev.maxmind.com/geoip/geoip2/web-services for more details.
16
- #
17
- # See {MaxMind::GeoIP2::Model::Country} for inherited methods.
18
- class City < Country
19
- # City data for the IP address.
20
- #
21
- # @return [MaxMind::GeoIP2::Record::City]
22
- attr_reader :city
9
+ module MaxMind
10
+ module GeoIP2
11
+ module Model
12
+ # Model class for the data returned by the GeoIP2 City web service and
13
+ # database. It is also used for GeoLite2 City lookups.
14
+ #
15
+ # The only difference between the City and Insights model classes is which
16
+ # fields in each record may be populated. See
17
+ # https://dev.maxmind.com/geoip/geoip2/web-services for more details.
18
+ #
19
+ # See {MaxMind::GeoIP2::Model::Country} for inherited methods.
20
+ class City < Country
21
+ # City data for the IP address.
22
+ #
23
+ # @return [MaxMind::GeoIP2::Record::City]
24
+ attr_reader :city
23
25
 
24
- # Location data for the IP address.
25
- #
26
- # @return [MaxMind::GeoIP2::Record::Location]
27
- attr_reader :location
26
+ # Location data for the IP address.
27
+ #
28
+ # @return [MaxMind::GeoIP2::Record::Location]
29
+ attr_reader :location
28
30
 
29
- # Postal data for the IP address.
30
- #
31
- # @return [MaxMind::GeoIP2::Record::Postal]
32
- attr_reader :postal
31
+ # Postal data for the IP address.
32
+ #
33
+ # @return [MaxMind::GeoIP2::Record::Postal]
34
+ attr_reader :postal
33
35
 
34
- # The country subdivisions for the IP address.
35
- #
36
- # The number and type of subdivisions varies by country, but a subdivision
37
- # is typically a state, province, country, etc. Subdivisions are ordered
38
- # from most general (largest) to most specific (smallest).
39
- #
40
- # If the response did not contain any subdivisions, this attribute will be
41
- # an empty array.
42
- #
43
- # @return [Array<MaxMind::GeoIP2::Record::Subdivision>]
44
- attr_reader :subdivisions
36
+ # The country subdivisions for the IP address.
37
+ #
38
+ # The number and type of subdivisions varies by country, but a subdivision
39
+ # is typically a state, province, country, etc. Subdivisions are ordered
40
+ # from most general (largest) to most specific (smallest).
41
+ #
42
+ # If the response did not contain any subdivisions, this attribute will be
43
+ # an empty array.
44
+ #
45
+ # @return [Array<MaxMind::GeoIP2::Record::Subdivision>]
46
+ attr_reader :subdivisions
45
47
 
46
- # @!visibility private
47
- def initialize(record, locales)
48
- super(record, locales)
49
- @city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
50
- @location = MaxMind::GeoIP2::Record::Location.new(record['location'])
51
- @postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])
52
- @subdivisions = create_subdivisions(record['subdivisions'], locales)
53
- end
48
+ # @!visibility private
49
+ def initialize(record, locales)
50
+ super(record, locales)
51
+ @city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
52
+ @location = MaxMind::GeoIP2::Record::Location.new(record['location'])
53
+ @postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])
54
+ @subdivisions = create_subdivisions(record['subdivisions'], locales)
55
+ end
54
56
 
55
- # The most specific subdivision returned.
56
- #
57
- # If the response did not contain any subdivisions, this method returns
58
- # nil.
59
- #
60
- # @return [MaxMind::GeoIP2::Record::Subdivision, nil]
61
- def most_specific_subdivision
62
- @subdivisions.last
63
- end
57
+ # The most specific subdivision returned.
58
+ #
59
+ # If the response did not contain any subdivisions, this method returns
60
+ # nil.
61
+ #
62
+ # @return [MaxMind::GeoIP2::Record::Subdivision, nil]
63
+ def most_specific_subdivision
64
+ @subdivisions.last
65
+ end
64
66
 
65
- private
67
+ private
66
68
 
67
- def create_subdivisions(subdivisions, locales)
68
- return [] if subdivisions.nil?
69
+ def create_subdivisions(subdivisions, locales)
70
+ return [] if subdivisions.nil?
69
71
 
70
- subdivisions.map do |s|
71
- MaxMind::GeoIP2::Record::Subdivision.new(s, locales)
72
+ subdivisions.map do |s|
73
+ MaxMind::GeoIP2::Record::Subdivision.new(s, locales)
74
+ end
75
+ end
72
76
  end
73
77
  end
74
78
  end
@@ -2,31 +2,35 @@
2
2
 
3
3
  require 'maxmind/geoip2/model/abstract'
4
4
 
5
- module MaxMind::GeoIP2::Model
6
- # Model class for the GeoIP2 Connection Type database.
7
- class ConnectionType < Abstract
8
- # The connection type may take the following values: "Dialup", "Cable/DSL",
9
- # "Corporate", "Cellular". Additional values may be added in the future.
10
- #
11
- # @return [String, nil]
12
- def connection_type
13
- get('connection_type')
14
- end
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoIP2 Connection Type database.
9
+ class ConnectionType < Abstract
10
+ # The connection type may take the following values: "Dialup", "Cable/DSL",
11
+ # "Corporate", "Cellular". Additional values may be added in the future.
12
+ #
13
+ # @return [String, nil]
14
+ def connection_type
15
+ get('connection_type')
16
+ end
15
17
 
16
- # The IP address that the data in the model is for.
17
- #
18
- # @return [String]
19
- def ip_address
20
- get('ip_address')
21
- end
18
+ # The IP address that the data in the model is for.
19
+ #
20
+ # @return [String]
21
+ def ip_address
22
+ get('ip_address')
23
+ end
22
24
 
23
- # The network in CIDR notation associated with the record. In particular,
24
- # this is the largest network where all of the fields besides ip_address
25
- # have the same value.
26
- #
27
- # @return [String]
28
- def network
29
- get('network')
25
+ # The network in CIDR notation associated with the record. In particular,
26
+ # this is the largest network where all of the fields besides ip_address
27
+ # have the same value.
28
+ #
29
+ # @return [String]
30
+ def network
31
+ get('network')
32
+ end
33
+ end
30
34
  end
31
35
  end
32
36
  end