ip2geo 0.0.1

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: 3cdf86cb50c5d08c734ea15c78ec62d0f218dae67ccdb074b9b889fb0c77681f
4
+ data.tar.gz: ed6420fd62687669cf430d6525b6415326a595cd60e4fae6a65ce0aae268e182
5
+ SHA512:
6
+ metadata.gz: 2a7f9f99b598e068ccff78fb2ba2199835a2db6ee086f7871228fcd8e785d321b72b7187bc195a77589acbb6a7f7f170e1d3410044355b913d9e41f2766ac0e0
7
+ data.tar.gz: 190301f6be0572c93262611ec8cc2ee6f4ec631c9c810755777076daa379bc87ab22eaa5f543ef959584acd46c4ab54f3529d8c6218c1acb0c70917c55bc40c7
data/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) 2024 Ip2Geo. All rights reserved.
2
+
3
+ This software is proprietary and confidential. Unauthorized copying, distribution,
4
+ modification, or use of this software, via any medium, is strictly prohibited.
5
+
6
+ No license, express or implied, is granted to any person or entity for any purpose.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ <div align='center'>
2
+ <a href='https://axios-http.com'>
3
+ <img
4
+ src='https://cdn-prod.ip2geo.dev/assets/npm/generals/ip2geo.svg'
5
+ alt='Ip2Geo Logo'
6
+ />
7
+ </a>
8
+
9
+ <br />
10
+ </div>
11
+
12
+ <p align='center'>
13
+ Your go-to IP-to-Geo cloud service — fast, accurate, and built for developers. Start free with 2,000 monthly stored conversions.
14
+ </p>
15
+
16
+ <p align='center'>
17
+ <a href='https://ip2geo.dev'>
18
+ <b>
19
+ Website
20
+ </b>
21
+ </a>
22
+
23
+ <a href='https://app.ip2geo.dev'>
24
+ <b>
25
+ Dashboard
26
+ </b>
27
+ </a>
28
+
29
+ <a href='https://docs.ip2geo.dev'>
30
+ <b>
31
+ Documentation
32
+ </b>
33
+ </a>
34
+
35
+ <a href='https://status.ip2geo.dev'>
36
+ <b>
37
+ Services Status
38
+ </b>
39
+ </a>
40
+ </p>
41
+
42
+ <br />
43
+
44
+ ## Intro to the Ruby Gem
45
+
46
+ This gem provides a lightweight way to integrate Ip2Geo into your Ruby applications.
47
+
48
+ ### Installing
49
+ ```bash
50
+ gem install ip2geo
51
+ ```
52
+
53
+ ### Initializing
54
+ ```ruby
55
+ require 'ip2geo'
56
+
57
+ Ip2Geo.init(ENV['IP2GEO_API_KEY'])
58
+ ```
59
+
60
+ ### Using
61
+ ```ruby
62
+ converted_ip = Ip2Geo.convert_ip(ip: '8.8.8.8')
63
+
64
+ converted_ips = Ip2Geo.convert_ips(
65
+ ips: [
66
+ '8.8.8.8',
67
+ '1.1.1.1',
68
+ '9.9.9.9',
69
+ '64.6.64.6'
70
+ ]
71
+ )
72
+
73
+ # {
74
+ # 'success' => true,
75
+ # 'code' => 200,
76
+ # 'message' => 'Success',
77
+ # 'data' => { ... },
78
+ # '_req' => {
79
+ # 'reqId' => 'string',
80
+ # 'resTime' => 123
81
+ # }
82
+ # }
83
+
84
+ # {
85
+ # 'success' => true,
86
+ # 'code' => 200,
87
+ # 'message' => 'Success',
88
+ # 'data' => [
89
+ # {
90
+ # 'ip' => '8.8.8.8',
91
+ # 'conversion' => { ... }
92
+ # }
93
+ # ],
94
+ # '_req' => {
95
+ # 'reqId' => 'string',
96
+ # 'resTime' => 123
97
+ # }
98
+ # }
99
+ ```
data/ip2geo.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'ip2geo'
5
+ spec.version = '0.0.1'
6
+ spec.authors = ['Ip2Geo']
7
+ spec.email = ['support@ip2geo.dev']
8
+
9
+ spec.summary = 'Official Ruby SDK for Ip2Geo - IP to Geolocation API.'
10
+ spec.description = 'A lightweight Ruby client for the Ip2Geo cloud service. Get accurate geolocation data for IP addresses including continent, country, city, timezone, ASN, and more.'
11
+ spec.homepage = 'https://ip2geo.dev'
12
+ spec.required_ruby_version = '>= 2.7.0'
13
+
14
+ spec.license = 'Nonstandard'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['changelog_uri'] = 'https://docs.ip2geo.dev/changelog'
18
+ spec.metadata['documentation_uri'] = 'https://docs.ip2geo.dev'
19
+
20
+ spec.files = Dir.glob('lib/**/*') + ['ip2geo.gemspec', 'README.md', 'LICENSE']
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'json', '~> 2.0'
24
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Data
5
+ API_ENDPOINT = 'https://api.ip2geo.dev'
6
+ DOCS_URL = 'https://docs.ip2geo.dev'
7
+ RUBYGEMS_URL = 'https://rubygems.org/api/v1/gems/ip2geo.json'
8
+
9
+ IP_TYPES = {
10
+ IPV4: 'ipv4',
11
+ IPV6: 'ipv6'
12
+ }.freeze
13
+
14
+ HTTP_METHODS = {
15
+ ALL: 'all',
16
+ GET: 'GET',
17
+ POST: 'POST',
18
+ DELETE: 'DELETE',
19
+ OPTIONS: 'OPTIONS'
20
+ }.freeze
21
+
22
+ HEADER_KEYS = {
23
+ X_API_KEY: 'X-Api-Key'
24
+ }.freeze
25
+
26
+ FILE_TYPES = {
27
+ JSON: 'json'
28
+ }.freeze
29
+
30
+ FILE_FORMATS = {
31
+ APPLICATION_JSON: 'application/json'
32
+ }.freeze
33
+
34
+ HEADER_TYPES = {
35
+ CONTENT_TYPE: 'Content-Type'
36
+ }.freeze
37
+
38
+ ENDPOINTS = {
39
+ CONVERT: '/convert',
40
+ CONVERSIONS: {
41
+ GET: '/conversions/get',
42
+ LIST: '/conversions/list'
43
+ }.freeze
44
+ }.freeze
45
+
46
+ SELECT = {
47
+ CONVERSION: {
48
+ ID: 'id',
49
+ UNIQUE_ID: 'uniqueId',
50
+ DATA: 'data',
51
+ STATUS: 'status',
52
+ STARTED_AT: 'startedAt',
53
+ COMPLETED_AT: 'completedAt',
54
+ CREATED_AT: 'createdAt',
55
+ CREDITED: 'credited',
56
+ CONVERT_ONLY: 'convertOnly'
57
+ }.freeze,
58
+ DATA: {
59
+ IP: 'data.ip',
60
+ TYPE: 'data.type',
61
+ IS_EU: 'data.is_eu',
62
+ CONTINENT: {
63
+ NAME: 'data.continent.name',
64
+ CODE: 'data.continent.code',
65
+ GEONAME_ID: 'data.continent.geoname_id',
66
+ COUNTRY: {
67
+ NAME: 'data.continent.country.name',
68
+ CODE: 'data.continent.country.code',
69
+ GEONAME_ID: 'data.continent.country.geoname_id',
70
+ PHONE_CODE: 'data.continent.country.phone_code',
71
+ CAPITAL: 'data.continent.country.capital',
72
+ TLD: 'data.continent.country.tld',
73
+ SUBDIVISION: {
74
+ NAME: 'data.continent.country.subdivision.name',
75
+ CODE: 'data.continent.country.subdivision.code'
76
+ }.freeze,
77
+ CITY: {
78
+ NAME: 'data.continent.country.city.name',
79
+ GEONAME_ID: 'data.continent.country.city.geoname_id',
80
+ LATITUDE: 'data.continent.country.city.latitude',
81
+ LONGITUDE: 'data.continent.country.city.longitude',
82
+ ACCURACY_RADIUS: 'data.continent.country.city.accuracy_radius',
83
+ METRO_CODE: 'data.continent.country.city.metro_code',
84
+ POSTAL_CODE: 'data.continent.country.city.postal_code',
85
+ TIMEZONE: {
86
+ NAME: 'data.continent.country.city.timezone.name',
87
+ TIME_NOW: 'data.continent.country.city.timezone.time_now'
88
+ }.freeze
89
+ }.freeze,
90
+ FLAG: {
91
+ IMG: 'data.continent.country.flag.img',
92
+ EMOJI: 'data.continent.country.flag.emoji',
93
+ EMOJI_UNICODE: 'data.continent.country.flag.emoji_unicode'
94
+ }.freeze,
95
+ CURRENCY: {
96
+ NAME: 'data.continent.country.currency.name',
97
+ CODE: 'data.continent.country.currency.code',
98
+ SYMBOL: 'data.continent.country.currency.symbol'
99
+ }.freeze
100
+ }.freeze
101
+ }.freeze,
102
+ REGISTERED_COUNTRY: {
103
+ NAME: 'data.registered_country.name',
104
+ CODE: 'data.registered_country.code',
105
+ GEONAME_ID: 'data.registered_country.geoname_id'
106
+ }.freeze,
107
+ ASN: {
108
+ NUMBER: 'data.asn.number',
109
+ NAME: 'data.asn.name'
110
+ }.freeze,
111
+ COMPLETION_TIME: {
112
+ MILISECONDS: 'data.completion_time.miliseconds',
113
+ SECONDS: 'data.completion_time.seconds'
114
+ }.freeze
115
+ }.freeze
116
+ }.freeze
117
+ end
118
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Data
5
+ MESSAGES = {
6
+ conversions: {
7
+ 'ip-address-is-required-to-convert-it' => 'IP address is required to convert it.',
8
+ 'invalid-ip-address-so-we-could-not-convert-it' => 'Invalid IP address, so we could not convert it.',
9
+ 'ip-addresses-are-required-to-convert-them' => 'IP addresses are required to convert them.',
10
+ 'all-ip-addresses-are-invalid' => 'All IP addresses are invalid.',
11
+ 'some-ip-addresses-are-invalid' => 'Some IP addresses are invalid.',
12
+ 'conversion-id-is-required' => 'Conversion ID is required.',
13
+ 'conversion-ids-are-required' => 'Conversion IDs are required.'
14
+ }
15
+ }.freeze
16
+
17
+ def self.translate(key)
18
+ keys = key.split('.')
19
+ result = MESSAGES
20
+
21
+ keys.each do |k|
22
+ result = result[k.to_sym] || result[k]
23
+ return key if result.nil?
24
+ end
25
+
26
+ result
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Data
5
+ class State
6
+ class << self
7
+ attr_accessor :auth_key, :version_update_message, :is_version_checked
8
+
9
+ def reset!
10
+ @auth_key = nil
11
+ @version_update_message = true
12
+ @is_version_checked = false
13
+ end
14
+ end
15
+
16
+ reset!
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module Ip2Geo
7
+ module Helpers
8
+ class CheckVersion
9
+ RUBYGEMS_API_URL = 'https://rubygems.org/api/v1/gems/ip2geo.json'
10
+
11
+ class << self
12
+ def call
13
+ return if Data::State.is_version_checked || !Data::State.version_update_message
14
+
15
+ Data::State.is_version_checked = true
16
+
17
+ current_version = Ip2Geo::VERSION
18
+ latest_version = fetch_latest_version
19
+
20
+ return unless current_version && latest_version
21
+ return if current_version == latest_version
22
+
23
+ display_update_message(current_version, latest_version)
24
+ rescue StandardError
25
+ # Silently fail - version check is non-critical
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_latest_version
31
+ uri = URI.parse(RUBYGEMS_API_URL)
32
+ http = Net::HTTP.new(uri.host, uri.port)
33
+ http.use_ssl = true
34
+ http.open_timeout = 5
35
+ http.read_timeout = 5
36
+
37
+ request = Net::HTTP::Get.new(uri.request_uri)
38
+ response = http.request(request)
39
+
40
+ return nil unless response.is_a?(Net::HTTPSuccess)
41
+
42
+ data = JSON.parse(response.body)
43
+ data['version']
44
+ rescue StandardError
45
+ nil
46
+ end
47
+
48
+ def display_update_message(current_version, latest_version)
49
+ box_width = 70
50
+ top_border = "\u250C#{"\u2500" * (box_width - 2)}\u2510"
51
+ bottom_border = "\u2514#{"\u2500" * (box_width - 2)}\u2518"
52
+
53
+ lines = [
54
+ '',
55
+ top_border,
56
+ center_text("\e[1m\e[33mUpdate available\e[0m \e[2m#{current_version} \u2192 #{latest_version}\e[0m", box_width),
57
+ center_text("Changelog: \e[36mhttps://docs.ip2geo.dev/sdk/changelogs\e[0m", box_width),
58
+ center_text("Run \e[32m`gem update ip2geo`\e[0m to update.", box_width),
59
+ center_text('', box_width),
60
+ center_text("Disable this warning using: \e[36mversion_update_message: false\e[0m on init.", box_width),
61
+ bottom_border,
62
+ ''
63
+ ]
64
+
65
+ lines.each { |line| warn line }
66
+ end
67
+
68
+ def center_text(text, box_width)
69
+ visible_length = text.gsub(/\e\[[0-9;]*m/, '').length
70
+ padding = [(box_width - 2 - visible_length) / 2, 0].max
71
+ right_padding = [box_width - 2 - padding - visible_length, 0].max
72
+
73
+ "\u2502#{' ' * padding}#{text}#{' ' * right_padding}\u2502"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'openssl'
7
+
8
+ module Ip2Geo
9
+ module Helpers
10
+ class Http
11
+ class << self
12
+ def request(endpoint, body = nil)
13
+ uri = URI.parse(endpoint)
14
+ http = Net::HTTP.new(uri.host, uri.port)
15
+ http.use_ssl = uri.scheme == 'https'
16
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
17
+ http.ssl_version = :TLSv1_2
18
+ http.open_timeout = 30
19
+ http.read_timeout = 30
20
+
21
+ # Fix for macOS certificate CRL verification issues
22
+ cert_store = OpenSSL::X509::Store.new
23
+ cert_store.set_default_paths
24
+ http.cert_store = cert_store
25
+
26
+ headers = {
27
+ Data::HEADER_TYPES[:CONTENT_TYPE] => Data::FILE_FORMATS[:APPLICATION_JSON],
28
+ Data::HEADER_KEYS[:X_API_KEY] => Data::State.auth_key
29
+ }
30
+
31
+ if body && !body.empty?
32
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
33
+ request.body = body.to_json
34
+ else
35
+ request = Net::HTTP::Get.new(uri.request_uri, headers)
36
+ end
37
+
38
+ response = http.request(request)
39
+ JSON.parse(response.body, symbolize_names: false)
40
+ rescue StandardError => e
41
+ warn "[Ip2Geo::Http] Error: #{e.message}"
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Helpers
5
+ class IpValidation
6
+ IPV4_REGEX = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.freeze
7
+ IPV6_REGEX = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,7}:$|^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6}|:)$|^:((:[0-9a-fA-F]{1,4}){1,7}|:)$/.freeze
8
+
9
+ class << self
10
+ # Validate an IP address and return if it is IPv4 or IPv6.
11
+ #
12
+ # @param ip_address [String] The IP address to validate
13
+ # @return [Hash] A hash with :ip4 and :ip6 boolean values
14
+ def validate(ip_address)
15
+ return { ip4: false, ip6: false } unless ip_address.is_a?(String)
16
+
17
+ {
18
+ ip4: IPV4_REGEX.match?(ip_address),
19
+ ip6: IPV6_REGEX.match?(ip_address)
20
+ }
21
+ rescue StandardError => e
22
+ warn "[Ip2Geo::IpValidation] Error: #{e.message}"
23
+ { ip4: false, ip6: false }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class ConvertIP
6
+ class << self
7
+ # Get geo information from an IP address.
8
+ #
9
+ # @param options [Hash] The options for the conversion
10
+ # @option options [String] :ip The IP address to retrieve geo information for (required)
11
+ # @option options [Boolean] :convert_only If true, stores minimal data and charges $0.001 per conversion
12
+ # instead of $0.0005 (default: false)
13
+ # @return [Hash, nil] The API response containing geo data, or nil if the request fails
14
+ def call(options = {})
15
+ ip = options[:ip]
16
+ convert_only = options.fetch(:convert_only, false)
17
+
18
+ unless ip
19
+ return error_response(
20
+ Data.translate('conversions.ip-address-is-required-to-convert-it')
21
+ )
22
+ end
23
+
24
+ validation = Helpers::IpValidation.validate(ip)
25
+
26
+ unless validation[:ip4] || validation[:ip6]
27
+ return error_response(
28
+ Data.translate('conversions.invalid-ip-address-so-we-could-not-convert-it')
29
+ )
30
+ end
31
+
32
+ url = "#{Data::API_ENDPOINT}#{Data::ENDPOINTS[:CONVERT]}?ip=#{ip}&convertOnly=#{convert_only}"
33
+ Helpers::Http.request(url)
34
+ rescue StandardError => e
35
+ warn "[Ip2Geo::ConvertIP] Error: #{e.message}"
36
+ nil
37
+ end
38
+
39
+ private
40
+
41
+ def error_response(message)
42
+ {
43
+ 'success' => false,
44
+ 'message' => message,
45
+ 'code' => 400,
46
+ 'data' => nil,
47
+ '_req' => {
48
+ 'reqId' => nil,
49
+ 'resTime' => 0
50
+ }
51
+ }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class ConvertIPs
6
+ class << self
7
+ # Get geo information from multiple IP addresses.
8
+ #
9
+ # @param options [Hash] The options for the conversion
10
+ # @option options [Array<String>] :ips An array of IP addresses to retrieve geo information for (required)
11
+ # @option options [Boolean] :convert_only If true, stores minimal data and charges $0.001 per conversion
12
+ # instead of $0.0005 (default: false)
13
+ # @return [Hash, nil] The API response containing geo data for all IPs, or nil if the request fails
14
+ def call(options = {})
15
+ ips = options[:ips]
16
+ convert_only = options.fetch(:convert_only, false)
17
+
18
+ unless ips.is_a?(Array) && !ips.empty?
19
+ return error_response(
20
+ Data.translate('conversions.ip-addresses-are-required-to-convert-them'),
21
+ nil
22
+ )
23
+ end
24
+
25
+ valid_ips = []
26
+ invalid_ips = []
27
+
28
+ ips.each do |ip|
29
+ validation = Helpers::IpValidation.validate(ip)
30
+ if validation[:ip4] || validation[:ip6]
31
+ valid_ips << ip
32
+ else
33
+ invalid_ips << ip
34
+ end
35
+ end
36
+
37
+ if invalid_ips.any?
38
+ all_invalid = invalid_ips.length == ips.length
39
+ message = all_invalid ? 'conversions.all-ip-addresses-are-invalid' : 'conversions.some-ip-addresses-are-invalid'
40
+
41
+ return error_response(
42
+ Data.translate(message),
43
+ invalid_ips.map { |ip| { 'ip' => ip, 'conversion' => nil } }
44
+ )
45
+ end
46
+
47
+ url = "#{Data::API_ENDPOINT}#{Data::ENDPOINTS[:CONVERT]}"
48
+ body = { ips: ips, convertOnly: convert_only }
49
+
50
+ Helpers::Http.request(url, body)
51
+ rescue StandardError => e
52
+ warn "[Ip2Geo::ConvertIPs] Error: #{e.message}"
53
+ nil
54
+ end
55
+
56
+ private
57
+
58
+ def error_response(message, data)
59
+ {
60
+ 'success' => false,
61
+ 'message' => message,
62
+ 'code' => 400,
63
+ 'data' => data,
64
+ '_req' => {
65
+ 'reqId' => nil,
66
+ 'resTime' => 0
67
+ }
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class GetConversion
6
+ class << self
7
+ # Retrieve a single conversion by its ID.
8
+ #
9
+ # @param options [Hash] The options for retrieving the conversion
10
+ # @option options [String] :conversion_id The unique identifier of the conversion to retrieve (required)
11
+ # @option options [Array<String>] :select Optional array of property names to return
12
+ # @return [Hash, nil] The API response containing conversion data, or nil if the request fails
13
+ def call(options = {})
14
+ conversion_id = options[:conversion_id]
15
+ select = options[:select]
16
+
17
+ unless conversion_id
18
+ return error_response(
19
+ Data.translate('conversions.conversion-id-is-required')
20
+ )
21
+ end
22
+
23
+ params = []
24
+ params << "select=#{select.join(',')}" if select.is_a?(Array) && !select.empty?
25
+
26
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
27
+ url = "#{Data::API_ENDPOINT}#{Data::ENDPOINTS[:CONVERSIONS][:GET]}#{query_string}"
28
+
29
+ Helpers::Http.request(url, { conversionId: conversion_id })
30
+ rescue StandardError => e
31
+ warn "[Ip2Geo::GetConversion] Error: #{e.message}"
32
+ nil
33
+ end
34
+
35
+ private
36
+
37
+ def error_response(message)
38
+ {
39
+ 'success' => false,
40
+ 'message' => message,
41
+ 'code' => 400,
42
+ 'data' => nil,
43
+ '_req' => {
44
+ 'reqId' => nil,
45
+ 'resTime' => 0
46
+ }
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class GetConversions
6
+ class << self
7
+ # Retrieve multiple conversions by their IDs.
8
+ #
9
+ # @param options [Hash] The options for retrieving conversions
10
+ # @option options [Array<String>] :conversion_ids An array of unique identifiers to retrieve (required)
11
+ # @option options [Array<String>] :select Optional array of property names to return
12
+ # @return [Hash, nil] The API response containing conversions data, or nil if the request fails
13
+ def call(options = {})
14
+ conversion_ids = options[:conversion_ids]
15
+ select = options[:select]
16
+
17
+ unless conversion_ids.is_a?(Array) && !conversion_ids.empty?
18
+ return error_response(
19
+ Data.translate('conversions.conversion-ids-are-required')
20
+ )
21
+ end
22
+
23
+ params = []
24
+ params << "select=#{select.join(',')}" if select.is_a?(Array) && !select.empty?
25
+
26
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
27
+ url = "#{Data::API_ENDPOINT}#{Data::ENDPOINTS[:CONVERSIONS][:GET]}#{query_string}"
28
+
29
+ Helpers::Http.request(url, { conversionIds: conversion_ids })
30
+ rescue StandardError => e
31
+ warn "[Ip2Geo::GetConversions] Error: #{e.message}"
32
+ nil
33
+ end
34
+
35
+ private
36
+
37
+ def error_response(message)
38
+ {
39
+ 'success' => false,
40
+ 'message' => message,
41
+ 'code' => 400,
42
+ 'data' => nil,
43
+ '_req' => {
44
+ 'reqId' => nil,
45
+ 'resTime' => 0
46
+ }
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class Init
6
+ class << self
7
+ # Initializes the SDK with the given API key.
8
+ #
9
+ # @param key [String] The API key used to authenticate with Ip2Geo
10
+ # @param options [Hash] Optional configuration
11
+ # @option options [Boolean] :version_update_message Whether to display version update messages (default: true)
12
+ # @return [Boolean] Whether the initialization was successful
13
+ def call(key, options = {})
14
+ Data::State.auth_key = key
15
+ Data::State.version_update_message = options.fetch(:version_update_message, true)
16
+
17
+ Helpers::CheckVersion.call
18
+
19
+ true
20
+ rescue StandardError => e
21
+ Data::State.reset!
22
+ warn "[Ip2Geo::Init] Error: #{e.message}"
23
+ false
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2Geo
4
+ module Methods
5
+ class ListConversions
6
+ class << self
7
+ # List conversions with pagination and optional filtering.
8
+ #
9
+ # @param options [Hash] The options for listing conversions
10
+ # @option options [Integer] :offset The number of conversions to skip (default: 0)
11
+ # @option options [Integer] :limit The maximum number of conversions to return (default: 50, max: 50)
12
+ # @option options [Array<String>] :select Optional array of property names to return
13
+ # @option options [String] :ip_search Filter conversions by IP address
14
+ # @return [Hash, nil] The API response containing conversions list with pagination info, or nil if fails
15
+ def call(options = {})
16
+ offset = options.fetch(:offset, 0)
17
+ limit = options.fetch(:limit, 50)
18
+ select = options[:select]
19
+ ip_search = options[:ip_search]
20
+
21
+ params = []
22
+ params << "offset=#{offset}" if offset.positive?
23
+ params << "limit=#{limit}" if limit != 50
24
+ params << "select=#{select.join(',')}" if select.is_a?(Array) && !select.empty?
25
+ params << "ipSearch=#{ip_search}" if ip_search
26
+
27
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
28
+ url = "#{Data::API_ENDPOINT}#{Data::ENDPOINTS[:CONVERSIONS][:LIST]}#{query_string}"
29
+
30
+ Helpers::Http.request(url)
31
+ rescue StandardError => e
32
+ warn "[Ip2Geo::ListConversions] Error: #{e.message}"
33
+ nil
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/ip2geo.rb ADDED
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ip2Geo Ruby SDK
4
+ # Official Ruby SDK for Ip2Geo - IP to Geolocation API
5
+ #
6
+ # @example Basic usage
7
+ # require 'ip2geo'
8
+ #
9
+ # # Initialize with your API key
10
+ # Ip2Geo.init('your-api-key')
11
+ #
12
+ # # Convert a single IP address
13
+ # result = Ip2Geo.convert_ip(ip: '8.8.8.8')
14
+ #
15
+ # # Convert multiple IP addresses
16
+ # result = Ip2Geo.convert_ips(ips: ['8.8.8.8', '1.1.1.1'])
17
+ #
18
+ # # List your conversions
19
+ # result = Ip2Geo.list_conversions(limit: 10)
20
+
21
+ require_relative 'ip2geo/data/constants'
22
+ require_relative 'ip2geo/data/states'
23
+ require_relative 'ip2geo/data/messages'
24
+ require_relative 'ip2geo/helpers/http'
25
+ require_relative 'ip2geo/helpers/ip_validation'
26
+ require_relative 'ip2geo/helpers/check_version'
27
+ require_relative 'ip2geo/methods/init'
28
+ require_relative 'ip2geo/methods/convert_ip'
29
+ require_relative 'ip2geo/methods/convert_ips'
30
+ require_relative 'ip2geo/methods/get_conversion'
31
+ require_relative 'ip2geo/methods/get_conversions'
32
+ require_relative 'ip2geo/methods/list_conversions'
33
+
34
+ module Ip2Geo
35
+ VERSION = '0.0.1'
36
+
37
+ class << self
38
+ # Initializes the SDK with the given API key.
39
+ #
40
+ # @param key [String] The API key used to authenticate with Ip2Geo
41
+ # @param options [Hash] Optional configuration
42
+ # @option options [Boolean] :version_update_message Whether to display version update messages (default: true)
43
+ # @return [Boolean] Whether the initialization was successful
44
+ #
45
+ # @example
46
+ # Ip2Geo.init('your-api-key')
47
+ # Ip2Geo.init('your-api-key', version_update_message: false)
48
+ def init(key, options = {})
49
+ Methods::Init.call(key, options)
50
+ end
51
+
52
+ # Get geo information from an IP address.
53
+ #
54
+ # @param options [Hash] The options for the conversion
55
+ # @option options [String] :ip The IP address to retrieve geo information for (required)
56
+ # @option options [Boolean] :convert_only If true, stores minimal data (default: false)
57
+ # @return [Hash, nil] The API response containing geo data
58
+ #
59
+ # @example
60
+ # result = Ip2Geo.convert_ip(ip: '8.8.8.8')
61
+ # result = Ip2Geo.convert_ip(ip: '8.8.8.8', convert_only: true)
62
+ def convert_ip(options = {})
63
+ Methods::ConvertIP.call(options)
64
+ end
65
+
66
+ # Get geo information from multiple IP addresses.
67
+ #
68
+ # @param options [Hash] The options for the conversion
69
+ # @option options [Array<String>] :ips An array of IP addresses (required)
70
+ # @option options [Boolean] :convert_only If true, stores minimal data (default: false)
71
+ # @return [Hash, nil] The API response containing geo data for all IPs
72
+ #
73
+ # @example
74
+ # result = Ip2Geo.convert_ips(ips: ['8.8.8.8', '1.1.1.1'])
75
+ def convert_ips(options = {})
76
+ Methods::ConvertIPs.call(options)
77
+ end
78
+
79
+ # Retrieve a single conversion by its ID.
80
+ #
81
+ # @param options [Hash] The options for retrieving the conversion
82
+ # @option options [String] :conversion_id The unique identifier (required)
83
+ # @option options [Array<String>] :select Optional array of property names to return
84
+ # @return [Hash, nil] The API response containing conversion data
85
+ #
86
+ # @example
87
+ # result = Ip2Geo.get_conversion(conversion_id: 'abc123')
88
+ # result = Ip2Geo.get_conversion(conversion_id: 'abc123', select: ['id', 'data.ip'])
89
+ def get_conversion(options = {})
90
+ Methods::GetConversion.call(options)
91
+ end
92
+
93
+ # Retrieve multiple conversions by their IDs.
94
+ #
95
+ # @param options [Hash] The options for retrieving conversions
96
+ # @option options [Array<String>] :conversion_ids An array of unique identifiers (required)
97
+ # @option options [Array<String>] :select Optional array of property names to return
98
+ # @return [Hash, nil] The API response containing conversions data
99
+ #
100
+ # @example
101
+ # result = Ip2Geo.get_conversions(conversion_ids: ['abc123', 'def456'])
102
+ def get_conversions(options = {})
103
+ Methods::GetConversions.call(options)
104
+ end
105
+
106
+ # List conversions with pagination and optional filtering.
107
+ #
108
+ # @param options [Hash] The options for listing conversions
109
+ # @option options [Integer] :offset The number of conversions to skip (default: 0)
110
+ # @option options [Integer] :limit The maximum number to return (default: 50, max: 50)
111
+ # @option options [Array<String>] :select Optional array of property names to return
112
+ # @option options [String] :ip_search Filter conversions by IP address
113
+ # @return [Hash, nil] The API response containing conversions list with pagination info
114
+ #
115
+ # @example
116
+ # result = Ip2Geo.list_conversions
117
+ # result = Ip2Geo.list_conversions(limit: 10, offset: 20)
118
+ # result = Ip2Geo.list_conversions(ip_search: '8.8.8.8')
119
+ def list_conversions(options = {})
120
+ Methods::ListConversions.call(options)
121
+ end
122
+
123
+ # Validate an IP address and return if it is IPv4 or IPv6.
124
+ #
125
+ # @param ip_address [String] The IP address to validate
126
+ # @return [Hash] A hash with :ip4 and :ip6 boolean values
127
+ #
128
+ # @example
129
+ # result = Ip2Geo.ip_validation('8.8.8.8')
130
+ # # => { ip4: true, ip6: false }
131
+ def ip_validation(ip_address)
132
+ Helpers::IpValidation.validate(ip_address)
133
+ end
134
+
135
+ # SELECT constants for field selection
136
+ #
137
+ # @return [Hash] The SELECT constants
138
+ #
139
+ # @example
140
+ # Ip2Geo.select[:CONVERSION][:ID]
141
+ # # => 'id'
142
+ # Ip2Geo.select[:DATA][:CONTINENT][:COUNTRY][:NAME]
143
+ # # => 'data.continent.country.name'
144
+ def select
145
+ Data::SELECT
146
+ end
147
+ end
148
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ip2geo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ip2Geo
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-01-30 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: json
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ description: A lightweight Ruby client for the Ip2Geo cloud service. Get accurate
27
+ geolocation data for IP addresses including continent, country, city, timezone,
28
+ ASN, and more.
29
+ email:
30
+ - support@ip2geo.dev
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - LICENSE
36
+ - README.md
37
+ - ip2geo.gemspec
38
+ - lib/ip2geo.rb
39
+ - lib/ip2geo/data/constants.rb
40
+ - lib/ip2geo/data/messages.rb
41
+ - lib/ip2geo/data/states.rb
42
+ - lib/ip2geo/helpers/check_version.rb
43
+ - lib/ip2geo/helpers/http.rb
44
+ - lib/ip2geo/helpers/ip_validation.rb
45
+ - lib/ip2geo/methods/convert_ip.rb
46
+ - lib/ip2geo/methods/convert_ips.rb
47
+ - lib/ip2geo/methods/get_conversion.rb
48
+ - lib/ip2geo/methods/get_conversions.rb
49
+ - lib/ip2geo/methods/init.rb
50
+ - lib/ip2geo/methods/list_conversions.rb
51
+ homepage: https://ip2geo.dev
52
+ licenses:
53
+ - Nonstandard
54
+ metadata:
55
+ homepage_uri: https://ip2geo.dev
56
+ changelog_uri: https://docs.ip2geo.dev/changelog
57
+ documentation_uri: https://docs.ip2geo.dev
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 2.7.0
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubygems_version: 3.6.3
73
+ specification_version: 4
74
+ summary: Official Ruby SDK for Ip2Geo - IP to Geolocation API.
75
+ test_files: []