maxmind-geoip2 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +3 -3
  4. data/lib/maxmind/geoip2/client.rb +269 -267
  5. data/lib/maxmind/geoip2/errors.rb +0 -7
  6. data/lib/maxmind/geoip2/model/abstract.rb +19 -15
  7. data/lib/maxmind/geoip2/model/anonymous_ip.rb +54 -50
  8. data/lib/maxmind/geoip2/model/asn.rb +33 -29
  9. data/lib/maxmind/geoip2/model/city.rb +59 -55
  10. data/lib/maxmind/geoip2/model/connection_type.rb +27 -23
  11. data/lib/maxmind/geoip2/model/country.rb +57 -53
  12. data/lib/maxmind/geoip2/model/domain.rb +27 -23
  13. data/lib/maxmind/geoip2/model/enterprise.rb +13 -9
  14. data/lib/maxmind/geoip2/model/insights.rb +12 -8
  15. data/lib/maxmind/geoip2/model/isp.rb +45 -41
  16. data/lib/maxmind/geoip2/reader.rb +242 -240
  17. data/lib/maxmind/geoip2/record/abstract.rb +17 -13
  18. data/lib/maxmind/geoip2/record/city.rb +33 -29
  19. data/lib/maxmind/geoip2/record/continent.rb +32 -28
  20. data/lib/maxmind/geoip2/record/country.rb +47 -43
  21. data/lib/maxmind/geoip2/record/location.rb +64 -60
  22. data/lib/maxmind/geoip2/record/maxmind.rb +14 -10
  23. data/lib/maxmind/geoip2/record/place.rb +22 -18
  24. data/lib/maxmind/geoip2/record/postal.rb +26 -22
  25. data/lib/maxmind/geoip2/record/represented_country.rb +20 -16
  26. data/lib/maxmind/geoip2/record/subdivision.rb +42 -38
  27. data/lib/maxmind/geoip2/record/traits.rb +195 -191
  28. data/maxmind-geoip2.gemspec +1 -1
  29. data/test/data/LICENSE +4 -0
  30. data/test/data/MaxMind-DB-spec.md +570 -0
  31. data/test/data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  32. data/test/data/README.md +4 -0
  33. data/test/data/bad-data/README.md +7 -0
  34. data/test/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb +0 -0
  35. data/test/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb +0 -0
  36. data/test/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb +1 -0
  37. data/test/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb +0 -0
  38. data/test/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb +0 -0
  39. data/test/data/bad-data/maxminddb-golang/invalid-string-length.mmdb +1 -0
  40. data/test/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb +1 -0
  41. data/test/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb +0 -0
  42. data/test/data/perltidyrc +12 -0
  43. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +48 -0
  44. data/test/data/source-data/GeoIP2-City-Test.json +12852 -0
  45. data/test/data/source-data/GeoIP2-Connection-Type-Test.json +102 -0
  46. data/test/data/source-data/GeoIP2-Country-Test.json +15916 -0
  47. data/test/data/source-data/GeoIP2-DensityIncome-Test.json +14 -0
  48. data/test/data/source-data/GeoIP2-Domain-Test.json +452 -0
  49. data/test/data/source-data/GeoIP2-Enterprise-Test.json +687 -0
  50. data/test/data/source-data/GeoIP2-ISP-Test.json +12593 -0
  51. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +2061 -0
  52. data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +2132 -0
  53. data/test/data/source-data/GeoIP2-User-Count-Test.json +2837 -0
  54. data/test/data/source-data/GeoLite2-ASN-Test.json +37 -0
  55. data/test/data/source-data/README +15 -0
  56. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  57. data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
  58. data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
  59. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  60. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  61. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  62. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  63. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  64. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  65. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  66. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  67. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  68. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  69. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  70. data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  71. data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  72. data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
  73. data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
  74. data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  75. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  76. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  77. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  78. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  79. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  80. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  81. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  82. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  83. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  84. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  85. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  86. data/test/data/test-data/README.md +26 -0
  87. data/test/data/test-data/maps-with-pointers.raw +0 -0
  88. data/test/data/test-data/write-test-data.pl +641 -0
  89. data/test/data/tidyall.ini +5 -0
  90. metadata +64 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 107c7822a5a2de18a3339637547ca8fd8eef5163
