ipregistry 1.0.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 +7 -0
- data/CHANGELOG.md +35 -0
- data/LICENSE +201 -0
- data/README.md +285 -0
- data/lib/ipregistry/batch_response.rb +100 -0
- data/lib/ipregistry/cache.rb +100 -0
- data/lib/ipregistry/client.rb +424 -0
- data/lib/ipregistry/errors.rb +122 -0
- data/lib/ipregistry/models/base.rb +83 -0
- data/lib/ipregistry/models/ip_info.rb +111 -0
- data/lib/ipregistry/models/location.rb +69 -0
- data/lib/ipregistry/models/user_agent.rb +31 -0
- data/lib/ipregistry/version.rb +7 -0
- data/lib/ipregistry.rb +33 -0
- metadata +62 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ipregistry
|
|
4
|
+
# Value objects wrapping the JSON payloads returned by the Ipregistry API.
|
|
5
|
+
#
|
|
6
|
+
# Nested objects are always present (never nil), so chained access such as
|
|
7
|
+
# +info.location.country.name+ never raises even when the API omitted a
|
|
8
|
+
# section; absent fields return nil and absent flags return false.
|
|
9
|
+
module Models
|
|
10
|
+
# Base class for all response models. Wraps the decoded JSON hash and
|
|
11
|
+
# exposes convenient readers declared with a small class-level DSL.
|
|
12
|
+
class Base
|
|
13
|
+
# @param data [Hash, nil] the decoded JSON payload
|
|
14
|
+
def initialize(data = {})
|
|
15
|
+
@data = data.is_a?(Hash) ? data : {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The raw decoded payload backing this model.
|
|
19
|
+
# @return [Hash]
|
|
20
|
+
def to_h
|
|
21
|
+
@data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Raw access to any field of the payload, including ones without a
|
|
25
|
+
# dedicated reader.
|
|
26
|
+
def [](key)
|
|
27
|
+
@data[key.to_s]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ==(other)
|
|
31
|
+
other.instance_of?(self.class) && other.to_h == @data
|
|
32
|
+
end
|
|
33
|
+
alias eql? ==
|
|
34
|
+
|
|
35
|
+
def hash
|
|
36
|
+
[self.class, @data].hash
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inspect
|
|
40
|
+
"#<#{self.class.name} #{@data.inspect}>"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Declares readers returning the raw value of same-named JSON fields.
|
|
47
|
+
def field(*names)
|
|
48
|
+
names.each do |name|
|
|
49
|
+
key = name.to_s
|
|
50
|
+
define_method(name) { @data[key] }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Declares a predicate method (+name?+) for a boolean JSON field.
|
|
55
|
+
def flag(name, key: name.to_s)
|
|
56
|
+
define_method(:"#{name}?") { @data[key] ? true : false }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Declares a reader wrapping a nested JSON object in another model.
|
|
60
|
+
# The wrapped object is always returned, even when the field is
|
|
61
|
+
# absent, so chained access is nil-safe.
|
|
62
|
+
def embeds(name, model, key: name.to_s)
|
|
63
|
+
define_method(name) do
|
|
64
|
+
@embedded ||= {}
|
|
65
|
+
@embedded[name] ||= Models.const_get(model).new(@data[key])
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Declares a reader wrapping a nested JSON array in a list of models.
|
|
70
|
+
def embeds_many(name, model, key: name.to_s)
|
|
71
|
+
define_method(name) do
|
|
72
|
+
@embedded ||= {}
|
|
73
|
+
@embedded[name] ||= begin
|
|
74
|
+
klass = Models.const_get(model)
|
|
75
|
+
elements = @data[key]
|
|
76
|
+
elements.is_a?(Array) ? elements.map { |element| klass.new(element) }.freeze : [].freeze
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ipregistry
|
|
4
|
+
module Models
|
|
5
|
+
# Mobile carrier information associated with an IP address. MCC is the
|
|
6
|
+
# Mobile Country Code and MNC the Mobile Network Code.
|
|
7
|
+
class Carrier < Base
|
|
8
|
+
field :name, :mcc, :mnc
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Ownership information for the IP address. The type classifies the kind
|
|
12
|
+
# of company ("business", "education", "government", "hosting", or "isp").
|
|
13
|
+
class Company < Base
|
|
14
|
+
field :name, :domain, :type
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Network connection information for the IP address. The ASN is the
|
|
18
|
+
# Autonomous System Number (nil when unknown), and the type classifies
|
|
19
|
+
# the network ("business", "education", "government", "hosting",
|
|
20
|
+
# "inactive", or "isp").
|
|
21
|
+
class Connection < Base
|
|
22
|
+
field :asn, :domain, :organization, :route, :type
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Prefix and suffix applied around a formatted monetary value (for example
|
|
26
|
+
# the currency symbol and a sign).
|
|
27
|
+
class CurrencyFormatAffix < Base
|
|
28
|
+
field :prefix, :suffix
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# How monetary values are formatted for a currency.
|
|
32
|
+
class CurrencyFormat < Base
|
|
33
|
+
field :decimal_separator, :group_separator
|
|
34
|
+
|
|
35
|
+
embeds :negative, :CurrencyFormatAffix
|
|
36
|
+
embeds :positive, :CurrencyFormatAffix
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Currency information for the IP address location.
|
|
40
|
+
class Currency < Base
|
|
41
|
+
field :code, :name, :name_native, :plural, :plural_native,
|
|
42
|
+
:symbol, :symbol_native
|
|
43
|
+
|
|
44
|
+
embeds :format, :CurrencyFormat
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Threat-intelligence flags for the IP address.
|
|
48
|
+
class Security < Base
|
|
49
|
+
flag :abuser, key: "is_abuser"
|
|
50
|
+
flag :attacker, key: "is_attacker"
|
|
51
|
+
flag :bogon, key: "is_bogon"
|
|
52
|
+
flag :cloud_provider, key: "is_cloud_provider"
|
|
53
|
+
flag :proxy, key: "is_proxy"
|
|
54
|
+
flag :relay, key: "is_relay"
|
|
55
|
+
flag :tor, key: "is_tor"
|
|
56
|
+
flag :tor_exit, key: "is_tor_exit"
|
|
57
|
+
flag :anonymous, key: "is_anonymous"
|
|
58
|
+
flag :threat, key: "is_threat"
|
|
59
|
+
flag :vpn, key: "is_vpn"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Time zone information for the IP address location. The offset is the
|
|
63
|
+
# current offset from UTC in seconds.
|
|
64
|
+
class TimeZone < Base
|
|
65
|
+
field :id, :abbreviation, :current_time, :name, :offset
|
|
66
|
+
|
|
67
|
+
flag :in_daylight_saving
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The comprehensive set of information associated with an IP address
|
|
71
|
+
# returned by the Ipregistry API.
|
|
72
|
+
#
|
|
73
|
+
# Nested objects (carrier, company, connection, currency, location,
|
|
74
|
+
# security, and time_zone) are always present, so chained access never
|
|
75
|
+
# raises even when the API omitted them; absent fields return nil.
|
|
76
|
+
class IpInfo < Base
|
|
77
|
+
# ip is the IP address the data refers to; type is the IP version
|
|
78
|
+
# ("IPv4" or "IPv6"); hostname is the reverse-DNS hostname, when
|
|
79
|
+
# hostname resolution is requested and available.
|
|
80
|
+
field :ip, :type, :hostname
|
|
81
|
+
|
|
82
|
+
embeds :carrier, :Carrier
|
|
83
|
+
embeds :company, :Company
|
|
84
|
+
embeds :connection, :Connection
|
|
85
|
+
embeds :currency, :Currency
|
|
86
|
+
embeds :location, :Location
|
|
87
|
+
embeds :security, :Security
|
|
88
|
+
embeds :time_zone, :TimeZone
|
|
89
|
+
|
|
90
|
+
def ipv4? = @data["type"] == "IPv4"
|
|
91
|
+
|
|
92
|
+
def ipv6? = @data["type"] == "IPv6"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# IpInfo enriched with parsed User-Agent data. Returned by
|
|
96
|
+
# {Client#lookup_origin}, where the User-Agent of the calling client is
|
|
97
|
+
# known.
|
|
98
|
+
class RequesterIpInfo < IpInfo
|
|
99
|
+
# The parsed User-Agent of the requester, or nil when the API did not
|
|
100
|
+
# return any.
|
|
101
|
+
# @return [UserAgent, nil]
|
|
102
|
+
def user_agent
|
|
103
|
+
value = @data["user_agent"]
|
|
104
|
+
return nil unless value.is_a?(Hash)
|
|
105
|
+
|
|
106
|
+
@embedded ||= {}
|
|
107
|
+
@embedded[:user_agent] ||= UserAgent.new(value)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ipregistry
|
|
4
|
+
module Models
|
|
5
|
+
# Continent-level information for a location.
|
|
6
|
+
class Continent < Base
|
|
7
|
+
field :code, :name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Language information.
|
|
11
|
+
class Language < Base
|
|
12
|
+
field :code, :name
|
|
13
|
+
|
|
14
|
+
# The language's name in the language itself.
|
|
15
|
+
def native_name
|
|
16
|
+
@data["native"]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Representations of a country flag across several icon sets.
|
|
21
|
+
class Flag < Base
|
|
22
|
+
field :emoji, :emoji_unicode, :emojitwo, :noto, :twemoji, :wikimedia
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Country-level information for a location.
|
|
26
|
+
class Country < Base
|
|
27
|
+
# area is the total land area in square kilometers; borders lists the
|
|
28
|
+
# ISO 3166-1 alpha-2 codes of bordering countries; code is the ISO
|
|
29
|
+
# 3166-1 alpha-2 country code (for example "US"); population is the
|
|
30
|
+
# estimated number of inhabitants; population_density is the number of
|
|
31
|
+
# inhabitants per square kilometer; tld is the country-code top-level
|
|
32
|
+
# domain (for example ".us").
|
|
33
|
+
field :area, :calling_code, :capital, :code, :name,
|
|
34
|
+
:population, :population_density, :tld
|
|
35
|
+
|
|
36
|
+
embeds :flag, :Flag
|
|
37
|
+
embeds_many :languages, :Language
|
|
38
|
+
|
|
39
|
+
# ISO 3166-1 alpha-2 codes of bordering countries.
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def borders
|
|
42
|
+
value = @data["borders"]
|
|
43
|
+
value.is_a?(Array) ? value : []
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Administrative region (state/province) information. The code is
|
|
48
|
+
# typically the ISO 3166-2 subdivision code.
|
|
49
|
+
class Region < Base
|
|
50
|
+
field :code, :name
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Geographical location associated with an IP address.
|
|
54
|
+
class Location < Base
|
|
55
|
+
# city, postal, and the decimal-degree latitude/longitude (nil when
|
|
56
|
+
# unavailable).
|
|
57
|
+
field :city, :postal, :latitude, :longitude
|
|
58
|
+
|
|
59
|
+
embeds :continent, :Continent
|
|
60
|
+
embeds :country, :Country
|
|
61
|
+
embeds :region, :Region
|
|
62
|
+
# The primary language spoken at the location.
|
|
63
|
+
embeds :language, :Language
|
|
64
|
+
|
|
65
|
+
# Whether the location is within a European Union member state.
|
|
66
|
+
flag :in_eu
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ipregistry
|
|
4
|
+
module Models
|
|
5
|
+
# Device data parsed from a User-Agent string.
|
|
6
|
+
class UserAgentDevice < Base
|
|
7
|
+
field :brand, :name, :type
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Layout-engine data parsed from a User-Agent string.
|
|
11
|
+
class UserAgentEngine < Base
|
|
12
|
+
field :name, :type, :version, :version_major
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Operating-system data parsed from a User-Agent string.
|
|
16
|
+
class UserAgentOperatingSystem < Base
|
|
17
|
+
field :name, :type, :version
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Structured data parsed from a raw User-Agent string. The header field
|
|
21
|
+
# is the raw User-Agent string that was parsed.
|
|
22
|
+
class UserAgent < Base
|
|
23
|
+
field :header, :name, :type, :version, :version_major
|
|
24
|
+
|
|
25
|
+
embeds :device, :UserAgentDevice
|
|
26
|
+
embeds :engine, :UserAgentEngine
|
|
27
|
+
embeds :operating_system, :UserAgentOperatingSystem, key: "os"
|
|
28
|
+
alias os operating_system
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/ipregistry.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ipregistry/version"
|
|
4
|
+
require_relative "ipregistry/errors"
|
|
5
|
+
require_relative "ipregistry/cache"
|
|
6
|
+
require_relative "ipregistry/models/base"
|
|
7
|
+
require_relative "ipregistry/models/location"
|
|
8
|
+
require_relative "ipregistry/models/ip_info"
|
|
9
|
+
require_relative "ipregistry/models/user_agent"
|
|
10
|
+
require_relative "ipregistry/batch_response"
|
|
11
|
+
require_relative "ipregistry/client"
|
|
12
|
+
|
|
13
|
+
# Official Ruby client for the Ipregistry API (https://ipregistry.co), a fast
|
|
14
|
+
# and reliable IP geolocation and threat data service.
|
|
15
|
+
#
|
|
16
|
+
# client = Ipregistry::Client.new("YOUR_API_KEY")
|
|
17
|
+
# info = client.lookup("8.8.8.8")
|
|
18
|
+
# info.location.country.name # => "United States"
|
|
19
|
+
# info.security.vpn? # => false
|
|
20
|
+
module Ipregistry
|
|
21
|
+
# Reports whether the given raw User-Agent string looks like a crawler or
|
|
22
|
+
# bot. It is a lightweight heuristic — useful for skipping IP lookups on
|
|
23
|
+
# automated traffic — that matches the substrings "bot", "spider", and
|
|
24
|
+
# "slurp" case-insensitively.
|
|
25
|
+
#
|
|
26
|
+
# unless Ipregistry.bot?(request.user_agent)
|
|
27
|
+
# info = client.lookup(request.remote_ip)
|
|
28
|
+
# end
|
|
29
|
+
def self.bot?(user_agent)
|
|
30
|
+
value = user_agent.to_s.downcase
|
|
31
|
+
value.include?("bot") || value.include?("spider") || value.include?("slurp")
|
|
32
|
+
end
|
|
33
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ipregistry
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ipregistry
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Look up IP addresses (single, batch, or the requester's own) and parse
|
|
13
|
+
User-Agent strings with the Ipregistry API. Returns carrier, company, connection,
|
|
14
|
+
currency, location, time zone, and threat data. Zero runtime dependencies, optional
|
|
15
|
+
in-memory caching, and automatic retries with exponential backoff.
|
|
16
|
+
email:
|
|
17
|
+
- support@ipregistry.co
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
25
|
+
- lib/ipregistry.rb
|
|
26
|
+
- lib/ipregistry/batch_response.rb
|
|
27
|
+
- lib/ipregistry/cache.rb
|
|
28
|
+
- lib/ipregistry/client.rb
|
|
29
|
+
- lib/ipregistry/errors.rb
|
|
30
|
+
- lib/ipregistry/models/base.rb
|
|
31
|
+
- lib/ipregistry/models/ip_info.rb
|
|
32
|
+
- lib/ipregistry/models/location.rb
|
|
33
|
+
- lib/ipregistry/models/user_agent.rb
|
|
34
|
+
- lib/ipregistry/version.rb
|
|
35
|
+
homepage: https://github.com/ipregistry/ipregistry-ruby
|
|
36
|
+
licenses:
|
|
37
|
+
- Apache-2.0
|
|
38
|
+
metadata:
|
|
39
|
+
homepage_uri: https://github.com/ipregistry/ipregistry-ruby
|
|
40
|
+
source_code_uri: https://github.com/ipregistry/ipregistry-ruby
|
|
41
|
+
changelog_uri: https://github.com/ipregistry/ipregistry-ruby/blob/main/CHANGELOG.md
|
|
42
|
+
bug_tracker_uri: https://github.com/ipregistry/ipregistry-ruby/issues
|
|
43
|
+
documentation_uri: https://ipregistry.co/docs
|
|
44
|
+
rubygems_mfa_required: 'true'
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '3.1'
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
requirements: []
|
|
59
|
+
rubygems_version: 3.6.9
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Official Ruby client for the Ipregistry IP geolocation and threat data API
|
|
62
|
+
test_files: []
|