Skip to content

Add extended geo fields for v0.3.0 - #4

Merged
rameerez merged 7 commits into
mainfrom
v0.3.0-extended-geo-fields
Feb 8, 2026
Merged

Add extended geo fields for v0.3.0#4
rameerez merged 7 commits into
mainfrom
v0.3.0-extended-geo-fields

Conversation

@rameerez

@rameerez rameerez commented Feb 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add 6 new geo fields to LocationResult and wire them through Cloudflare and MaxMind providers
  • Add 70 new tests (118 → 188 total, 415 assertions) with comprehensive coverage
  • Standardize CI/build configuration and update README documentation
  • Bump version to 0.3.0 (no breaking changes)

New features

6 new geo fields on LocationResult

All are optional keyword arguments defaulting to nil for full backward compatibility:

Field Type Description
region String Subdivision/state name (e.g. "California")
region_code String Subdivision ISO code (e.g. "CA")
continent String Continent code (e.g. "NA")
timezone String IANA timezone (e.g. "America/Los_Angeles")
latitude Float Latitude coordinate
longitude Float Longitude coordinate

CloudflareProvider enhancements

Now reads 6 additional Cloudflare "Add visitor location headers" Managed Transform headers:

  • HTTP_CF_REGIONregion
  • HTTP_CF_REGION_CODEregion_code
  • HTTP_CF_LATITUDElatitude
  • HTTP_CF_LONGITUDElongitude
  • HTTP_CF_TIMEZONEtimezone
  • HTTP_CF_CONTINENTcontinent

Added extract_header and parse_coordinate private helper methods for cleaner header extraction and coordinate parsing.

MaxmindProvider enhancements

Now extracts from the MaxMind record:

  • region — subdivision name with English language preference fallback
  • region_code — subdivision ISO code
  • continent — continent code
  • timezone — from location data
  • latitude — from location data
  • longitude — from location data

Added extract_region private helper with English language preference fallback.

to_h includes all new fields

The to_h method now returns all 6 new keys (set to nil when not provided).

Testing improvements

  • 70 new tests added (118 → 188 total), 415 assertions
  • Comprehensive coverage for all new fields across LocationResult, CloudflareProvider, MaxmindProvider
  • Integration tests for end-to-end field propagation through the full lookup pipeline
  • Mock request helpers extended with new header support for Cloudflare geo headers
  • 80.55% line / 72.57% branch coverage
  • Added minitest-reporters with SpecReporter for better test output

CI & build standardization

  • CI workflow: Standardized to Ruby 3.3, 3.4, 4.0 (dropped 3.2); removed Rails version matrix; added RAILS_ENV=test; simplified artifact naming
  • Gemfile: Standardized with proper :development and :development, :test dependency groups
  • gemspec: Dev dependencies moved to Gemfile (gemspec now only declares runtime deps); added rubygems_mfa_required metadata
  • .simplecov: Extracted to standalone file with branch coverage enabled
  • Rakefile: Removed redundant require "bundler/gem_tasks" line

README updates

  • Documented all 6 new fields with usage examples
  • Updated Cloudflare headers list to include new geo headers
  • Updated to_h example output to show new fields
  • Added RailsFast CTA

Breaking changes

None. All new fields are optional keyword arguments with nil defaults. Existing code that creates LocationResult with positional args only continues to work unchanged. The to_h output now includes 6 additional keys (all nil if not provided), which could affect code doing strict hash comparison but this is unlikely in practice.

Version bump

0.2.x0.3.0 — minor version bump for new features, no breaking changes.

Test plan

  • All 188 tests pass (415 assertions, 0 failures, 0 errors)
  • Verify Cloudflare provider reads all 6 new headers correctly in production
  • Verify MaxMind provider extracts all 6 new fields from real GeoIP database
  • Confirm backward compatibility with existing LocationResult usage
  • Verify to_h output includes new fields

🤖 Generated with Claude Code

rameerez and others added 4 commits February 8, 2026 21:07
… v0.3.0

Expand LocationResult with 6 new optional keyword args (region, region_code,
continent, timezone, latitude, longitude) and wire them through Cloudflare
and MaxMind providers. Add 70 new tests (188 total), standardize CI/build
config, and update README with new field documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e/metro_code

The Cloudflare "Add visitor location headers" managed transform uses
cf-iplatitude, cf-iplongitude, and cf-ipcontinent (with the "ip" prefix),
not cf-latitude, cf-longitude, and cf-continent as previously assumed.
This aligns all header constants with the official Cloudflare documentation.