4
- data.tar.gz: 8d730256e5f036178cf13cbd69ed5be2b86afc10
2
+ SHA256:
3
+ metadata.gz: 42699226c0ab8ec1152f4c56929c830916d7c226097082ae95959199d8796aa8
4
+ data.tar.gz: 97d399a1c58fb932171cfb4aa5c6b19cfb64af382771e2cdfc693e72bba2abd1
5
5
  SHA512:
6
- metadata.gz: 1c9dc8f9b3a115494fa9f20c603f772721568a3759b9e2619fde20751ba71a50109d911effeb7d702d286a2011e56d71049ff6a2b854b177f012bbf6bd904291
7
- data.tar.gz: a895b54353186ad7fdffec41d9fcf0efa03f093f2dd44a7bbe31e417e13e726a971bb4b88a8a7357c424b16d864061aa563aa150b87160a087d025e852be9a60
6
+ metadata.gz: cd7fac3bc1738b562586e23722eb62919a3d157a32619511ca2246b0b23c91eabcf9a3565e9f33ff080680bdb9e42a6cfaf984689de9231e8cc2b4edab5b5fa9
7
+ data.tar.gz: 05ce00df88e5de2ecfc7fceacc868fdf5d0b371091d7da5aae63089f9699c72195aaf70175fcdd8e16c3af4d251cd2cf8b78f06e462c2936ea66cc82a4ed622d
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2020-03-04)
4
+
5
+ * Modules are now always be defined. Previously we used a shorthand syntax
6
+ which meant including individual classes could leave module constants
7
+ undefined.
8
+
3
9
  ## 0.2.0 (2020-02-26)
4
10
 
5
11
  * Added support for the GeoIP2 Precision web services: Country, City, and
@@ -12,7 +12,7 @@ GEM
12
12
  ffi-compiler (1.0.1)
13
13
  ffi (>= 1.0.0)
14
14
  rake
15
- hashdiff (1.0.0)
15
+ hashdiff (1.0.1)
16
16
  http (4.3.0)
17
17
  addressable (~> 2.3)
18
18
  http-cookie (~> 1.0)
@@ -27,13 +27,13 @@ GEM
27
27
  maxmind-db (1.1.0)
28
28
  minitest (5.14.0)
29
29
  parallel (1.19.1)
30
- parser (2.7.0.2)
30
+ parser (2.7.0.4)
31
31
  ast (~> 2.4.0)
32
32
  public_suffix (4.0.3)
33
33
  rainbow (3.0.0)
34
34
  rake (13.0.1)
35
35
  rexml (3.2.4)
36
- rubocop (0.80.0)
36
+ rubocop (0.80.1)
37
37
  jaro_winkler (~> 1.5.1)
38
38
  parallel (~> 1.10)
39
39
  parser (>= 2.7.0.1)
@@ -7,307 +7,309 @@ require 'maxmind/geoip2/model/city'
7
7
  require 'maxmind/geoip2/model/country'
8
8
  require 'maxmind/geoip2/model/insights'
9
9
 
