maxmind-geoip2 1.4.0 → 1.5.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/maxmind/geoip2/record/traits.rb +9 -1
- data/lib/maxmind/geoip2/version.rb +1 -1
- metadata +9 -91
- data/CLAUDE.md +0 -390
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -126
- data/README.dev.md +0 -4
- data/Rakefile +0 -14
- data/maxmind-geoip2.gemspec +0 -41
- data/test/data/LICENSE-APACHE +0 -202
- data/test/data/LICENSE-MIT +0 -17
- data/test/data/MaxMind-DB-spec.md +0 -573
- data/test/data/README.md +0 -11
- data/test/data/bad-data/README.md +0 -7
- data/test/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb +0 -0
- data/test/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb +0 -0
- data/test/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb +0 -1
- data/test/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb +0 -0
- data/test/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb +0 -0
- data/test/data/bad-data/maxminddb-golang/invalid-string-length.mmdb +0 -1
- data/test/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb +0 -1
- data/test/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb +0 -0
- data/test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
- data/test/data/cmd/write-test-data/main.go +0 -68
- data/test/data/go.mod +0 -13
- data/test/data/go.sum +0 -16
- data/test/data/pkg/writer/decoder.go +0 -178
- data/test/data/pkg/writer/geoip2.go +0 -184
- data/test/data/pkg/writer/ip.go +0 -39
- data/test/data/pkg/writer/maxmind.go +0 -246
- data/test/data/pkg/writer/nestedstructures.go +0 -73
- data/test/data/pkg/writer/writer.go +0 -61
- data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +0 -175
- data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +0 -55
- data/test/data/source-data/GeoIP2-City-Test.json +0 -13272
- data/test/data/source-data/GeoIP2-Connection-Type-Test.json +0 -127
- data/test/data/source-data/GeoIP2-Country-Test.json +0 -15978
- data/test/data/source-data/GeoIP2-DensityIncome-Test.json +0 -14
- data/test/data/source-data/GeoIP2-Domain-Test.json +0 -457
- data/test/data/source-data/GeoIP2-Enterprise-Test.json +0 -1110
- data/test/data/source-data/GeoIP2-IP-Risk-Test.json +0 -31
- data/test/data/source-data/GeoIP2-ISP-Test.json +0 -12605
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json +0 -296
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +0 -3189
- data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +0 -2147
- data/test/data/source-data/GeoIP2-User-Count-Test.json +0 -2855
- data/test/data/source-data/GeoLite2-ASN-Test.json +0 -4120
- data/test/data/source-data/GeoLite2-City-Test.json +0 -12969
- data/test/data/source-data/GeoLite2-Country-Test.json +0 -11369
- data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
- data/test/data/test-data/README.md +0 -42
- data/test/data/test-data/maps-with-pointers.raw +0 -0
- data/test/data/tidyall.ini +0 -5
- data/test/test_client.rb +0 -467
- data/test/test_model_country.rb +0 -95
- data/test/test_model_names.rb +0 -50
- data/test/test_reader.rb +0 -589
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 101e87b30523d5d5ff9649bb1d379d0ea3d198a11ea4d989750c62bb15872cb2
|
|
4
|
+
data.tar.gz: 5c2580f874e9902dff611d5c408521f9486074db67d99ed68cbeb6d631ebe606
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 609a302c0c3f4d7087df0531ce26c643412a2c7c4c2d92366ebe8cf26fae8b5bd04b8b15bc6b6a0f5285fcf15e3b74dbd92cc3e63e697e308431c9660598904f
|
|
7
|
+
data.tar.gz: 91115ba0d2bf0868bec4644c58ac12c26f94e5ee1b55b63592e05fb7e1a3461251ab078fa709f684cc3166db3509737f68edc67a3f536674d2b235325cad61b4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.1 (2026-01-19)
|
|
4
|
+
|
|
5
|
+
* Re-release with a fix to the release process. This includes a bump of the
|
|
6
|
+
gem's version in `Gemfile.lock`.
|
|
7
|
+
|
|
8
|
+
## 1.5.0 (2026-01-19)
|
|
9
|
+
|
|
10
|
+
* Unnecessary files were removed from the published .gem. Pull request by
|
|
11
|
+
Orien Madgwick. GitHub #131.
|
|
12
|
+
* Updated `connection_pool` dependency to allow version 3+. Reported by
|
|
13
|
+
Igor Kasyanchuk. GitHub #140.
|
|
14
|
+
|
|
3
15
|
## 1.4.0 (2025-11-20)
|
|
4
16
|
|
|
5
17
|
* Ruby 3.2+ is now required. If you're using Ruby 3.0 or 3.1, please use
|
data/README.md
CHANGED
|
@@ -360,7 +360,7 @@ This library uses [Semantic Versioning](https://semver.org/).
|
|
|
360
360
|
|
|
361
361
|
## Copyright and License
|
|
362
362
|
|
|
363
|
-
This software is Copyright (c) 2020-
|
|
363
|
+
This software is Copyright (c) 2020-2026 by MaxMind, Inc.
|
|
364
364
|
|
|
365
365
|
This is free software, licensed under the [Apache License, Version
|
|
366
366
|
2.0](LICENSE-APACHE) or the [MIT License](LICENSE-MIT), at your option.
|
|
@@ -242,7 +242,15 @@ module MaxMind
|
|
|
242
242
|
# services is more static than the IP risk score provided in minFraud
|
|
243
243
|
# and is not responsive to traffic on your network. If you need realtime
|
|
244
244
|
# IP risk scoring based on behavioral signals on your own network, please
|
|
245
|
-
# use minFraud.
|
|
245
|
+
# use minFraud.
|
|
246
|
+
#
|
|
247
|
+
# We do not provide an IP risk snapshot for low-risk networks. If this
|
|
248
|
+
# field is not populated, we either do not have signals for the network
|
|
249
|
+
# or the signals we have show that the network is low-risk. If you would
|
|
250
|
+
# like to get signals for low-risk networks, please use the minFraud web
|
|
251
|
+
# services.
|
|
252
|
+
#
|
|
253
|
+
# This property is only available from Insights.
|
|
246
254
|
#
|
|
247
255
|
# @return [Float, nil]
|
|
248
256
|
def ip_risk_snapshot
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maxmind-geoip2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- William Storey
|
|
@@ -13,16 +13,22 @@ dependencies:
|
|
|
13
13
|
name: connection_pool
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '2.2'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '4.0'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
23
|
-
- - "
|
|
26
|
+
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
28
|
version: '2.2'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '4.0'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: http
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -177,14 +183,9 @@ extensions: []
|
|
|
177
183
|
extra_rdoc_files: []
|
|
178
184
|
files:
|
|
179
185
|
- CHANGELOG.md
|
|
180
|
-
- CLAUDE.md
|
|
181
|
-
- Gemfile
|
|
182
|
-
- Gemfile.lock
|
|
183
186
|
- LICENSE-APACHE
|
|
184
187
|
- LICENSE-MIT
|
|
185
|
-
- README.dev.md
|
|
186
188
|
- README.md
|
|
187
|
-
- Rakefile
|
|
188
189
|
- lib/maxmind/geoip2.rb
|
|
189
190
|
- lib/maxmind/geoip2/client.rb
|
|
190
191
|
- lib/maxmind/geoip2/errors.rb
|
|
@@ -213,89 +214,6 @@ files:
|
|
|
213
214
|
- lib/maxmind/geoip2/record/subdivision.rb
|
|
214
215
|
- lib/maxmind/geoip2/record/traits.rb
|
|
215
216
|
- lib/maxmind/geoip2/version.rb
|
|
216
|
-
- maxmind-geoip2.gemspec
|
|
217
|
-
- test/data/LICENSE-APACHE
|
|
218
|
-
- test/data/LICENSE-MIT
|
|
219
|
-
- test/data/MaxMind-DB-spec.md
|
|
220
|
-
- test/data/README.md
|
|
221
|
-
- test/data/bad-data/README.md
|
|
222
|
-
- test/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb
|
|
223
|
-
- test/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb
|
|
224
|
-
- test/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb
|
|
225
|
-
- test/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb
|
|
226
|
-
- test/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb
|
|
227
|
-
- test/data/bad-data/maxminddb-golang/invalid-string-length.mmdb
|
|
228
|
-
- test/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb
|
|
229
|
-
- test/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb
|
|
230
|
-
- test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb
|
|
231
|
-
- test/data/cmd/write-test-data/main.go
|
|
232
|
-
- test/data/go.mod
|
|
233
|
-
- test/data/go.sum
|
|
234
|
-
- test/data/pkg/writer/decoder.go
|
|
235
|
-
- test/data/pkg/writer/geoip2.go
|
|
236
|
-
- test/data/pkg/writer/ip.go
|
|
237
|
-
- test/data/pkg/writer/maxmind.go
|
|
238
|
-
- test/data/pkg/writer/nestedstructures.go
|
|
239
|
-
- test/data/pkg/writer/writer.go
|
|
240
|
-
- test/data/source-data/GeoIP-Anonymous-Plus-Test.json
|
|
241
|
-
- test/data/source-data/GeoIP2-Anonymous-IP-Test.json
|
|
242
|
-
- test/data/source-data/GeoIP2-City-Test.json
|
|
243
|
-
- test/data/source-data/GeoIP2-Connection-Type-Test.json
|
|
244
|
-
- test/data/source-data/GeoIP2-Country-Test.json
|
|
245
|
-
- test/data/source-data/GeoIP2-DensityIncome-Test.json
|
|
246
|
-
- test/data/source-data/GeoIP2-Domain-Test.json
|
|
247
|
-
- test/data/source-data/GeoIP2-Enterprise-Test.json
|
|
248
|
-
- test/data/source-data/GeoIP2-IP-Risk-Test.json
|
|
249
|
-
- test/data/source-data/GeoIP2-ISP-Test.json
|
|
250
|
-
- test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json
|
|
251
|
-
- test/data/source-data/GeoIP2-Precision-Enterprise-Test.json
|
|
252
|
-
- test/data/source-data/GeoIP2-Static-IP-Score-Test.json
|
|
253
|
-
- test/data/source-data/GeoIP2-User-Count-Test.json
|
|
254
|
-
- test/data/source-data/GeoLite2-ASN-Test.json
|
|
255
|
-
- test/data/source-data/GeoLite2-City-Test.json
|
|
256
|
-
- test/data/source-data/GeoLite2-Country-Test.json
|
|
257
|
-
- test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb
|
|
258
|
-
- test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb
|
|
259
|
-
- test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb
|
|
260
|
-
- test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb
|
|
261
|
-
- test/data/test-data/GeoIP2-City-Test.mmdb
|
|
262
|
-
- test/data/test-data/GeoIP2-Connection-Type-Test.mmdb
|
|
263
|
-
- test/data/test-data/GeoIP2-Country-Test.mmdb
|
|
264
|
-
- test/data/test-data/GeoIP2-DensityIncome-Test.mmdb
|
|
265
|
-
- test/data/test-data/GeoIP2-Domain-Test.mmdb
|
|
266
|
-
- test/data/test-data/GeoIP2-Enterprise-Test.mmdb
|
|
267
|
-
- test/data/test-data/GeoIP2-IP-Risk-Test.mmdb
|
|
268
|
-
- test/data/test-data/GeoIP2-ISP-Test.mmdb
|
|
269
|
-
- test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb
|
|
270
|
-
- test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb
|
|
271
|
-
- test/data/test-data/GeoIP2-User-Count-Test.mmdb
|
|
272
|
-
- test/data/test-data/GeoLite2-ASN-Test.mmdb
|
|
273
|
-
- test/data/test-data/GeoLite2-City-Test.mmdb
|
|
274
|
-
- test/data/test-data/GeoLite2-Country-Test.mmdb
|
|
275
|
-
- test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb
|
|
276
|
-
- test/data/test-data/MaxMind-DB-string-value-entries.mmdb
|
|
277
|
-
- test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb
|
|
278
|
-
- test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb
|
|
279
|
-
- test/data/test-data/MaxMind-DB-test-decoder.mmdb
|
|
280
|
-
- test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb
|
|
281
|
-
- test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb
|
|
282
|
-
- test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb
|
|
283
|
-
- test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb
|
|
284
|
-
- test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb
|
|
285
|
-
- test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb
|
|
286
|
-
- test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb
|
|
287
|
-
- test/data/test-data/MaxMind-DB-test-mixed-24.mmdb
|
|
288
|
-
- test/data/test-data/MaxMind-DB-test-mixed-28.mmdb
|
|
289
|
-
- test/data/test-data/MaxMind-DB-test-mixed-32.mmdb
|
|
290
|
-
- test/data/test-data/MaxMind-DB-test-nested.mmdb
|
|
291
|
-
- test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb
|
|
292
|
-
- test/data/test-data/README.md
|
|
293
|
-
- test/data/test-data/maps-with-pointers.raw
|
|
294
|
-
- test/data/tidyall.ini
|
|
295
|
-
- test/test_client.rb
|
|
296
|
-
- test/test_model_country.rb
|
|
297
|
-
- test/test_model_names.rb
|
|
298
|
-
- test/test_reader.rb
|
|
299
217
|
homepage: https://github.com/maxmind/GeoIP2-ruby
|
|
300
218
|
licenses:
|
|
301
219
|
- Apache-2.0
|
data/CLAUDE.md
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
**GeoIP2-ruby** is MaxMind's official Ruby client library for:
|
|
8
|
-
- **GeoIP2/GeoLite2 Web Services**: Country, City, and Insights endpoints
|
|
9
|
-
- **GeoIP2/GeoLite2 Databases**: Local MMDB file reading for various database types (City, Country, ASN, Anonymous IP, Anonymous Plus, ISP, etc.)
|
|
10
|
-
|
|
11
|
-
The library provides both web service clients and database readers that return strongly-typed model objects containing geographic, ISP, anonymizer, and other IP-related data.
|
|
12
|
-
|
|
13
|
-
**Key Technologies:**
|
|
14
|
-
- Ruby 3.2+ (uses frozen string literals and modern Ruby features)
|
|
15
|
-
- MaxMind DB Reader for binary database files
|
|
16
|
-
- HTTP gem for web service client functionality
|
|
17
|
-
- Minitest for testing
|
|
18
|
-
- RuboCop with multiple plugins for code quality
|
|
19
|
-
|
|
20
|
-
## Code Architecture
|
|
21
|
-
|
|
22
|
-
### Package Structure
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
lib/maxmind/geoip2/
|
|
26
|
-
├── model/ # Response models (City, Insights, AnonymousIP, etc.)
|
|
27
|
-
├── record/ # Data records (City, Location, Traits, etc.)
|
|
28
|
-
├── client.rb # HTTP client for MaxMind web services
|
|
29
|
-
├── reader.rb # Local MMDB file reader
|
|
30
|
-
├── errors.rb # Custom exceptions for error handling
|
|
31
|
-
└── version.rb # Version constant
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Key Design Patterns
|
|
35
|
-
|
|
36
|
-
#### 1. **Attr Reader Pattern for Immutable Data**
|
|
37
|
-
|
|
38
|
-
Models expose data through `attr_reader` attributes that are initialized in the constructor. Unlike PHP's readonly properties, Ruby uses instance variables with attr_reader:
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
class City < Country
|
|
42
|
-
attr_reader :city
|
|
43
|
-
attr_reader :location
|
|
44
|
-
attr_reader :postal
|
|
45
|
-
attr_reader :subdivisions
|
|
46
|
-
|
|
47
|
-
def initialize(record, locales)
|
|
48
|
-
super
|
|
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
|
|
54
|
-
end
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**Key Points:**
|
|
58
|
-
- Instance variables are set in the constructor
|
|
59
|
-
- Use `attr_reader` to expose them
|
|
60
|
-
- Models and records are initialized from hash data (from JSON/DB)
|
|
61
|
-
- Records are composed objects (City contains City record, Location record, etc.)
|
|
62
|
-
|
|
63
|
-
#### 2. **Inheritance Hierarchies**
|
|
64
|
-
|
|
65
|
-
Models follow clear inheritance patterns:
|
|
66
|
-
- `Country` → base model with country/continent data
|
|
67
|
-
- `City` extends `Country` → adds city, location, postal, subdivisions
|
|
68
|
-
- `Insights` extends `City` → adds additional web service fields (web service only)
|
|
69
|
-
- `Enterprise` extends `City` → adds enterprise-specific fields
|
|
70
|
-
|
|
71
|
-
Records have similar patterns:
|
|
72
|
-
- `Abstract` → base with `get` method for accessing hash data
|
|
73
|
-
- `Place` extends `Abstract` → adds names/locales handling
|
|
74
|
-
- Specific records (`City`, `Country`, etc.) extend `Place` or `Abstract`
|
|
75
|
-
|
|
76
|
-
#### 3. **Get Method Pattern for Data Access**
|
|
77
|
-
|
|
78
|
-
Both models and records use a protected `get` method to safely access hash data:
|
|
79
|
-
|
|
80
|
-
```ruby
|
|
81
|
-
def get(key)
|
|
82
|
-
if @record.nil? || !@record.key?(key)
|
|
83
|
-
return false if key.start_with?('is_')
|
|
84
|
-
return nil
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
@record[key]
|
|
88
|
-
end
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
- Returns `false` for missing boolean fields (starting with `is_`)
|
|
92
|
-
- Returns `nil` for missing optional fields
|
|
93
|
-
- Records store the raw hash in `@record` instance variable
|
|
94
|
-
|
|
95
|
-
Public methods expose data through the `get` method:
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
def anonymizer_confidence
|
|
99
|
-
get('anonymizer_confidence')
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def provider_name
|
|
103
|
-
get('provider_name')
|
|
104
|
-
end
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
#### 4. **Lazy Parsing for Special Types**
|
|
108
|
-
|
|
109
|
-
Some fields require parsing and are computed lazily:
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
def network_last_seen
|
|
113
|
-
return @network_last_seen if defined?(@network_last_seen)
|
|
114
|
-
|
|
115
|
-
date_string = get('network_last_seen')
|
|
116
|
-
|
|
117
|
-
if !date_string
|
|
118
|
-
return nil
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
@network_last_seen = Date.parse(date_string)
|
|
122
|
-
end
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
- Use `defined?(@variable)` to check if already parsed
|
|
126
|
-
- Parse only once and cache in instance variable
|
|
127
|
-
- Handle nil cases before parsing
|
|
128
|
-
|
|
129
|
-
#### 5. **Web Service Only vs Database Models**
|
|
130
|
-
|
|
131
|
-
Some models are only used by web services and do **not** need MaxMind DB support:
|
|
132
|
-
|
|
133
|
-
**Web Service Only Models**:
|
|
134
|
-
- Models that are exclusive to web service responses
|
|
135
|
-
- Simpler implementation, just inherit and define in model hierarchy
|
|
136
|
-
- Example: `Insights` (extends City but used only for web service)
|
|
137
|
-
|
|
138
|
-
**Database-Supported Models**:
|
|
139
|
-
- Models used by both web services and database files
|
|
140
|
-
- Reader has specific methods (e.g., `anonymous_ip`, `anonymous_plus`, `city`)
|
|
141
|
-
- Must handle MaxMind DB format data structures
|
|
142
|
-
- Example: `City`, `Country`, `AnonymousIP`, `AnonymousPlus`
|
|
143
|
-
|
|
144
|
-
## Testing Conventions
|
|
145
|
-
|
|
146
|
-
### Running Tests
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
# Install dependencies
|
|
150
|
-
bundle install
|
|
151
|
-
|
|
152
|
-
# Run all tests
|
|
153
|
-
bundle exec rake test
|
|
154
|
-
|
|
155
|
-
# Run tests and RuboCop
|
|
156
|
-
bundle exec rake # default task
|
|
157
|
-
|
|
158
|
-
# Run RuboCop only
|
|
159
|
-
bundle exec rake rubocop
|
|
160
|
-
|
|
161
|
-
# Run specific test file
|
|
162
|
-
ruby -Ilib:test test/test_reader.rb
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Test Structure
|
|
166
|
-
|
|
167
|
-
Tests are organized by functionality:
|
|
168
|
-
- `test/test_reader.rb` - Database reader tests
|
|
169
|
-
- `test/test_client.rb` - Web service client tests
|
|
170
|
-
- `test/test_model_*.rb` - Model-specific tests
|
|
171
|
-
- `test/data/` - Test fixtures and sample database files
|
|
172
|
-
|
|
173
|
-
### Test Patterns
|
|
174
|
-
|
|
175
|
-
Tests use Minitest with a constant for test data:
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
class CountryModelTest < Minitest::Test
|
|
179
|
-
RAW = {
|
|
180
|
-
'continent' => {
|
|
181
|
-
'code' => 'NA',
|
|
182
|
-
'geoname_id' => 42,
|
|
183
|
-
'names' => { 'en' => 'North America' },
|
|
184
|
-
},
|
|
185
|
-
'country' => {
|
|
186
|
-
'geoname_id' => 1,
|
|
187
|
-
'iso_code' => 'US',
|
|
188
|
-
'names' => { 'en' => 'United States of America' },
|
|
189
|
-
},
|
|
190
|
-
'traits' => {
|
|
191
|
-
'ip_address' => '1.2.3.4',
|
|
192
|
-
'prefix_length' => 24,
|
|
193
|
-
},
|
|
194
|
-
}.freeze
|
|
195
|
-
|
|
196
|
-
def test_values
|
|
197
|
-
model = MaxMind::GeoIP2::Model::Country.new(RAW, ['en'])
|
|
198
|
-
|
|
199
|
-
assert_equal(42, model.continent.geoname_id)
|
|
200
|
-
assert_equal('NA', model.continent.code)
|
|
201
|
-
assert_equal('United States of America', model.country.name)
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
When adding new fields to models:
|
|
207
|
-
1. Update the `RAW` constant to include the new field
|
|
208
|
-
2. Add assertions to verify the field is properly populated
|
|
209
|
-
3. Test both presence and absence of the field (nil handling)
|
|
210
|
-
4. Test with different values if applicable
|
|
211
|
-
|
|
212
|
-
## Working with This Codebase
|
|
213
|
-
|
|
214
|
-
### Adding New Fields to Existing Models
|
|
215
|
-
|
|
216
|
-
For database models (like AnonymousPlus):
|
|
217
|
-
|
|
218
|
-
1. **Add a public method** that calls `get`:
|
|
219
|
-
```ruby
|
|
220
|
-
# A description of the field.
|
|
221
|
-
#
|
|
222
|
-
# @return [Type, nil]
|
|
223
|
-
def field_name
|
|
224
|
-
get('field_name')
|
|
225
|
-
end
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
2. **For fields requiring parsing** (dates, complex types), use lazy loading:
|
|
229
|
-
```ruby
|
|
230
|
-
def network_last_seen
|
|
231
|
-
return @network_last_seen if defined?(@network_last_seen)
|
|
232
|
-
|
|
233
|
-
date_string = get('network_last_seen')
|
|
234
|
-
|
|
235
|
-
if !date_string
|
|
236
|
-
return nil
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
@network_last_seen = Date.parse(date_string)
|
|
240
|
-
end
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
For composed models (like City, Country):
|
|
244
|
-
|
|
245
|
-
1. **Add `attr_reader`** for the new record/field:
|
|
246
|
-
```ruby
|
|
247
|
-
attr_reader :new_field
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
2. **Initialize in constructor**:
|
|
251
|
-
```ruby
|
|
252
|
-
def initialize(record, locales)
|
|
253
|
-
super
|
|
254
|
-
@new_field = record['new_field']
|
|
255
|
-
end
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
3. **Provide comprehensive YARD documentation** (`@return` tags)
|
|
259
|
-
4. **Update tests** to include the new field in test data and assertions
|
|
260
|
-
5. **Update CHANGELOG.md** with the change
|
|
261
|
-
|
|
262
|
-
### Adding New Models
|
|
263
|
-
|
|
264
|
-
When creating a new model class:
|
|
265
|
-
|
|
266
|
-
1. **Determine if web service only or database-supported**
|
|
267
|
-
2. **Follow the pattern** from existing similar models
|
|
268
|
-
3. **Extend the appropriate base class** (e.g., `Country`, `City`, or standalone)
|
|
269
|
-
4. **Use `attr_reader`** for composed record objects
|
|
270
|
-
5. **Provide comprehensive YARD documentation** for all public methods
|
|
271
|
-
6. **Add corresponding tests** with full coverage
|
|
272
|
-
7. **If database-supported**, add a method to `Reader` class
|
|
273
|
-
|
|
274
|
-
### Deprecation Guidelines
|
|
275
|
-
|
|
276
|
-
When deprecating fields:
|
|
277
|
-
|
|
278
|
-
1. **Use `@deprecated` in YARD doc** with version and alternative:
|
|
279
|
-
```ruby
|
|
280
|
-
# This field is deprecated as of version 2.0.0.
|
|
281
|
-
# Use the anonymizer object from the Insights response instead.
|
|
282
|
-
#
|
|
283
|
-
# @return [Boolean]
|
|
284
|
-
# @deprecated since 2.0.0
|
|
285
|
-
def is_anonymous
|
|
286
|
-
get('is_anonymous')
|
|
287
|
-
end
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
2. **Keep deprecated fields functional** - don't break existing code
|
|
291
|
-
3. **Update CHANGELOG.md** with deprecation notices
|
|
292
|
-
4. **Document alternatives** in the deprecation message
|
|
293
|
-
|
|
294
|
-
### CHANGELOG.md Format
|
|
295
|
-
|
|
296
|
-
Always update `CHANGELOG.md` for user-facing changes.
|
|
297
|
-
|
|
298
|
-
**Important**: Do not add a date to changelog entries until release time.
|
|
299
|
-
|
|
300
|
-
- If there's an existing version entry without a date (e.g., `1.5.0`), add your changes there
|
|
301
|
-
- If creating a new version entry, don't include a date - it will be added at release time
|
|
302
|
-
- Use past tense for descriptions
|
|
303
|
-
|
|
304
|
-
```markdown
|
|
305
|
-
## 1.5.0
|
|
306
|
-
|
|
307
|
-
* A new `field_name` method has been added to `MaxMind::GeoIP2::Model::ModelName`.
|
|
308
|
-
This method provides information about...
|
|
309
|
-
* The `old_field` method in `MaxMind::GeoIP2::Model::ModelName` has been deprecated.
|
|
310
|
-
Please use `new_field` instead.
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Common Pitfalls and Solutions
|
|
314
|
-
|
|
315
|
-
### Problem: Incorrect Nil Handling
|
|
316
|
-
|
|
317
|
-
Using the wrong nil check can cause unexpected behavior.
|
|
318
|
-
|
|
319
|
-
**Solution**: Follow these patterns:
|
|
320
|
-
- Use `if !variable` or `if variable.nil?` to check for nil
|
|
321
|
-
- The `get` method returns `nil` for missing keys (except `is_*` keys which return `false`)
|
|
322
|
-
- Use `defined?(@variable)` to check if an instance variable has been set (for lazy loading)
|
|
323
|
-
|
|
324
|
-
### Problem: Missing YARD Documentation
|
|
325
|
-
|
|
326
|
-
New methods without documentation make the API harder to use.
|
|
327
|
-
|
|
328
|
-
**Solution**: Always add YARD documentation:
|
|
329
|
-
- Use `@return [Type, nil]` for the return type
|
|
330
|
-
- Add a description of what the method returns
|
|
331
|
-
- Use `@deprecated since X.Y.Z` for deprecated methods
|
|
332
|
-
- Include examples in model class documentation if helpful
|
|
333
|
-
|
|
334
|
-
### Problem: Test Failures After Adding Fields
|
|
335
|
-
|
|
336
|
-
Tests fail because fixtures don't include new fields.
|
|
337
|
-
|
|
338
|
-
**Solution**: Update all related tests:
|
|
339
|
-
1. Add field to test `RAW` constant or test data hash
|
|
340
|
-
2. Add assertions for the new field
|
|
341
|
-
3. Test nil case if field is optional
|
|
342
|
-
4. Test different data types if applicable
|
|
343
|
-
|
|
344
|
-
## Code Style Requirements
|
|
345
|
-
|
|
346
|
-
- **RuboCop enforced** with multiple plugins (minitest, performance, rake, thread_safety)
|
|
347
|
-
- **Frozen string literals** (`# frozen_string_literal: true`) in all files
|
|
348
|
-
- **Target Ruby 3.2+**
|
|
349
|
-
- **No metrics cops** - AbcSize, ClassLength, MethodLength disabled
|
|
350
|
-
- **Trailing commas allowed** in arrays, hashes, and arguments
|
|
351
|
-
- **Use `if !condition`** instead of `unless condition` (NegatedIf disabled)
|
|
352
|
-
|
|
353
|
-
Key RuboCop configurations:
|
|
354
|
-
- Line length not enforced
|
|
355
|
-
- Format string token checks disabled
|
|
356
|
-
- Numeric predicates allowed in any style
|
|
357
|
-
- Multiple assertions allowed in tests
|
|
358
|
-
|
|
359
|
-
## Development Workflow
|
|
360
|
-
|
|
361
|
-
### Setup
|
|
362
|
-
```bash
|
|
363
|
-
bundle install
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Before Committing
|
|
367
|
-
```bash
|
|
368
|
-
# Run tests and linting
|
|
369
|
-
bundle exec rake
|
|
370
|
-
|
|
371
|
-
# Or run separately
|
|
372
|
-
bundle exec rake test
|
|
373
|
-
bundle exec rake rubocop
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### Running Single Test
|
|
377
|
-
```bash
|
|
378
|
-
ruby -Ilib:test test/test_reader.rb
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Version Requirements
|
|
382
|
-
- **Ruby 3.2+** required
|
|
383
|
-
- Target compatibility should match current supported Ruby versions (3.2-3.4)
|
|
384
|
-
|
|
385
|
-
## Additional Resources
|
|
386
|
-
|
|
387
|
-
- [API Documentation](https://www.rubydoc.info/gems/maxmind-geoip2)
|
|
388
|
-
- [GeoIP2 Web Services Docs](https://dev.maxmind.com/geoip/docs/web-services)
|
|
389
|
-
- [MaxMind DB Format](https://maxmind.github.io/MaxMind-DB/)
|
|
390
|
-
- GitHub Issues: https://github.com/maxmind/GeoIP2-ruby/issues
|