trackdown 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6bef9cf45c5eb0f1cebf958c00d8b655d0840b07fe3ae3bca3732e0bdd25e2f8
4
+ data.tar.gz: ac99be87d20b6795d7ce156bc9ae50af70e0b4506fa61c00851f5c1abb690e39
5
+ SHA512:
6
+ metadata.gz: e69d703884469e3880eb06472182e00d356884a92089074a9e4ea916b8709d22bb34de22c8d5d37e16d8aada028099f96e22a2bc2a88c6aa10610a99f04c208b
7
+ data.tar.gz: 18c3f1641bcec2facfbefc038d715f434e8a2f9bf5e53b1d83d85ed12d38fa008460bbe936cf47654de2686f6bdb2412fc6a90d2836107e573783e8a9fec435a
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## [0.1.0] - 2024-10-29
2
+
3
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Javi R
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # πŸ“ `trackdown` - Ruby gem to geolocate IPs (MaxMind BYOK)
2
+
3
+ `trackdown` is a Ruby gem that easily allows you to geolocate IP addresses. It's a simple, convenient wrapper on top of MaxMind. Just bring your own MaxMind keys, and you're good to go. It keeps your MaxMind database updated regularly, and it offers a handy API for Rails applications to fetch country, city, and emoji flag information for any IP address.
4
+
5
+ Given an IP, it gives you the corresponding:
6
+ - πŸ—ΊοΈ Country (two-letter country code + country name)
7
+ - πŸ“ City
8
+ - πŸ‡ΊπŸ‡Έ Emoji flag of the country
9
+
10
+ `trackdown` is BYOK (Bring Your Own Key) – you'll need your own MaxMind keys for it to work. It's your responsibility to make sure your app complies with the license for the MaxMind database you're using. Get a MaxMind account and license key at [MaxMind](https://www.maxmind.com/).
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'trackdown'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ bundle install
24
+ ```
25
+
26
+ ## Setup
27
+
28
+ First, run the installation generator:
29
+
30
+ ```bash
31
+ rails generate trackdown:install
32
+ ```
33
+
34
+ This will create an initializer file at `config/initializers/trackdown.rb`. Open this file and add your MaxMind license key and account ID:
35
+
36
+ ```ruby
37
+ Trackdown.configure do |config|
38
+ # Tip: do not write your plaintext keys in the code, use Rails.application.credentials instead
39
+ config.maxmind_account_id = 'your_account_id_here'
40
+ config.maxmind_license_key = 'your_license_key_here'
41
+ end
42
+ ```
43
+
44
+ > [!TIP]
45
+ > To get your MaxMind account ID and license key, you need to create an account at [MaxMind](https://www.maxmind.com/) and get a license key.
46
+
47
+ You can also configure the path where the MaxMind database will be stored. By default, it will be stored at `db/GeoLite2-City.mmdb`:
48
+
49
+ ```ruby
50
+ config.database_path = Rails.root.join('db', 'GeoLite2-City.mmdb').to_s
51
+ ```
52
+
53
+ The generator also creates a `TrackdownDatabaseRefreshJob` job for regularly updating the MaxMind database. You can just get a database the first time and just keep using it, but the information will get outdated and some IPs will become stale or inaccurate.
54
+
55
+ To keep your IP geolocation accurate, you need to make sure the `TrackdownDatabaseRefreshJob` runs regularly. How you do that, exactly, depends on the queueing system you're using.
56
+
57
+ If you're using `solid_queue` (the Rails 8 default), you can easily add it to your schedule in the `config/recurring.yml` file like this:
58
+ ```yaml
59
+ production:
60
+ refresh_maxmind_database:
61
+ class: TrackdownDatabaseRefreshJob
62
+ queue: default
63
+ schedule: every day at 4am US/Pacific
64
+ ```
65
+
66
+ After setting everything up, you can run the following command to update the MaxMind database / get the first fresh copy of it:
67
+
68
+ ```ruby
69
+ Trackdown.update_database
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ To geolocate an IP address:
75
+
76
+ ```ruby
77
+ Trackdown.locate('8.8.8.8').country
78
+ # => 'United States'
79
+ ```
80
+
81
+ You can also do things like:
82
+ ```ruby
83
+ Trackdown.locate('8.8.8.8').emoji
84
+ # => 'πŸ‡ΊπŸ‡Έ'
85
+ ```
86
+
87
+ In fact, there are a few methods you can use:
88
+ ```ruby
89
+ result = Trackdown.locate('8.8.8.8')
90
+
91
+ result.country_code # => 'US'
92
+ result.country_name # => 'United States'
93
+ result.country # => 'United States' (alias for country_name)
94
+ result.country_info # => # A big hash of information about the country, from the `countries` gem
95
+ result.city # => 'Mountain View'
96
+ result.flag_emoji # => 'πŸ‡ΊπŸ‡Έ'
97
+ result.emoji # => 'πŸ‡ΊπŸ‡Έ' (alias for flag_emoji)
98
+ result.country_flag # => 'πŸ‡ΊπŸ‡Έ' (alias for flag_emoji)
99
+ ```
100
+
101
+ For `country_info` we're leveraging the [`countries`](https://github.com/countries/countries) gem, so you get a lot of information about the country, like the continent, the region, the languages spoken, the currency, and more:
102
+
103
+ ```ruby
104
+ result.country_info.alpha3 # => "USA"
105
+ result.country_info.currency_code # => "USD"
106
+ result.country_info.continent # => 'North America'
107
+ result.country_info.nationality # => 'American'
108
+ result.country_info.iso_long_name # => 'The United States of America'
109
+ ```
110
+
111
+ If you prefer, you can also get all the information as a hash:
112
+ ```ruby
113
+ result.to_h
114
+ # => {
115
+ # country_code: 'US',
116
+ # country_name: 'United States',
117
+ # city: 'Mountain View',
118
+ # flag_emoji: 'πŸ‡ΊπŸ‡Έ',
119
+ # country_info: { ... }
120
+ # }
121
+ ```
122
+
123
+ To manually update the MaxMind IP database:
124
+ ```ruby
125
+ Trackdown.update_database
126
+ ```
127
+
128
+
129
+ ## Development
130
+
131
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
132
+
133
+ To install this gem onto your local machine, run `bundle exec rake install`.
134
+
135
+ ## Contributing
136
+
137
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rameerez/trackdown. Our code of conduct is: just be nice and make your mom proud of what you do and post online.
138
+
139
+ ## License
140
+
141
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,25 @@
1
+ module Trackdown
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ def create_initializer
7
+ template 'trackdown.rb', 'config/initializers/trackdown.rb'
8
+ end
9
+
10
+ def create_database_refresh_job
11
+ template 'trackdown_database_refresh_job.rb', 'app/jobs/trackdown_database_refresh_job.rb'
12
+ end
13
+
14
+ def display_post_install_message
15
+ say "\tThe `trackdown` gem has been successfully installed!", :green
16
+ say "\nTo complete the setup:"
17
+ say " 1. Configure your MaxMind credentials in `config/initializers/trackdown.rb`"
18
+ say " 2. Run 'Trackdown.update_database' to get a fresh MaxMind IP database."
19
+ say " 3. Make sure you configure your queueing system to run the TrackdownDatabaseRefreshJob regularly so the IP database is updated regularly."
20
+ say "\nEnjoy `trackdown`!", :green
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Trackdown.configure do |config|
4
+ # Required: Your MaxMind credentials
5
+ config.maxmind_account_id = Rails.application.credentials.dig(:maxmind, :account_id)
6
+ config.maxmind_license_key = Rails.application.credentials.dig(:maxmind, :license_key)
7
+
8
+ # Optional: Configure database location (defaults to db/GeoLite2-City.mmdb)
9
+ # config.database_path = Rails.root.join('db', 'GeoLite2-City.mmdb').to_s
10
+
11
+ # Optional: Configure timeouts and pooling (defaults shown)
12
+ # config.timeout = 3 # Timeout for individual lookups
13
+ # config.pool_size = 5 # Size of the connection pool
14
+ # config.pool_timeout = 3 # Timeout when waiting for a connection from the pool
15
+
16
+ # Optional: Configure memory mode (defaults to MODE_MEMORY)
17
+ # config.memory_mode = MaxMind::DB::MODE_FILE # Use MODE_FILE to reduce memory usage
18
+
19
+ # Optional: Configure IP validation (defaults to true)
20
+ # config.reject_private_ips = true # Reject private/local IP addresses
21
+ end
@@ -0,0 +1,7 @@
1
+ class TrackdownDatabaseRefreshJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(*args)
5
+ Trackdown.update_database
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackdown
4
+ class Configuration
5
+ attr_accessor :maxmind_license_key, :maxmind_account_id, :database_path,
6
+ :timeout, :pool_size, :pool_timeout, :memory_mode,
7
+ :reject_private_ips
8
+
9
+ def initialize
10
+ @maxmind_license_key = nil
11
+ @maxmind_account_id = nil
12
+ @database_path = defined?(Rails) ? Rails.root.join('db', 'GeoLite2-City.mmdb').to_s : 'db/GeoLite2-City.mmdb'
13
+ @timeout = 3 # seconds
14
+ @pool_size = 5
15
+ @pool_timeout = 3 # seconds
16
+ @memory_mode = MaxMind::DB::MODE_MEMORY
17
+ @reject_private_ips = true
18
+ end
19
+
20
+ def validate!
21
+ missing = []
22
+ missing << 'maxmind_license_key' if maxmind_license_key.nil?
23
+ missing << 'maxmind_account_id' if maxmind_account_id.nil?
24
+
25
+ raise Error, "Missing required configuration: #{missing.join(', ')} – Please set these in your config/initializers/trackdown.rb initializer." unless missing.empty?
26
+
27
+ validate_paths!
28
+ validate_timeouts!
29
+ end
30
+
31
+ def reject_private_ips?
32
+ @reject_private_ips
33
+ end
34
+
35
+ private
36
+
37
+ def validate_paths!
38
+ unless database_path && !database_path.empty?
39
+ raise Error, "database_path cannot be empty"
40
+ end
41
+ end
42
+
43
+ def validate_timeouts!
44
+ raise Error, "timeout must be positive" unless timeout.positive?
45
+ raise Error, "pool_timeout must be positive" unless pool_timeout.positive?
46
+ raise Error, "pool_size must be positive" unless pool_size.positive?
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ require 'open-uri'
2
+ require 'zlib'
3
+ require 'rubygems/package'
4
+
5
+ module Trackdown
6
+ class DatabaseUpdater
7
+ DOWNLOAD_URL = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%{license_key}&suffix=tar.gz"
8
+
9
+ class << self
10
+ def update
11
+ download_url = DOWNLOAD_URL % { license_key: Trackdown.configuration.maxmind_license_key }
12
+
13
+ options = {
14
+ http_basic_authentication: [
15
+ Trackdown.configuration.maxmind_account_id.to_s,
16
+ Trackdown.configuration.maxmind_license_key.to_s
17
+ ],
18
+ ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER
19
+ }
20
+
21
+ URI.open(download_url, **options) do |remote_file|
22
+ Zlib::GzipReader.wrap(remote_file) do |gz|
23
+ Gem::Package::TarReader.new(gz) do |tar|
24
+ tar.each do |entry|
25
+ if entry.full_name.end_with?('.mmdb')
26
+ FileUtils.mkdir_p(File.dirname(Trackdown.configuration.database_path))
27
+
28
+ File.open(Trackdown.configuration.database_path, 'wb') do |file|
29
+ file.write(entry.read)
30
+ end
31
+ break
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ Rails.logger.info("MaxMind database updated successfully") if defined?(Rails)
39
+ true
40
+ rescue OpenURI::HTTPError => e
41
+ message = case e.message
42
+ when /401/
43
+ "Authentication failed. Please check your MaxMind account ID and license key."
44
+ when /403/
45
+ "Access forbidden. Your MaxMind license may not have access to this database."
46
+ else
47
+ "HTTP Error: #{e.message}"
48
+ end
49
+ Rails.logger.error("Error updating MaxMind database: #{message}") if defined?(Rails)
50
+ raise Error, message
51
+ rescue => e
52
+ Rails.logger.error("Error updating MaxMind database: #{e.message}") if defined?(Rails)
53
+ raise Error, "Failed to update database: #{e.message}"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackdown
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/db'
4
+ require 'connection_pool'
5
+ require_relative 'location_result'
6
+ require_relative 'ip_validator'
7
+
8
+ module Trackdown
9
+ class IpLocator
10
+ class TimeoutError < Trackdown::Error; end
11
+ class DatabaseError < Trackdown::Error; end
12
+
13
+ class << self
14
+ def locate(ip)
15
+ IpValidator.validate!(ip)
16
+
17
+ if Trackdown.configuration.reject_private_ips? && IpValidator.private_ip?(ip)
18
+ raise IpValidator::InvalidIpError, "Private IP addresses are not allowed"
19
+ end
20
+
21
+ record = fetch_record(ip)
22
+ return LocationResult.new(nil, 'Unknown', 'Unknown', '🏳️') if record.nil?
23
+
24
+ country_code = extract_country_code(record)
25
+ country_name = extract_country_name(record)
26
+ city = extract_city(record)
27
+ flag_emoji = get_emoji_flag(country_code)
28
+
29
+ LocationResult.new(country_code, country_name, city, flag_emoji)
30
+ end
31
+
32
+ private
33
+
34
+ def fetch_record(ip)
35
+ Trackdown.ensure_database_exists!
36
+
37
+ Timeout.timeout(Trackdown.configuration.timeout) do
38
+ reader_pool.with do |reader|
39
+ reader.get(ip)
40
+ end
41
+ end
42
+ rescue Timeout::Error
43
+ raise TimeoutError, "MaxMind database lookup timed out after #{Trackdown.configuration.timeout} seconds"
44
+ rescue Trackdown::Error => e
45
+ raise e
46
+ rescue StandardError => e
47
+ Rails.logger.error("Error fetching IP data: #{e.message}") if defined?(Rails)
48
+ raise DatabaseError, "Database error: #{e.message}"
49
+ end
50
+
51
+ def reader_pool
52
+ @reader_pool ||= ConnectionPool.new(
53
+ size: Trackdown.configuration.pool_size,
54
+ timeout: Trackdown.configuration.pool_timeout
55
+ ) do
56
+ MaxMind::DB.new(
57
+ Trackdown.configuration.database_path,
58
+ mode: Trackdown.configuration.memory_mode
59
+ )
60
+ end
61
+ end
62
+
63
+ def extract_country_code(record)
64
+ record&.dig('country', 'iso_code')
65
+ end
66
+
67
+ def extract_country_name(record)
68
+ record&.dig('country', 'names', 'en') ||
69
+ (record&.dig('country', 'names')&.values&.first) ||
70
+ 'Unknown'
71
+ end
72
+
73
+ def extract_city(record)
74
+ record&.dig('city', 'names', 'en') ||
75
+ (record&.dig('city', 'names')&.values&.first) ||
76
+ 'Unknown'
77
+ end
78
+
79
+ def get_emoji_flag(country_code)
80
+ country_code ? country_code.tr('A-Z', "\u{1F1E6}-\u{1F1FF}") : "🏳️"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ipaddr'
4
+
5
+ module Trackdown
6
+ class IpValidator
7
+ class InvalidIpError < Trackdown::Error; end
8
+
9
+ def self.validate!(ip)
10
+ return if ip.nil?
11
+
12
+ begin
13
+ IPAddr.new(ip.to_s)
14
+ rescue IPAddr::InvalidAddressError
15
+ raise InvalidIpError, "Invalid IP address format: #{ip}"
16
+ end
17
+ end
18
+
19
+ def self.private_ip?(ip)
20
+ addr = IPAddr.new(ip.to_s)
21
+ addr.private? || addr.loopback?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'countries'
4
+
5
+ module Trackdown
6
+ class LocationResult
7
+ attr_reader :country_code, :country_name, :city, :flag_emoji
8
+
9
+ def initialize(country_code, country_name, city, flag_emoji)
10
+ @country_code = country_code
11
+ @country_name = country_name
12
+ @city = city
13
+ @flag_emoji = flag_emoji
14
+ end
15
+
16
+ alias_method :country, :country_name
17
+ alias_method :emoji, :flag_emoji
18
+ alias_method :emoji_flag, :flag_emoji
19
+ alias_method :country_flag, :flag_emoji
20
+
21
+ def country_info
22
+ return nil unless country_code
23
+ ISO3166::Country.new(country_code)
24
+ end
25
+
26
+ def to_h
27
+ {
28
+ country_code: @country_code,
29
+ country_name: @country_name,
30
+ city: @city,
31
+ flag_emoji: @flag_emoji,
32
+ country_info: country_info&.data || {}
33
+ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackdown
4
+ VERSION = "0.1.0"
5
+ end
data/lib/trackdown.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "trackdown/error"
4
+ require_relative "trackdown/version"
5
+ require_relative "trackdown/configuration"
6
+ require_relative "trackdown/ip_validator"
7
+ require_relative "trackdown/ip_locator"
8
+ require_relative "trackdown/database_updater"
9
+ require_relative "trackdown/location_result"
10
+
11
+ module Trackdown
12
+ class << self
13
+ attr_writer :configuration
14
+ end
15
+
16
+ def self.configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def self.configure
21
+ yield(configuration)
22
+ configuration.validate!
23
+ end
24
+
25
+ def self.locate(ip)
26
+ ensure_database_exists!
27
+ IpLocator.locate(ip)
28
+ end
29
+
30
+ def self.update_database
31
+ DatabaseUpdater.update
32
+ end
33
+
34
+ def self.database_exists?
35
+ File.exist?(configuration.database_path)
36
+ end
37
+
38
+ def self.ensure_database_exists!
39
+ unless database_exists?
40
+ raise Error, "MaxMind database not found. Please set your MaxMind keys in config/initializers/trackdown.rb as described in the `trackdown` gem README, and then run Trackdown.update_database to download the MaxMind IP geolocation database."
41
+ end
42
+ end
43
+ end
data/sig/trackdown.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Trackdown
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trackdown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - rameerez
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: maxmind-db
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: connection_pool
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: countries
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '7.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '7.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.21'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.21'
97
+ description: Trackdown is a Ruby gem that easily allows you to geolocate IP addresses.
98
+ It's a simple, convenient wrapper on top of the MaxMind database. Plug your MaxMind
99
+ license key and you're good to go. It keeps your MaxMind database updated regularly,
100
+ and it offers a handy API for Rails applications to fetch country, city, and emoji
101
+ flag information for any IP address.
102
+ email:
103
+ - rubygems@rameerez.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - CHANGELOG.md
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - lib/generators/trackdown/install_generator.rb
113
+ - lib/generators/trackdown/templates/trackdown.rb
114
+ - lib/generators/trackdown/templates/trackdown_database_refresh_job.rb
115
+ - lib/trackdown.rb
116
+ - lib/trackdown/configuration.rb
117
+ - lib/trackdown/database_updater.rb
118
+ - lib/trackdown/error.rb
119
+ - lib/trackdown/ip_locator.rb
120
+ - lib/trackdown/ip_validator.rb
121
+ - lib/trackdown/location_result.rb
122
+ - lib/trackdown/version.rb
123
+ - sig/trackdown.rbs
124
+ homepage: https://github.com/rameerez/trackdown
125
+ licenses:
126
+ - MIT
127
+ metadata:
128
+ allowed_push_host: https://rubygems.org
129
+ homepage_uri: https://github.com/rameerez/trackdown
130
+ source_code_uri: https://github.com/rameerez/trackdown
131
+ changelog_uri: https://github.com/rameerez/trackdown/blob/main/CHANGELOG.md
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: 3.0.0
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 3.5.16
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Get country, city, and emoji flag information for IP addresses using a MaxMind
151
+ database
152
+ test_files: []