alexreisner/geocoder
Complete Ruby geocoding solution.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 6w ago
- ✓30+ active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 50% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/alexreisner/geocoder)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/alexreisner/geocoder on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: alexreisner/geocoder
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/alexreisner/geocoder shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 6w ago
- 30+ active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 50% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live alexreisner/geocoder
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/alexreisner/geocoder.
What it runs against: a local clone of alexreisner/geocoder — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in alexreisner/geocoder | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 73 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of alexreisner/geocoder. If you don't
# have one yet, run these first:
#
# git clone https://github.com/alexreisner/geocoder.git
# cd geocoder
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of alexreisner/geocoder and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "alexreisner/geocoder(\\.git)?\\b" \\
&& ok "origin remote is alexreisner/geocoder" \\
|| miss "origin remote is not alexreisner/geocoder (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/geocoder.rb" \\
&& ok "lib/geocoder.rb" \\
|| miss "missing critical file: lib/geocoder.rb"
test -f "lib/geocoder/lookup.rb" \\
&& ok "lib/geocoder/lookup.rb" \\
|| miss "missing critical file: lib/geocoder/lookup.rb"
test -f "lib/geocoder/lookups/base.rb" \\
&& ok "lib/geocoder/lookups/base.rb" \\
|| miss "missing critical file: lib/geocoder/lookups/base.rb"
test -f "lib/geocoder/models/active_record.rb" \\
&& ok "lib/geocoder/models/active_record.rb" \\
|| miss "missing critical file: lib/geocoder/models/active_record.rb"
test -f "lib/geocoder/cache.rb" \\
&& ok "lib/geocoder/cache.rb" \\
|| miss "missing critical file: lib/geocoder/cache.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 73 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~43d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/alexreisner/geocoder"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Geocoder is a complete Ruby geocoding library that converts between addresses and geographic coordinates (latitude/longitude), as well as IP addresses to locations. It supports forward geocoding (address → coordinates), reverse geocoding (coordinates → address), and IP geolocation, integrating with 40+ geocoding APIs (Google Maps, OpenStreetMap, Mapbox, etc.) and providing performance optimizations through caching, batch processing, and geospatial database queries. Monolithic gem with clear layering: lib/geocoder/lookups/ contains 40+ API adapter classes (one per service), lib/geocoder/cache_stores/ provides caching backends (Redis, generic), lib/generators/ provides Rails integration via generators for initializers and database migrations, and lib/geocoder.rb is the main entry point. Examples/ directory includes runnable patterns for app-defined lookups and cache bypass. CLI interface exposed via lib/geocoder/cli.rb and bin/geocode.
👥Who it's for
Ruby on Rails developers and gem maintainers building location-aware applications who need to geocode user addresses, find nearby records, or geolocate visitors without building custom API integrations. Also used by developers working with ActiveRecord or Mongoid models who need declarative geocoding without writing raw API code.
🌱Maturity & risk
Production-ready and actively maintained. The repo shows 508KB of Ruby code across a mature structure with Rails 5.x–8.x support, comprehensive test coverage (implied by the Rakefile and CI in .github/workflows/default.yml), and a CI pipeline. Commit recency should be verified by checking CHANGELOG.md and git log, but the project supports modern Rails versions, indicating ongoing maintenance.
Low risk for core geocoding, but API-dependent: reliability depends on third-party geocoding services' uptime and accuracy. Single maintainer (alexreisner) creates potential bottleneck for critical issues. No major version bumps visible in filename structure, suggesting stable API but potentially slower response to breaking changes in dependency ecosystems. Caching layer (lib/geocoder/cache_stores/) adds complexity if misconfigured.
Active areas of work
Unable to determine from provided file structure alone; check CHANGELOG.md for recent changes, .github/workflows/default.yml for current CI tests, and GitHub Issues/PRs tabs. The presence of gemfiles/Gemfile.rails5.0 suggests active Rails version testing. Recent additions likely involve API adapters and database compatibility across Rails 5–8.
🚀Get running
git clone https://github.com/alexreisner/geocoder.git && cd geocoder && bundle install (uses Gemfile and gemfiles/Gemfile.rails5.0 for version-specific testing). Run bin/console for interactive testing or bin/geocode for CLI usage.
Daily commands: For CLI: bin/geocode <address>. For Rails app: require 'geocoder' in Gemfile, run bundle install, optionally run rails generate geocoder:config to create config/initializers/geocoder.rb, then use Geocoder.search(query) in code. For tests: bundle exec rake (runs RSpec suite defined in Rakefile).
🗺️Map of the codebase
lib/geocoder.rb— Main entry point and public API; defines Geocoder module with core methods like geocode, reverse_geocode, and config.lib/geocoder/lookup.rb— Abstract factory for selecting and instantiating the correct lookup service; every geocoding request routes through this.lib/geocoder/lookups/base.rb— Base class for all 40+ lookup service adapters; defines the interface every API implementation must follow.lib/geocoder/models/active_record.rb— ActiveRecord integration layer; adds geocoding methods and scopes to Rails models.lib/geocoder/cache.rb— Caching abstraction layer; determines whether to hit cache or call live API.lib/geocoder/configuration.rb— Global configuration store for API keys, lookup service selection, and feature flags.lib/geocoder/calculations.rb— Core geospatial math library; implements distance calculations and bounding box logic.
🛠️How to make changes
Add a new geocoding service API adapter
- Create a new lookup class inheriting from Geocoder::Lookup::Base in lib/geocoder/lookups/your_service.rb (
lib/geocoder/lookups/base.rb) - Implement required methods: name, query_url(), parse_result(). See Google or Nominatim for examples. (
lib/geocoder/lookups/google.rb) - Add your service name to the lookup factory in lib/geocoder/lookup.rb as a require and a case statement (
lib/geocoder/lookup.rb) - Update config documentation in README_API_GUIDE.md with API key requirements and rate limits (
README_API_GUIDE.md) - Test with Geocoder.search('address', lookup: :your_service) or in config; use test.rb as mock for unit tests (
lib/geocoder/lookups/test.rb)
Add geospatial query method to ActiveRecord models
- Open lib/geocoder/models/active_record.rb and add a module with your custom scope method (
lib/geocoder/models/active_record.rb) - Use Geocoder::Calculations methods (distance, bearing, bounding box) to build SQL WHERE clauses (
lib/geocoder/calculations.rb) - Include Geocoder::Model in your ActiveRecord class with geocoded_by and reverse_geocoded_by declarations (
lib/geocoder.rb) - Test by calling YourModel.near(lat, lng, radius).limit(10) or create custom scope syntax (
lib/geocoder/models/active_record.rb)
Customize caching behavior
- Extend or create a cache store class inheriting from Geocoder::CacheStores::Base (
lib/geocoder/cache_stores/base.rb) - Implement read() and write() methods; optionally handle TTL and key generation (
lib/geocoder/cache_stores/redis.rb) - Register your store in Geocoder.configure(cache: YourCacheClass.new) or via initializer (
lib/geocoder/configuration.rb) - Cache logic checks hit in lib/geocoder/cache.rb before calling lookup; ensure parse_result is deterministic (
lib/geocoder/cache.rb)
Extend IP geolocation lookup selection
- Edit lib/geocoder/ip_address.rb to add IP detection or lookup routing logic (
lib/geocoder/ip_address.rb) - Call Geocoder.search(ip_string) which uses ip_address.rb to select appropriate lookup service (
lib/geocoder/lookup.rb) - Check config[:ip_lookup] to allow user override; fallback to default IP service (e.g., ipinfo_io) (
lib/geocoder/configuration.rb)
🔧Why these technologies
- Ruby 2.5+ with JSON — Lightweight, expressive language; JSON parsing is built-in or via json gem; minimal dependencies for broad Ruby ecosystem
- 40+ pluggable lookup adapters — Users can switch between services (Google, Nominatim, ESRI, etc.) without code changes; supports fallback strategies and multi-region deployments
- ActiveRecord model integration — Natural syntax for Rails developers (Model.geocode, Model.near); leverages existing migrations and database abstraction
- Multi-backend caching (Redis, Memcached, file store) — Reduces API calls; supports distributed systems; cache can be shared across web servers
- Haversine distance formula — Mathematically accurate for spherical Earth; fast O(1) calculation; standard geospatial query method
⚖️Trade-offs already made
-
One gem covers forward, reverse, and IP geocoding across 40+ services
- Why: Single dependency; consistent API; developers don't need to switch gems per service
- Consequence: Larger gem size; some services rarely used; complex lookup factory; harder to optimize for one use case
-
Lookup services are instantiated at runtime, not at boot
- Why: Allows dynamic lookup selection and API key injection per request
- Consequence: Slightly slower first geocoding call; more memory if many lookups are cached; factory method adds indirection
-
Cache abstraction sits between geocoding request and API call
- Why: Transparent caching; users don't manage cache keys; same cache works with any service
- Consequence: Cache must be determin
🪤Traps & gotchas
API key management: many lookups require API keys set via Geocoder.configure (api_key: 'xxx'); missing keys cause silent failures or rate-limiting. Cache invalidation: cached results persist across queries unless explicitly bypassed (see examples/cache_bypass.rb). Database assumptions: geospatial queries assume lat/lng columns exist; custom column names require config. IP geolocation accuracy: depends heavily on GeoIP database freshness (MaxMind generators in lib/generators/geocoder/maxmind/ may need periodic updates). Reverse geocoding rate limits: some APIs aggressively throttle reverse geocoding; no built-in backoff besides caching. Rails version lock: some features require Rails 5.0+; earlier versions unsupported.
🏗️Architecture
💡Concepts to learn
- Reverse Geocoding — Core feature of this gem; converts coordinates back to human-readable addresses, essential for mobile location features and geospatial queries
- Adapter/Strategy Pattern — Architecture pattern used throughout lookups/ directory to swap between 40+ geocoding APIs transparently without changing client code
- Haversine Formula — Used internally by lib/geocoder/calculations.rb to compute great-circle distances between coordinates for radius searches
- Bounding Box Queries — Geospatial optimization technique (rectangle-based filtering before radius checks) implemented in lib/geocoder/calculations.rb for database efficiency
- GeoJSON — Standard format for representing geographic features; many lookups parse/return GeoJSON from APIs before converting to geocoder result objects
- Caching Strategies (TTL, Cache Invalidation) — lib/geocoder/cache.rb and cache_stores/ implement time-based and conditional caching to reduce expensive API calls; critical for performance at scale
- IP Geolocation (MaxMind GeoIP) — lib/geocoder/ip_address.rb and generators/geocoder/maxmind/ handle IP-to-location lookups using GeoIP databases; essential for analytics and fraud detection
🔗Related repos
ruby-geocoder/rgeo— Complementary Ruby geospatial library providing geometry objects and spatial operations; often used alongside geocoder for advanced GIS logicLeaflet/Leaflet— Standard JavaScript mapping library frequently paired with geocoder on frontend to display geocoded results on interactive mapsmapbox/mapbox-gl-js— Modern WebGL map rendering; often paired with geocoder for geocoding + rich map UX (geocoder supports Mapbox as a lookup service)thoughtbot/factory_bot— Commonly used in test suites for geocoder-dependent Rails apps to generate fixture objects with fake coordinatesrails/rails— Core dependency; geocoder integrates deeply with ActiveRecord models and Rails generators
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test suite for cache_stores implementations
The repo has three cache store implementations (base.rb, generic.rb, redis.rb) in lib/geocoder/cache_stores/ but there's no evidence of dedicated test files for these critical performance components. Given caching is a key feature advertised in the README, comprehensive tests for cache hit/miss scenarios, TTL handling, and Redis-specific behavior would improve reliability and make contributions safer.
- [ ] Create test/geocoder/cache_stores/base_test.rb with unit tests for BaseCache interface
- [ ] Create test/geocoder/cache_stores/generic_test.rb testing hash-based caching and expiration
- [ ] Create test/geocoder/cache_stores/redis_test.rb with Redis integration tests (using Redis mock or test container)
- [ ] Add tests for cache key generation, serialization/deserialization edge cases
- [ ] Ensure test coverage for cache bypass behavior (see examples/cache_bypass.rb)
Add GitHub Actions workflow for testing against multiple Ruby/Rails versions
The repo supports Ruby 2.5+ and Rails 5.x through 8.x per the README, and has multiple Gemfiles (Gemfile.rails5.0 visible), but only .github/workflows/default.yml exists. A matrix workflow testing multiple Ruby versions (2.5, 3.0, 3.1, 3.2, 3.3) against multiple Rails versions would catch version-specific bugs early and improve contributor confidence.
- [ ] Create .github/workflows/matrix_tests.yml with Ruby version matrix (2.5, 3.0, 3.1, 3.2, 3.3)
- [ ] Add Rails version matrix targeting 5.0, 6.0, 7.0, 8.0 using existing Gemfiles
- [ ] Configure job to run rake tests with each combination
- [ ] Add explicit JRuby testing (mentioned in README as supported)
- [ ] Document in CONTRIBUTING.md which versions must pass before merge
Create integration test suite for major Lookup services
The repo supports 40+ geocoding APIs (Google, Bing, Esri, Azure, Baidu, etc.) visible in lib/geocoder/lookups/, but without dedicated integration tests for each lookup, it's difficult to catch breaking changes from API providers. A structured test suite using VCR cassettes or mock responses would validate the parsing/response handling for each major service.
- [ ] Create test/geocoder/lookups/google_test.rb with real/cassette-based tests for forward and reverse geocoding
- [ ] Create test/geocoder/lookups/bing_test.rb following same pattern
- [ ] Create test/geocoder/lookups/esri_test.rb with Esri token handling tests (lib/geocoder/esri_token.rb exists)
- [ ] Add test helpers to generate mock responses matching each API's format
- [ ] Document in CONTRIBUTING.md how to add tests for new lookup services with response cassettes
🌿Good first issues
- Add test coverage for lib/geocoder/esri_token.rb and lib/geocoder/exceptions.rb (both present but no corresponding spec files visible in provided structure); write unit tests for token refresh logic and exception hierarchy.: low: Small files with clear responsibilities, good onboarding to test structure
- Expand examples/ directory: add example for batch geocoding with error handling (mentioned in README as advanced feature but no runnable example visible), add example for custom cache store implementation beyond cache_bypass.rb.: medium: Improves docs via executable code; teaches caching and batch patterns
- Document API-specific quirks: create README sections for each lookup service (Google Maps, OpenStreetMap, Mapbox, etc.) listing rate limits, response format quirks, required config keys (currently scattered across lookups/ files); auto-generate from lookup class docstrings.: medium-high: Reduces user friction; PR involves linking lookups/ adapter code to README_API_GUIDE.md
⭐Top contributors
Click to expand
Top contributors
- @alexreisner — 50 commits
- @AhlOct — 10 commits
- @mikehale — 6 commits
- @iltempo — 3 commits
- @TheRusskiy — 2 commits
📝Recent commits
Click to expand
Recent commits
aa134e3— Merge pull request #1705 from mikehale/update-build (alexreisner)05ee377— valid copyright code (mikehale)d5182ef— ensure ERB is required (mikehale)d8454a6— add required gems for ruby 4 (mikehale)a11d52e— sort gemfile (mikehale)6589281— update actions/checkout to v6 (mikehale)428488a— new ruby version (mikehale)2d3be47— Merge pull request #1704 from pzaich/add-mapbox-county-tests (alexreisner)20cffe5— Add a county accessor to Mapbox geocoder results that maps to Mapbox's district context field. This follows the same pat (pzaich)72f1e1e— Merge pull request #1702 from ChaelCodes/add-atreet-address-to-nominatim (alexreisner)
🔒Security observations
- High · Multiple API Keys and Credentials Handling —
lib/geocoder/lookups/* (all lookup services). The codebase integrates with 40+ geocoding APIs (Google, Bing, Esri, Azure, etc.). Many lookups require API keys for authentication. If these credentials are not properly managed (stored in environment variables rather than hardcoded), they could be exposed in logs, error messages, or version control. Fix: Ensure all API keys are loaded exclusively from environment variables or secure vaults. Never hardcode credentials. Implement credential rotation policies and use AWS Secrets Manager, HashiCorp Vault, or similar for production environments. - High · ESRI Token Management Security —
lib/geocoder/esri_token.rb. The ESRI token management module (lib/geocoder/esri_token.rb) handles token generation and caching. Insecure token storage or transmission could lead to unauthorized access to ESRI services. Fix: Verify tokens are transmitted over HTTPS only, stored securely with appropriate expiration, and regenerated before expiration. Implement secure token revocation mechanisms. - High · Cache Storage Security Risks —
lib/geocoder/cache.rb, lib/geocoder/cache_stores/redis.rb. The caching system (lib/geocoder/cache.rb, lib/geocoder/cache_stores/*) caches geocoding results which may contain sensitive location data. Redis cache store may be exposed to network attacks if not properly secured. Fix: Encrypt cached data at rest. Require authentication for Redis connections. Implement access controls and use secure network isolation. Consider implementing cache TTL for sensitive data. - Medium · User Input Validation in CLI Tool —
lib/geocoder/cli.rb, bin/geocode. The CLI tool (lib/geocoder/cli.rb, bin/geocode) accepts user input for geocoding queries. Without proper input validation and sanitization, this could be susceptible to injection attacks or DOS attacks. Fix: Implement strict input validation and sanitization. Limit query length and complexity. Add rate limiting and DOS protection mechanisms. - Medium · SQL Injection Risk in Database Integration —
lib/geocoder/calculations.rb, lib/geocoder/lookups/base.rb. The geocoder integrates with ActiveRecord/Mongoid for database queries (geospatial queries, caching). Improper query construction could lead to SQL injection vulnerabilities. Fix: Always use parameterized queries and ORM built-in protection mechanisms. Never concatenate user input directly into SQL queries. Use prepared statements exclusively. - Medium · HTTP Client Security Configuration —
lib/geocoder/lookups/base.rb and all lookup implementations. Multiple lookup services make HTTP requests to external APIs. Insecure SSL/TLS configuration, missing certificate validation, or insufficient timeout settings could expose the application to MITM attacks. Fix: Enforce SSL/TLS certificate validation. Set appropriate connection and read timeouts. Use secure HTTP libraries (e.g., Net::HTTP with verify_mode=OpenSSL::SSL::VERIFY_PEER). Consider certificate pinning for critical services. - Medium · IP Address Geolocation Information Disclosure —
lib/geocoder/ip_address.rb. IP-based geocoding (lib/geocoder/ip_address.rb) may disclose user location information. This could be a privacy concern if not properly handled, especially for GDPR compliance. Fix: Implement user consent mechanisms before collecting IP-based location data. Provide opt-out mechanisms. Log and audit access to location data. Implement data retention policies and secure deletion. - Medium · Error Message Information Disclosure —
lib/geocoder/exceptions.rb, lib/geocoder/logger.rb, lib/geocoder/kernel_logger.rb. Exception handling (lib/geocoder/exceptions.rb) may expose sensitive information about API keys, service URLs, or system internals in error messages. Fix: Sanitize error messages before displaying to users. Log detailed errors securely server-side only. Avoid exposing API keys, URLs, or stack traces in client-facing error messages. - Low · Dependency Management and Updates —
undefined. No detailed Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.