10
- module MaxMind::GeoIP2
11
- # This class provides a client API for all the
12
- # {https://dev.maxmind.com/geoip/geoip2/web-services/ GeoIP2 Precision web
13
- # services}. The services are Country, City, and Insights. Each service
14
- # returns a different set of data about an IP address, with Country returning
15
- # the least data and Insights the most.
16
- #
17
- # Each web service is represented by a different model class, and these model
18
- # classes in turn contain multiple record classes. The record classes have
19
- # attributes which contain data about the IP address.
20
- #
21
- # If the web service does not return a particular piece of data for an IP
22
- # address, the associated attribute is not populated.
23
- #
24
- # The web service may not return any information for an entire record, in
25
- # which case all of the attributes for that record class will be empty.
26
- #
27
- # == Usage
28
- #
29
- # The basic API for this class is the same for all of the web service end
30
- # points. First you create a web service client object with your MaxMind
31
- # account ID and license key, then you call the method corresponding to a
32
- # specific end point, passing it the IP address you want to look up.
33
- #
34
- # If the request succeeds, the method call will return a model class for the
35
- # service you called. This model in turn contains multiple record classes,
36
- # each of which represents part of the data returned by the web service.
37
- #
38
- # If the request fails, the client class throws an exception.
39
- #
40
- # == Example
41
- #
42
- # require 'maxmind/geoip2'
43
- #
44
- # client = MaxMind::GeoIP2::Client.new(
45
- # account_id: 42,
46
- # license_key: 'abcdef123456',
47
- # )
48
- #
49
- # # Replace 'city' with the method corresponding to the web service you
50
- # # are using, e.g., 'country', 'insights'.
51
- # record = client.city('128.101.101.101')
52
- #
53
- # puts record.country.iso_code
54
- class Client
55
- # rubocop:disable Metrics/ParameterLists
56
-
57
- # Create a Client that may be used to query a GeoIP2 Precision web service.
58
- #
59
- # Once created, the Client is safe to use for lookups from multiple
60
- # threads.
61
- #
62
- # @param account_id [Integer] your MaxMind account ID.
63
- #
64
- # @param license_key [String] your MaxMind license key.
10
+ module MaxMind
11
+ module GeoIP2
12
+ # This class provides a client API for all the
13
+ # {https://dev.maxmind.com/geoip/geoip2/web-services/ GeoIP2 Precision web
14
+ # services}. The services are Country, City, and Insights. Each service
15
+ # returns a different set of data about an IP address, with Country returning
16
+ # the least data and Insights the most.
65
17
  #
66
- # @param locales [Array<String>] a list of locale codes to use in the name
67
- # property from most preferred to least preferred.
18
+ # Each web service is represented by a different model class, and these model
19
+ # classes in turn contain multiple record classes. The record classes have
20
+ # attributes which contain data about the IP address.
68
21
  #
69
- # @param host [String] the host to use when querying the web service.
22
+ # If the web service does not return a particular piece of data for an IP
23
+ # address, the associated attribute is not populated.
70
24
  #
71
- # @param timeout [Integer] the number of seconds to wait for a request
72
- # before timing out. If 0, no timeout is set.
25
+ # The web service may not return any information for an entire record, in
26
+ # which case all of the attributes for that record class will be empty.
73
27
  #
74
- # @param proxy_address [String] proxy address to use, if any.
28
+ # == Usage
75
29
  #
76
- # @param proxy_port [Integer] proxy port to use, if any.
77
- #
78
- # @param proxy_username [String] proxy username to use, if any.
79
- #
80
- # @param proxy_password [String] proxy password to use, if any.
81
- def initialize(
82
- account_id:,
83
- license_key:,
84
- locales: ['en'],
85
- host: 'geoip.maxmind.com',
86
- timeout: 0,
87
- proxy_address: '',
88
- proxy_port: 0,
89
- proxy_username: '',
90
- proxy_password: ''
91
- )
92
- @account_id = account_id
93
- @license_key = license_key
94
- @locales = locales
95
- @host = host
96
- @timeout = timeout
97
- @proxy_address = proxy_address
98
- @proxy_port = proxy_port
99
- @proxy_username = proxy_username
100
- @proxy_password = proxy_password
101
- end
102
- # rubocop:enable Metrics/ParameterLists
103
-
104
- # This method calls the GeoIP2 Precision City web service.
30
+ # The basic API for this class is the same for all of the web service end
31
+ # points. First you create a web service client object with your MaxMind
32
+ # account ID and license key, then you call the method corresponding to a
33
+ # specific end point, passing it the IP address you want to look up.
105
34
  #