Also adds the two previously missing Cloudflare headers:
- cf-postal-code (visitor's postal/ZIP code)
- cf-metro-code (visitor's metro/DMA code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cloudflare provider already extracts postal_code and metro_code but
MaxMind provider was missing them. This adds extraction from the
MaxMind GeoLite2-City database for provider parity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests 29 IPs across 6 continents covering field coverage, to_h
round-trip, aliases, coordinate validation, data types, performance
benchmarks, thread safety, and edge cases (private IPs, IPv6).

Run manually: ruby test/manual/stress_test_maxmind.rb /path/to/GeoLite2-City.mmdb

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rameerez

rameerez commented Feb 8, 2026

Copy link
Copy Markdown
Owner Author

Latest changes

MaxMind provider parity

Added postal_code and metro_code extraction to MaxMind provider for feature parity with Cloudflare provider:

  • postal_code from record.dig('postal', 'code')
  • metro_code from record.dig('location', 'metro_code') (converted to string)

6 new tests, updated fixture and assertions. 198 tests, 434 assertions, all passing.

Manual stress test

Added test/manual/stress_test_maxmind.rb — a comprehensive stress test against a real GeoLite2-City database. Covers:

  • 29 IPs across 6 continents
  • Field coverage report across all 12 fields
  • to_h round-trip verification
  • Alias verification
  • country_info (ISO3166) integration
  • Coordinate range validation
  • Data type validation on all fields
  • Performance benchmark (sub-millisecond: ~0.1ms/lookup, ~9k ops/sec)
  • Thread safety (1,000 concurrent lookups across 10 threads)
  • Edge cases: private IP rejection, IPv6, nil records

Run with: ruby test/manual/stress_test_maxmind.rb /path/to/GeoLite2-City.mmdb

All tests pass ✅

rameerez and others added 3 commits February 8, 2026 22:06
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Repository owner deleted a comment from claude Bot Feb 8, 2026
Repository owner deleted a comment from claude Bot Feb 8, 2026
Repository owner deleted a comment from claude Bot Feb 8, 2026
Repository owner deleted a comment from claude Bot Feb 8, 2026
@claude

claude Bot commented Feb 8, 2026

Copy link
Copy Markdown

Pull Request Review: v0.3.0 Extended Geo Fields

Summary

This PR adds 8 new geolocation fields to the gem in a backward-compatible way. The implementation is well-structured with comprehensive test coverage (70 new tests, 188 total). All CI tests are passing across Ruby 3.3, 3.4, and 4.0.

✅ Strengths

Code Quality

  • Excellent backward compatibility: New fields are optional keyword arguments with nil defaults, preserving the existing API
  • Comprehensive test coverage: 188 tests with 415 assertions, achieving 80.55% line / 72.57% branch coverage
  • Clean separation of concerns: Helper methods like extract_header, parse_coordinate, and extract_region improve code readability
  • Defensive error handling: Coordinate parsing gracefully handles invalid values, empty strings, and type errors
  • Consistent implementation: Both providers (Cloudflare and MaxMind) extract all 8 new fields with feature parity

Testing

  • Thorough edge case coverage: empty strings, nil values, boundary coordinates (±90°, ±180°), non-numeric strings
  • Integration tests verify end-to-end field propagation
  • Manual stress test (test/manual/stress_test_maxmind.rb) provides real-world validation with 29 IPs across 6 continents
  • Mock helpers properly extended to support new headers

Documentation

  • README updated with clear examples and field descriptions
  • Cloudflare header mapping table added with official docs links
  • MaxMind database documentation referenced
  • CHANGELOG properly documents the release

CI/Build Improvements

  • Standardized to Ruby 3.3, 3.4, 4.0 (dropped 3.2)
  • Removed Rails version matrix (appropriate for a non-Rails-specific gem)
  • Proper test environment configuration with RAILS_ENV=test
  • SimpleCov extracted to standalone config with branch coverage enabled
  • Gemspec cleaned up with dev dependencies moved to Gemfile
  • Added rubygems_mfa_required metadata for security

🔍 Observations & Minor Suggestions

1. Cloudflare Header Constants Correction ✅

lib/trackdown/providers/cloudflare_provider.rb:18-21

The PR correctly fixes header names to match Cloudflare's official "Add visitor location headers" documentation:

  • HTTP_CF_IPLATITUDE (not HTTP_CF_LATITUDE)
  • HTTP_CF_IPLONGITUDE (not HTTP_CF_LONGITUDE)
  • HTTP_CF_IPCONTINENT (not HTTP_CF_CONTINENT)

This correction was made in commit 1d12c831 and is properly tested.

2. MaxMind Metro Code String Conversion

lib/trackdown/providers/maxmind_provider.rb:60

metro_code: record&.dig('location', 'metro_code')&.to_s

Good: Using to_s ensures consistency since Cloudflare returns strings via headers. However, consider documenting this type conversion choice for future maintainers.

3. Test File Organization

test/manual/stress_test_maxmind.rb (416 lines)

Good: Manual test is appropriately placed outside the main test suite since it requires a real MaxMind database. Consider adding a comment at the top of the file explaining:

  • When to run this test
  • Why it's manual vs automated
  • Expected output format

4. Empty String Handling Inconsistency

lib/trackdown/providers/cloudflare_provider.rb:86-90

def extract_header(request, header)
  value = request.env[header]
  return nil if value.nil? || value.empty?
  value
end

Good: Empty strings are converted to nil, which is consistent with the API contract. This is properly tested in test_extract_header_empty_string_returns_nil.

5. Coordinate Validation

The code doesn't validate coordinate ranges (lat: -90 to 90, lon: -180 to 180). While this is fine since you're trusting the data sources (Cloudflare and MaxMind), consider:

  • Option A: Document that coordinates are not validated and assumed to be from trusted sources
  • Option B: Add optional validation with configurable strictness

6. README Promotional Content

README.md:3-4

The RailsFast promotional TIP block is prominent. While self-promotion is understandable for open-source work, consider:

  • Moving it to a "Sponsors" section at the bottom
  • Making it less prominent in the opening paragraph
  • Using a NOTE instead of TIP (TIPs typically convey technical guidance)

This is purely subjective and not a code quality issue.

🔒 Security Considerations

No concerns identified:

  • Input validation is appropriate for the trust model
  • No SQL injection risks (using MaxMind's binary database)
  • No command injection risks
  • Error handling doesn't leak sensitive information
  • MFA required for RubyGems publishing (rubygems_mfa_required: true)

🚀 Performance Considerations

Well optimized:

  • Cloudflare provider has zero overhead (direct header reads)
  • MaxMind provider uses connection pooling (already implemented)
  • No N+1 query risks
  • Coordinate parsing uses safe Float() conversion with exception handling
  • New fields don't add significant memory overhead

📊 Test Coverage Analysis

Coverage Stats: 80.55% line / 72.57% branch

Excellent coverage for the new features. The 70 new tests cover:

  • ✅ All 8 new fields initialization
  • ✅ Backward compatibility (positional args)
  • to_h serialization with new fields
  • ✅ Nil/empty string handling
  • ✅ Coordinate boundary values (0, ±90, ±180)
  • ✅ Invalid coordinate parsing
  • ✅ Provider-specific extraction logic
  • ✅ Multiple subdivisions handling (MaxMind)
  • ✅ Language fallback (English → first available)

Potential gap: Consider adding tests for:

  • Invalid coordinate values outside valid ranges (e.g., latitude: 100)
  • Very large float precision edge cases
  • Unicode characters in region/timezone names (though unlikely)

These are minor and the current coverage is production-ready.

🎯 Breaking Changes Assessment

No breaking changes as claimed:

  • All new fields are optional keyword arguments
  • Existing positional argument API unchanged
  • to_h output expanded but existing keys unchanged
  • Default provider behavior (:auto) unchanged

Potential consideration: to_h now includes 8 additional keys (all nil if not set). While unlikely to break code, strict hash comparisons or serialization with fixed schemas could be affected. This is well-documented in the PR description and acceptable for a minor version bump.

📝 Recommendations

High Priority

None - the code is production-ready.

Medium Priority

  1. Document metro_code type conversion in code comments
  2. Add top-of-file comment to test/manual/stress_test_maxmind.rb explaining usage
  3. Consider coordinate validation documentation to clarify the trust model

Low Priority

  1. README promotional content placement (subjective)
  2. Add a few edge case tests mentioned above (nice-to-have, not critical)

🎉 Final Verdict

✅ Approve with enthusiasm

This is a high-quality PR that:

  • Adds substantial new functionality (8 new fields)
  • Maintains 100% backward compatibility
  • Includes comprehensive tests (188 tests, 415 assertions)
  • Updates documentation thoroughly
  • Passes all CI checks
  • Shows excellent attention to detail in handling edge cases

The code quality is excellent, the testing is thorough, and the implementation is clean. The standardization of CI/build configuration is also a welcome improvement.

Recommended action: Merge after addressing any of the medium-priority items if desired (they're optional improvements, not blockers).

Great work! 🚀


Review performed by Claude Code

Repository owner deleted a comment from claude Bot Feb 8, 2026
Repository owner deleted a comment from claude Bot Feb 8, 2026
@rameerez
rameerez merged commit 3e49418 into main Feb 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant