Class: Trackdown::Providers::MaxmindProvider

Inherits:
BaseProvider show all
Defined in:
lib/trackdown/providers/maxmind_provider.rb

Overview

Provider that uses MaxMind GeoLite2 database for IP geolocation Requires the maxmind-db gem and a downloaded database file

Defined Under Namespace

Classes: DatabaseError, TimeoutError

Constant Summary collapse

ACCURACY_RADIUS_CONFIDENCE_PERCENTAGE =

MaxMind publishes the accuracy radius as the radius, in kilometres, within which the address is likely to be, at a 67% confidence level: https://support.maxmind.com/knowledge-base/articles/maxmind-geolocation-accuracy

67

Class Method Summary collapse

Methods inherited from BaseProvider

get_country_name, get_emoji_flag, parse_coordinate, request_provenance

Class Method Details

.available?(request: nil) ⇒ Boolean

Check if MaxMind database is available

Returns:

  • (Boolean)


49
50
51
52
53
54
# File 'lib/trackdown/providers/maxmind_provider.rb', line 49

def available?(request: nil)
  return false unless maxmind_available?
  return false unless Trackdown.database_exists?

  true
end

.database_fingerprintObject

The fingerprint used by the most recent successful reader fetch. Results do not read this global diagnostic: each one retains its reader-bound fingerprint, so concurrent generations can never mix provenance.



59
60
61
# File 'lib/trackdown/providers/maxmind_provider.rb', line 59

def database_fingerprint
  @fingerprint_mutex.synchronize { @database_fingerprint }
end

.locate(ip, request: nil) ⇒ LocationResult

Locate IP using MaxMind database

Parameters:

  • ip (String)

    The IP address to locate

  • request (ActionDispatch::Request, nil) (defaults to: nil)

    Not used by MaxMind provider

Returns:

Raises:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/trackdown/providers/maxmind_provider.rb', line 83

def locate(ip, request: nil)
  raise Trackdown::Error, "MaxMind database not found" unless Trackdown.database_exists?
  raise Trackdown::Error, "maxmind-db gem not installed. Add it to your Gemfile: gem 'maxmind-db'" unless maxmind_available?

  lookup = fetch_record(ip)
  record = lookup.record
  fingerprint = lookup.fingerprint
  provenance = database_provenance(fingerprint)

  # We looked, in this exact database, and this address simply isn't in it.
  return LocationResult.unavailable(:address_not_found, **provenance) if record.nil?

  country_code = extract_country_code(record)
  country_name = extract_country_name(record)
  city = extract_city(record)
  flag_emoji = get_emoji_flag(country_code)
  accuracy_radius = record&.dig('location', 'accuracy_radius')

  LocationResult.new(
    country_code, country_name, city, flag_emoji,
    region: extract_region(record),
    region_code: record&.dig('subdivisions', 0, 'iso_code'),
    continent: record&.dig('continent', 'code'),
    timezone: record&.dig('location', 'time_zone'),
    latitude: record&.dig('location', 'latitude'),
    longitude: record&.dig('location', 'longitude'),
    postal_code: record&.dig('postal', 'code'),
    metro_code: record&.dig('location', 'metro_code')&.to_s,
    accuracy_radius_in_kilometers: accuracy_radius,
    accuracy_radius_confidence_percentage: (ACCURACY_RADIUS_CONFIDENCE_PERCENTAGE if accuracy_radius),
    **provenance
  )
end

.provider_nameObject



40
41
42
# File 'lib/trackdown/providers/maxmind_provider.rb', line 40

def provider_name
  :maxmind
end

.provider_sourceObject



44
45
46
# File 'lib/trackdown/providers/maxmind_provider.rb', line 44

def provider_source
  :maxmind_local_database
end

.reset_database!Object

Forget the open database. Call this after replacing the .mmdb file so the next lookup opens the new one — Trackdown::DatabaseUpdater already does.



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/trackdown/providers/maxmind_provider.rb', line 65

def reset_database!
  # Let go of the pool rather than shutting it down: a lookup already in
  # flight must not fail because a refresh happened underneath it. Ruby
  # reclaims the old readers once the last lookup lets go of them.
  @pool_mutex.synchronize do
    @reader_pool = nil
    @fingerprint_mutex.synchronize do
      @database_fingerprint = nil
      @database_fingerprints = {}
    end
  end
  nil
end