106
- # @param ip_address [String] IPv4 or IPv6 address as a string. If no
107
- # address is provided, the address that the web service is called from is
108
- # used.
35
+ # If the request succeeds, the method call will return a model class for the
36
+ # service you called. This model in turn contains multiple record classes,
37
+ # each of which represents part of the data returned by the web service.
109
38
  #
110
- # @raise [HTTP::Error] if there was an error performing the HTTP request,
111
- # such as an error connecting.
39
+ # If the request fails, the client class throws an exception.
112
40
  #
113
- # @raise [JSON::ParserError] if there was invalid JSON in the response.
41
+ # == Example
114
42
  #
115
- # @raise [HTTPError] if there was a problem with the HTTP response, such as
116
- # an unexpected HTTP status code.
43
+ # require 'maxmind/geoip2'
117
44
  #
118
- # @raise [AddressInvalidError] if the web service believes the IP address
119
- # to be invalid or missing.
45
+ # client = MaxMind::GeoIP2::Client.new(
46
+ # account_id: 42,
47
+ # license_key: 'abcdef123456',
48
+ # )
120
49
  #
121
- # @raise [AddressNotFoundError] if the IP address was not found.
50
+ # # Replace 'city' with the method corresponding to the web service you
51
+ # # are using, e.g., 'country', 'insights'.
52
+ # record = client.city('128.101.101.101')
122
53
  #
123
- # @raise [AddressReservedError] if the IP address is reserved.
124
- #
125
- # @raise [AuthenticationError] if there was a problem authenticating to the
126
- # web service, such as an invalid or missing license key.
127
- #
128
- # @raise [InsufficientFundsError] if your account is out of credit.
129
- #
130
- # @raise [PermissionRequiredError] if your account does not have permission
131
- # to use the web service.
132
- #
133
- # @raise [InvalidRequestError] if the web service responded with an error
134
- # and there is no more specific error to raise.
135
- #
136
- # @return [MaxMind::GeoIP2::Model::City]
137
- def city(ip_address = 'me')
138
- response_for('city', MaxMind::GeoIP2::Model::City, ip_address)
139
- end
54
+ # puts record.country.iso_code
55
+ class Client
56
+ # rubocop:disable Metrics/ParameterLists
140
57
 
