maxmind-geoip2 1.2.0 → 1.4.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/CLAUDE.md +390 -0
  4. data/Gemfile.lock +126 -0
  5. data/README.dev.md +1 -1
  6. data/README.md +17 -2
  7. data/Rakefile +2 -2
  8. data/lib/maxmind/geoip2/model/anonymous_plus.rb +46 -0
  9. data/lib/maxmind/geoip2/model/city.rb +1 -1
  10. data/lib/maxmind/geoip2/model/insights.rb +14 -0
  11. data/lib/maxmind/geoip2/reader.rb +26 -1
  12. data/lib/maxmind/geoip2/record/anonymizer.rb +105 -0
  13. data/lib/maxmind/geoip2/record/location.rb +3 -3
  14. data/lib/maxmind/geoip2/record/traits.rb +38 -1
  15. data/lib/maxmind/geoip2/version.rb +1 -1
  16. data/maxmind-geoip2.gemspec +8 -5
  17. data/test/data/LICENSE-APACHE +202 -0
  18. data/test/data/LICENSE-MIT +17 -0
  19. data/test/data/MaxMind-DB-spec.md +1 -2
  20. data/test/data/README.md +8 -1
  21. data/test/data/go.mod +1 -1
  22. data/test/data/pkg/writer/geoip2.go +7 -5
  23. data/test/data/pkg/writer/maxmind.go +2 -1
  24. data/test/data/pkg/writer/writer.go +4 -1
  25. data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +175 -0
  26. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +6 -0
  27. data/test/data/source-data/GeoIP2-City-Test.json +73 -7
  28. data/test/data/source-data/GeoIP2-Country-Test.json +3 -28
  29. data/test/data/source-data/GeoIP2-Enterprise-Test.json +27 -8
  30. data/test/data/source-data/GeoIP2-IP-Risk-Test.json +31 -0
  31. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +585 -11
  32. data/test/data/source-data/GeoLite2-City-Test.json +0 -3
  33. data/test/data/source-data/GeoLite2-Country-Test.json +0 -3
  34. data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
  35. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  36. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  37. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  38. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  39. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  40. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  41. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  42. data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
  43. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  44. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  45. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  46. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  47. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  48. data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
  49. data/test/data/test-data/GeoLite2-Country-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-decoder.mmdb +0 -0
  53. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  54. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  55. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  56. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  57. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  58. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  59. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  60. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  61. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  62. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  63. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  64. data/test/test_client.rb +31 -6
  65. data/test/test_model_country.rb +13 -14
  66. data/test/test_model_names.rb +3 -0
  67. data/test/test_reader.rb +60 -23
  68. metadata +58 -11
  69. data/test/data/LICENSE +0 -4
  70. data/test/data/perltidyrc +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c1d04d352a8344e1e8d9c232ab7713bc0b74ddaa1e4a82aec61dbbf37bfd925
4
- data.tar.gz: 20bcee370b35a4e098b48b36146eb7da9674f6c271ba94744a435addbd9c5e05
3
+ metadata.gz: f26229b50b6ba4760df9389e76c5e705dc2b5b53e7fc50eb6be25d2af74f6985
4
+ data.tar.gz: bfcb0fef72edaf05cdfcb1cc55be5ce2d12afbc650912e062ae263649a279736
5
5
  SHA512:
6
- metadata.gz: 0331f1fe319379d035bd4f8675f775bbe31f0d3747d2f9a65754523cb6718160237d5a232d54cad7cd00cdcfe5243ff0f8a39276d749948116fc974221eb3d8a
7
- data.tar.gz: 1d3e2f6c9a5847671b4b86004c7407bacf25cb271674d7dd49c5217b15d20a307ba416ec19a5f04d2050925ee689a97f01ca899f2edcb611bb44c5ee1a13ba35
6
+ metadata.gz: 4d3de90ee7df88d6acbef1af76202af2130e9670ad726bea5f9753581bd5e3ae962f859720458f833731f0822cc08345171ac90cde7b446c784ca7c6ca21215c
7
+ data.tar.gz: 34b7484739814ecd6de33435310c8cb29840534c4beed8d063c2ed00f2e0eee46338cec95bef1be5acb2ac2947be937842a2edc1f646b46460d70fa656213f6d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0 (2025-11-20)
4
+
5
+ * Ruby 3.2+ is now required. If you're using Ruby 3.0 or 3.1, please use
6
+ version 1.3.0 of this gem.
7
+ * A new `anonymizer` object has been added to the `MaxMind::GeoIP2::Model::Insights`
8
+ model. This object indicates whether the IP address is part of an anonymizing
9
+ network, including VPN confidence scoring, provider name detection, and network
10
+ last seen date. This is only available from the GeoIP2 Insights web service.
11
+ * A new `ip_risk_snapshot` method has been added to `MaxMind::GeoIP2::Record::Traits`.
12
+ This field contains the risk associated with the IP address, ranging from 0.01 to
13
+ 99 (a higher score indicates a higher risk). This is only available from the GeoIP2
14
+ Insights web service.
15
+ * The `anonymous?`, `anonymous_vpn?`, `hosting_provider?`, `public_proxy?`,
16
+ `residential_proxy?`, and `tor_exit_node?` methods in
17
+ `MaxMind::GeoIP2::Record::Traits` have been deprecated. Please use the
18
+ corresponding methods in the `anonymizer` object from the GeoIP2 Insights
19
+ response instead.
20
+
21
+
22
+ ## 1.3.0 (2025-05-06)
23
+
24
+ * Support for the GeoIP Anonymous Plus database has been added. To do a
25
+ lookup in this database, use the `anonymous_plus` method on
26
+ `MaxMind::GeoIP2::Reader`.
27
+ * Ruby 3.0+ is now required. If you're using Ruby 2.5, 2.6, or 2.7, please
28
+ use version 1.2.0 of this gem.
29
+ * Deprecated `metro_code` on `MaxMind::GeoIP2::Record::Location`. The code
30
+ values are no longer being maintained.
31
+
3
32
  ## 1.2.0 (2023-12-04)
4
33
 
5
34
  * `MaxMind::GeoIP2::Client` now validates the IP address before making a
data/CLAUDE.md ADDED
@@ -0,0 +1,390 @@
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
data/Gemfile.lock ADDED
@@ -0,0 +1,126 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ maxmind-geoip2 (1.4.0)
5
+ connection_pool (~> 2.2)
6
+ http (>= 4.3, < 6.0)
7
+ maxmind-db (~> 1.4)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.8.7)
13
+ public_suffix (>= 2.0.2, < 7.0)
14
+ ast (2.4.3)
15
+ bigdecimal (3.3.1)
16
+ connection_pool (2.5.4)
17
+ crack (1.0.1)
18
+ bigdecimal
19
+ rexml
20
+ domain_name (0.6.20240107)
21
+ ffi (1.17.2)
22
+ ffi (1.17.2-aarch64-linux-gnu)
23
+ ffi (1.17.2-aarch64-linux-musl)
24
+ ffi (1.17.2-arm-linux-gnu)
25
+ ffi (1.17.2-arm-linux-musl)
26
+ ffi (1.17.2-arm64-darwin)
27
+ ffi (1.17.2-x86-linux-gnu)
28
+ ffi (1.17.2-x86-linux-musl)
29
+ ffi (1.17.2-x86_64-darwin)
30
+ ffi (1.17.2-x86_64-linux-gnu)
31
+ ffi (1.17.2-x86_64-linux-musl)
32
+ ffi-compiler (1.3.2)
33
+ ffi (>= 1.15.5)
34
+ rake
35
+ hashdiff (1.2.1)
36
+ http (5.3.1)
37
+ addressable (~> 2.8)
38
+ http-cookie (~> 1.0)
39
+ http-form_data (~> 2.2)
40
+ llhttp-ffi (~> 0.5.0)
41
+ http-cookie (1.1.0)
42
+ domain_name (~> 0.5)
43
+ http-form_data (2.3.0)
44
+ json (2.16.0)
45
+ language_server-protocol (3.17.0.5)
46
+ lint_roller (1.1.0)
47
+ llhttp-ffi (0.5.1)
48
+ ffi-compiler (~> 1.0)
49
+ rake (~> 13.0)
50
+ maxmind-db (1.4.0)
51
+ minitest (5.26.2)
52
+ parallel (1.27.0)
53
+ parser (3.3.10.0)
54
+ ast (~> 2.4.1)
55
+ racc
56
+ prism (1.6.0)
57
+ public_suffix (6.0.2)
58
+ racc (1.8.1)
59
+ rainbow (3.1.1)
60
+ rake (13.3.1)
61
+ regexp_parser (2.11.3)
62
+ rexml (3.4.4)
63
+ rubocop (1.81.7)
64
+ json (~> 2.3)
65
+ language_server-protocol (~> 3.17.0.2)
66
+ lint_roller (~> 1.1.0)
67
+ parallel (~> 1.10)
68
+ parser (>= 3.3.0.2)
69
+ rainbow (>= 2.2.2, < 4.0)
70
+ regexp_parser (>= 2.9.3, < 3.0)
71
+ rubocop-ast (>= 1.47.1, < 2.0)
72
+ ruby-progressbar (~> 1.7)
73
+ unicode-display_width (>= 2.4.0, < 4.0)
74
+ rubocop-ast (1.48.0)
75
+ parser (>= 3.3.7.2)
76
+ prism (~> 1.4)
77
+ rubocop-minitest (0.38.2)
78
+ lint_roller (~> 1.1)
79
+ rubocop (>= 1.75.0, < 2.0)
80
+ rubocop-ast (>= 1.38.0, < 2.0)
81
+ rubocop-performance (1.26.1)
82
+ lint_roller (~> 1.1)
83
+ rubocop (>= 1.75.0, < 2.0)
84
+ rubocop-ast (>= 1.47.1, < 2.0)
85
+ rubocop-rake (0.7.1)
86
+ lint_roller (~> 1.1)
87
+ rubocop (>= 1.72.1)
88
+ rubocop-thread_safety (0.7.3)
89
+ lint_roller (~> 1.1)
90
+ rubocop (~> 1.72, >= 1.72.1)
91
+ rubocop-ast (>= 1.44.0, < 2.0)
92
+ ruby-progressbar (1.13.0)
93
+ unicode-display_width (3.2.0)
94
+ unicode-emoji (~> 4.1)
95
+ unicode-emoji (4.1.0)
96
+ webmock (3.26.1)
97
+ addressable (>= 2.8.0)
98
+ crack (>= 0.3.2)
99
+ hashdiff (>= 0.4.0, < 2.0.0)
100
+
101
+ PLATFORMS
102
+ aarch64-linux-gnu
103
+ aarch64-linux-musl
104
+ arm-linux-gnu
105
+ arm-linux-musl
106
+ arm64-darwin
107
+ ruby
108
+ x86-linux-gnu
109
+ x86-linux-musl
110
+ x86_64-darwin
111
+ x86_64-linux-gnu
112
+ x86_64-linux-musl
113
+
114
+ DEPENDENCIES
115
+ maxmind-geoip2!
116
+ minitest
117
+ rake
118
+ rubocop
119
+ rubocop-minitest
120
+ rubocop-performance
121
+ rubocop-rake
122
+ rubocop-thread_safety
123
+ webmock
124
+
125
+ BUNDLED WITH
126
+ 2.6.9
data/README.dev.md CHANGED
@@ -1,4 +1,4 @@
1
1
  # How to release
2
2
 
3
3
  See
4
- [here](https://github.com/maxmind/MaxMind-DB-Reader-ruby/blob/main/README.dev.md).
4
+ [here](https://github.com/maxmind/minfraud-api-ruby/blob/main/README.dev.md).
data/README.md CHANGED
@@ -133,6 +133,21 @@ record = reader.anonymous_ip('128.101.101.101')
133
133
  puts "Anonymous" if record.is_anonymous
134
134
  ```
135
135
 
136
+ ### Anonymous Plus Example
137
+
138
+ ```ruby
139
+ require 'maxmind/geoip2'
140
+
141
+ # This creates the Reader object which should be reused across lookups.
142
+ reader = MaxMind::GeoIP2::Reader.new(
143
+ database: '/usr/share/GeoIP/GeoIP-Anonymous-Plus.mmdb',
144
+ )
145
+
146
+ record = reader.anonymous_plus('128.101.101.101')
147
+
148
+ puts record.anonymizer_confidence # 30
149
+ ```
150
+
136
151
  ### ASN Example
137
152
 
138
153
  ```ruby
@@ -332,7 +347,7 @@ client API, please see [our support page](https://www.maxmind.com/en/support).
332
347
 
333
348
  ## Requirements
334
349
 
335
- This code requires Ruby version 2.5 or higher.
350
+ This code requires Ruby version 3.2 or higher.
336
351
 
337
352
  ## Contributing
338
353
 
@@ -345,7 +360,7 @@ This library uses [Semantic Versioning](https://semver.org/).
345
360
 
346
361
  ## Copyright and License
347
362
 
348
- This software is Copyright (c) 2020-2023 by MaxMind, Inc.
363
+ This software is Copyright (c) 2020-2025 by MaxMind, Inc.
349
364
 
350
365
  This is free software, licensed under the [Apache License, Version
351
366
  2.0](LICENSE-APACHE) or the [MIT License](LICENSE-MIT), at your option.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bundler/gem_tasks'
3
4
  require 'rake/testtask'
4
5
  require 'rubocop/rake_task'
5
6
 
@@ -10,5 +11,4 @@ end
10
11
  RuboCop::RakeTask.new
11
12
 
12
13
  desc 'Run tests and RuboCop'
13
- task default: :test
14
- task default: :rubocop
14
+ task default: %i[test rubocop]
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'maxmind/geoip2/model/anonymous_ip'
5
+
6
+ module MaxMind
7
+ module GeoIP2
8
+ module Model
9
+ # Model class for the Anonymous Plus database.
10
+ class AnonymousPlus < AnonymousIP
11
+ # A score ranging from 1 to 99 that is our percent confidence that the
12
+ # network is currently part of an actively used VPN service.
13
+ #
14
+ # @return [Integer, nil]
15
+ def anonymizer_confidence
16
+ get('anonymizer_confidence')
17
+ end
18
+
19
+ # The last day that the network was sighted in our analysis of
20
+ # anonymized networks. This value is parsed lazily.
21
+ #
22
+ # @return [Date, nil] A Date object representing the last seen date,
23
+ # or nil if the date is not available.
24
+ def network_last_seen
25
+ return @network_last_seen if defined?(@network_last_seen)
26
+
27
+ date_string = get('network_last_seen')
28
+
29
+ if !date_string
30
+ return nil
31
+ end
32
+
33
+ @network_last_seen = Date.parse(date_string)
34
+ end
35
+
36
+ # The name of the VPN provider (e.g., NordVPN, SurfShark, etc.)
37
+ # associated with the network.
38
+ #
39
+ # @return [String, nil]
40
+ def provider_name
41
+ get('provider_name')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -46,7 +46,7 @@ module MaxMind
46
46
 
47
47
  # @!visibility private
48
48
  def initialize(record, locales)
49
- super(record, locales)
49
+ super
50
50
  @city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
51
51
  @location = MaxMind::GeoIP2::Record::Location.new(record['location'])
52
52
  @postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])