141
- # This method calls the GeoIP2 Precision Country web service.
142
- #
143
- # @param ip_address [String] IPv4 or IPv6 address as a string. If no
144
- # address is provided, the address that the web service is called from is
145
- # used.
146
- #
147
- # @raise [HTTP::Error] if there was an error performing the HTTP request,
148
- # such as an error connecting.
149
- #
150
- # @raise [JSON::ParserError] if there was invalid JSON in the response.
151
- #
152
- # @raise [HTTPError] if there was a problem with the HTTP response, such as
153
- # an unexpected HTTP status code.
154
- #
155
- # @raise [AddressInvalidError] if the web service believes the IP address
156
- # to be invalid or missing.
157
- #
158
- # @raise [AddressNotFoundError] if the IP address was not found.
159
- #
160
- # @raise [AddressReservedError] if the IP address is reserved.
161
- #
162
- # @raise [AuthenticationError] if there was a problem authenticating to the
163
- # web service, such as an invalid or missing license key.
164
- #
165
- # @raise [InsufficientFundsError] if your account is out of credit.
166
- #
167
- # @raise [PermissionRequiredError] if your account does not have permission
168
- # to use the web service.
169
- #
170
- # @raise [InvalidRequestError] if the web service responded with an error
171
- # and there is no more specific error to raise.
172
- #
173
- # @return [MaxMind::GeoIP2::Model::Country]
174
- def country(ip_address = 'me')
175
- response_for('country', MaxMind::GeoIP2::Model::Country, ip_address)
176
- end
177
-
178
- # This method calls the GeoIP2 Precision Insights web service.
179
- #
180
- # @param ip_address [String] IPv4 or IPv6 address as a string. If no
181
- # address is provided, the address that the web service is called from is
182
- # used.
183
- #
184
- # @raise [HTTP::Error] if there was an error performing the HTTP request,
185
- # such as an error connecting.
186
- #
187
- # @raise [JSON::ParserError] if there was invalid JSON in the response.
188
- #
189
- # @raise [HTTPError] if there was a problem with the HTTP response, such as
190
- # an unexpected HTTP status code.
191
- #
192
- # @raise [AddressInvalidError] if the web service believes the IP address
193
- # to be invalid or missing.
194
- #
195
- # @raise [AddressNotFoundError] if the IP address was not found.
196
- #
197
- # @raise [AddressReservedError] if the IP address is reserved.
198
- #
199
- # @raise [AuthenticationError] if there was a problem authenticating to the
200
- # web service, such as an invalid or missing license key.
201
- #
202
- # @raise [InsufficientFundsError] if your account is out of credit.
203
- #
204
- # @raise [PermissionRequiredError] if your account does not have permission
205
- # to use the web service.
206
- #
207
- # @raise [InvalidRequestError] if the web service responded with an error
208
- # and there is no more specific error to raise.
209
- #
210
- # @return [MaxMind::GeoIP2::Model::Insights]
211
- def insights(ip_address = 'me')
212
- response_for('insights', MaxMind::GeoIP2::Model::Insights, ip_address)
213
- end
58
+ # Create a Client that may be used to query a GeoIP2 Precision web service.
59
+ #
60
+ # Once created, the Client is safe to use for lookups from multiple
61
+ # threads.
62
+ #
63
+ # @param account_id [Integer] your MaxMind account ID.
64
+ #
65
+ # @param license_key [String] your MaxMind license key.
66
+ #
67
+ # @param locales [Array<String>] a list of locale codes to use in the name
68
+ # property from most preferred to least preferred.
69
+ #
70
+ # @param host [String] the host to use when querying the web service.
71
+ #
72
+ # @param timeout [Integer] the number of seconds to wait for a request
73
+ # before timing out. If 0, no timeout is set.
74
+ #
75
+ # @param proxy_address [String] proxy address to use, if any.
76
+ #
77
+ # @param proxy_port [Integer] proxy port to use, if any.
78
+ #
79
+ # @param proxy_username [String] proxy username to use, if any.
80
+ #
81
+ # @param proxy_password [String] proxy password to use, if any.
82
+ def initialize(
83
+ account_id:,
84
+ license_key:,
85
+ locales: ['en'],
86
+ host: 'geoip.maxmind.com',
87
+ timeout: 0,
88
+ proxy_address: '',
89
+ proxy_port: 0,
90
+ proxy_username: '',
91
+ proxy_password: ''
92
+ )
93
+ @account_id = account_id
94
+ @license_key = license_key
95
+ @locales = locales
96
+ @host = host
97
+ @timeout = timeout
98
+ @proxy_address = proxy_address
99
+ @proxy_port = proxy_port
100
+ @proxy_username = proxy_username
101
+ @proxy_password = proxy_password
102
+ end
103
+ # rubocop:enable Metrics/ParameterLists
214
104
 
215
- private
105
+ # This method calls the GeoIP2 Precision City web service.
106
+ #
107
+ # @param ip_address [String] IPv4 or IPv6 address as a string. If no
108
+ # address is provided, the address that the web service is called from is
109
+ # used.
110
+ #
111
+ # @raise [HTTP::Error] if there was an error performing the HTTP request,
112
+ # such as an error connecting.
113
+ #
114
+ # @raise [JSON::ParserError] if there was invalid JSON in the response.
115
+ #
116
+ # @raise [HTTPError] if there was a problem with the HTTP response, such as
117
+ # an unexpected HTTP status code.
118
+ #
119
+ # @raise [AddressInvalidError] if the web service believes the IP address
120
+ # to be invalid or missing.
121
+ #
122
+ # @raise [AddressNotFoundError] if the IP address was not found.
123
+ #
124
+ # @raise [AddressReservedError] if the IP address is reserved.
125
+ #
126
+ # @raise [AuthenticationError] if there was a problem authenticating to the
127
+ # web service, such as an invalid or missing license key.
128
+ #
129
+ # @raise [InsufficientFundsError] if your account is out of credit.
130
+ #
131
+ # @raise [PermissionRequiredError] if your account does not have permission
132
+ # to use the web service.
133
+ #
134
+ # @raise [InvalidRequestError] if the web service responded with an error
135
+ # and there is no more specific error to raise.
136
+ #
137
+ # @return [MaxMind::GeoIP2::Model::City]
138
+ def city(ip_address = 'me')
139
+ response_for('city', MaxMind::GeoIP2::Model::City, ip_address)
140
+ end
216
141
 
217
- def response_for(endpoint, model_class, ip_address)
218
- record = get(endpoint, ip_address)
142
+ # This method calls the GeoIP2 Precision Country web service.
143
+ #
144
+ # @param ip_address [String] IPv4 or IPv6 address as a string. If no
145
+ # address is provided, the address that the web service is called from is
146
+ # used.
147
+ #
148
+ # @raise [HTTP::Error] if there was an error performing the HTTP request,
149
+ # such as an error connecting.
150
+ #
151
+ # @raise [JSON::ParserError] if there was invalid JSON in the response.
152
+ #
153
+ # @raise [HTTPError] if there was a problem with the HTTP response, such as
154
+ # an unexpected HTTP status code.
155
+ #
156
+ # @raise [AddressInvalidError] if the web service believes the IP address
157
+ # to be invalid or missing.
158
+ #
159
+ # @raise [AddressNotFoundError] if the IP address was not found.
160
+ #
161
+ # @raise [AddressReservedError] if the IP address is reserved.
162
+ #
163
+ # @raise [AuthenticationError] if there was a problem authenticating to the
164
+ # web service, such as an invalid or missing license key.
165
+ #
166
+ # @raise [InsufficientFundsError] if your account is out of credit.
167
+ #
168
+ # @raise [PermissionRequiredError] if your account does not have permission
169
+ # to use the web service.
170
+ #
171
+ # @raise [InvalidRequestError] if the web service responded with an error
172
+ # and there is no more specific error to raise.
173
+ #
174
+ # @return [MaxMind::GeoIP2::Model::Country]
175
+ def country(ip_address = 'me')
176
+ response_for('country', MaxMind::GeoIP2::Model::Country, ip_address)
177
+ end
219
178
 
220
- model_class.new(record, @locales)
221
- end
179
+ # This method calls the GeoIP2 Precision Insights web service.
180
+ #
181
+ # @param ip_address [String] IPv4 or IPv6 address as a string. If no
182
+ # address is provided, the address that the web service is called from is
183
+ # used.
184
+ #
185
+ # @raise [HTTP::Error] if there was an error performing the HTTP request,
186
+ # such as an error connecting.
187
+ #
188
+ # @raise [JSON::ParserError] if there was invalid JSON in the response.
189
+ #
190
+ # @raise [HTTPError] if there was a problem with the HTTP response, such as
191
+ # an unexpected HTTP status code.
192
+ #
193
+ # @raise [AddressInvalidError] if the web service believes the IP address
194
+ # to be invalid or missing.
195
+ #
196
+ # @raise [AddressNotFoundError] if the IP address was not found.
197
+ #
198
+ # @raise [AddressReservedError] if the IP address is reserved.
199
+ #
200
+ # @raise [AuthenticationError] if there was a problem authenticating to the
201
+ # web service, such as an invalid or missing license key.
202
+ #
203
+ # @raise [InsufficientFundsError] if your account is out of credit.
204
+ #
205
+ # @raise [PermissionRequiredError] if your account does not have permission
206
+ # to use the web service.
207
+ #
208
+ # @raise [InvalidRequestError] if the web service responded with an error
209
+ # and there is no more specific error to raise.
210
+ #
211
+ # @return [MaxMind::GeoIP2::Model::Insights]
212
+ def insights(ip_address = 'me')
213
+ response_for('insights', MaxMind::GeoIP2::Model::Insights, ip_address)
214
+ end
222
215
 
223
- # rubocop:disable Metrics/CyclomaticComplexity
224
- # rubocop:disable Metrics/PerceivedComplexity
225
- def get(endpoint, ip_address)
226
- url = 'https://' + @host + '/geoip/v2.1/' + endpoint + '/' + ip_address
216
+ private
227
217
 
228
- headers = HTTP.basic_auth(user: @account_id, pass: @license_key)
229
- .headers(
230
- accept: 'application/json',
231
- user_agent: 'MaxMind-GeoIP2-ruby',
232
- )
233
- timeout = @timeout > 0 ? headers.timeout(@timeout) : headers
218
+ def response_for(endpoint, model_class, ip_address)
219
+ record = get(endpoint, ip_address)
234
220
 
235
- proxy = timeout
236
- if @proxy_address != ''
237
- opts = {}
238
- opts[:proxy_port] = @proxy_port if @proxy_port != 0
239
- opts[:proxy_username] = @proxy_username if @proxy_username != ''
240
- opts[:proxy_password] = @proxy_password if @proxy_password != ''
241
- proxy = timeout.via(@proxy_address, opts)
221
+ model_class.new(record, @locales)
242
222
  end
243
223
 
244
- response = proxy.get(url)
224
+ # rubocop:disable Metrics/CyclomaticComplexity
225
+ # rubocop:disable Metrics/PerceivedComplexity
226
+ def get(endpoint, ip_address)
227
+ url = 'https://' + @host + '/geoip/v2.1/' + endpoint + '/' + ip_address
245
228
 
246
- body = response.to_s
247
- is_json = response.headers[:content_type]&.include?('json')
229
+ headers = HTTP.basic_auth(user: @account_id, pass: @license_key)
230
+ .headers(
231
+ accept: 'application/json',
232
+ user_agent: 'MaxMind-GeoIP2-ruby',
233
+ )
234
+ timeout = @timeout > 0 ? headers.timeout(@timeout) : headers
248
235
 
249
- if response.status.client_error?
250
- return handle_client_error(endpoint, response.code, body, is_json)
251
- end
236
+ proxy = timeout
237
+ if @proxy_address != ''
238
+ opts = {}
239
+ opts[:proxy_port] = @proxy_port if @proxy_port != 0
240
+ opts[:proxy_username] = @proxy_username if @proxy_username != ''
241
+ opts[:proxy_password] = @proxy_password if @proxy_password != ''
242
+ proxy = timeout.via(@proxy_address, opts)
243
+ end
252
244
 
253
- if response.status.server_error?
254
- raise HTTPError,
255
- "Received server error response (#{response.code}) for #{endpoint} with body #{body}"
256
- end
245
+ response = proxy.get(url)
257
246
 
258
- if response.code != 200
259
- raise HTTPError,
260
- "Received unexpected response (#{response.code}) for #{endpoint} with body #{body}"
261
- end
247
+ body = response.to_s
248
+ is_json = response.headers[:content_type]&.include?('json')
262
249
 
263
- handle_success(endpoint, body, is_json)
264
- end
265
- # rubocop:enable Metrics/CyclomaticComplexity
266
- # rubocop:enable Metrics/PerceivedComplexity
250
+ if response.status.client_error?
251
+ return handle_client_error(endpoint, response.code, body, is_json)
252
+ end
267
253
 
268
- # rubocop:disable Metrics/CyclomaticComplexity
269
- def handle_client_error(endpoint, status, body, is_json)
270
- if !is_json
271
- raise HTTPError,
272
- "Received client error response (#{status}) for #{endpoint} but it is not JSON: #{body}"
273
- end
254
+ if response.status.server_error?
255
+ raise HTTPError,
256
+ "Received server error response (#{response.code}) for #{endpoint} with body #{body}"
257
+ end
274
258
 
275
- error = JSON.parse(body)
259
+ if response.code != 200
260
+ raise HTTPError,
261
+ "Received unexpected response (#{response.code}) for #{endpoint} with body #{body}"
262
+ end
276
263
 
277
- if !error.key?('code') || !error.key?('error')
278
- raise HTTPError,
279
- "Received client error response (#{status}) that is JSON but does not specify code or error keys: #{body}"
264
+ handle_success(endpoint, body, is_json)
280
265
  end
266
+ # rubocop:enable Metrics/CyclomaticComplexity
267
+ # rubocop:enable Metrics/PerceivedComplexity
281
268
 
282
- case error['code']
283
- when 'IP_ADDRESS_INVALID', 'IP_ADDRESS_REQUIRED'
284
- raise AddressInvalidError, error['error']
285
- when 'IP_ADDRESS_NOT_FOUND'
286
- raise AddressNotFoundError, error['error']
287
- when 'IP_ADDRESS_RESERVED'
288
- raise AddressReservedError, error['error']
289
- when 'ACCOUNT_ID_REQUIRED',
290
- 'ACCOUNT_ID_UNKNOWN',
291
- 'AUTHORIZATION_INVALID',
292
- 'LICENSE_KEY_REQUIRED'
293
- raise AuthenticationError, error['error']
294
- when 'INSUFFICIENT_FUNDS'
295
- raise InsufficientFundsError, error['error']
296
- when 'PERMISSION_REQUIRED'
297
- raise PermissionRequiredError, error['error']
298
- else
299
- raise InvalidRequestError, error['error']
300
- end
301
- end
302
- # rubocop:enable Metrics/CyclomaticComplexity
269
+ # rubocop:disable Metrics/CyclomaticComplexity
270
+ def handle_client_error(endpoint, status, body, is_json)
271
+ if !is_json
272
+ raise HTTPError,
273
+ "Received client error response (#{status}) for #{endpoint} but it is not JSON: #{body}"
274
+ end
275
+
276
+ error = JSON.parse(body)
303
277
 
304
- def handle_success(endpoint, body, is_json)
305
- if !is_json
306
- raise HTTPError,
307
- "Received a success response for #{endpoint} but it is not JSON: #{body}"
278
+ if !error.key?('code') || !error.key?('error')
279
+ raise HTTPError,
280
+ "Received client error response (#{status}) that is JSON but does not specify code or error keys: #{body}"
281
+ end
282
+
283
+ case error['code']
284
+ when 'IP_ADDRESS_INVALID', 'IP_ADDRESS_REQUIRED'
285
+ raise AddressInvalidError, error['error']
286
+ when 'IP_ADDRESS_NOT_FOUND'
287
+ raise AddressNotFoundError, error['error']
288
+ when 'IP_ADDRESS_RESERVED'
289
+ raise AddressReservedError, error['error']
290
+ when 'ACCOUNT_ID_REQUIRED',
291
+ 'ACCOUNT_ID_UNKNOWN',
292
+ 'AUTHORIZATION_INVALID',
293
+ 'LICENSE_KEY_REQUIRED'
294
+ raise AuthenticationError, error['error']
295
+ when 'INSUFFICIENT_FUNDS'
296
+ raise InsufficientFundsError, error['error']
297
+ when 'PERMISSION_REQUIRED'
298
+ raise PermissionRequiredError, error['error']
299
+ else
300
+ raise InvalidRequestError, error['error']
301
+ end
308
302
  end
303
+ # rubocop:enable Metrics/CyclomaticComplexity
304
+
305
+ def handle_success(endpoint, body, is_json)
306
+ if !is_json
307
+ raise HTTPError,
308
+ "Received a success response for #{endpoint} but it is not JSON: #{body}"
309
+ end
309
310
 
310
- JSON.parse(body)
311
+ JSON.parse(body)
312
+ end
311
313
  end
312
314
  end
313
315
  end