masklen 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/README.md +183 -0
- data/lib/masklen/client.rb +170 -0
- data/lib/masklen/error.rb +18 -0
- data/lib/masklen/types.rb +158 -0
- data/lib/masklen/version.rb +3 -0
- data/lib/masklen.rb +25 -0
- metadata +48 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6079eabf7927a845e4bf6c10badae83dac6e028f2a0e4c7815a8d2745723c295
|
|
4
|
+
data.tar.gz: 6ee8e4a6f9c0084b979729060d5388d4044bef02c01ace8c2ccab58438506c2f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1164448a63590a6869f555a5c0e360938bd4562124eb3d5aacae37c11fbf072bb53e164c0b0eb30de998582802c127ad4164d0b50421d193bac293f4024a4383
|
|
7
|
+
data.tar.gz: 75f709d8290831b5b68809158c610d4197ceeea85ecc573417d2a943c4296098f12803686f67ecee6dc4036bbbd516d10c07c8384e8c7562e2b94e2ec0fe13ab
|
data/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# masklen
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for the [masklen.dev](https://masklen.dev) IP intelligence API.
|
|
4
|
+
|
|
5
|
+
Look up geolocation, network, privacy, and locale data for any IPv4 or IPv6 address. Zero external dependencies -- uses only Ruby stdlib.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem "masklen"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install directly:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
gem install masklen
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require "masklen"
|
|
25
|
+
|
|
26
|
+
client = Masklen::Client.new(api_key: "your-api-key")
|
|
27
|
+
|
|
28
|
+
# 1. Look up a specific IP address
|
|
29
|
+
result = client.lookup("8.8.8.8")
|
|
30
|
+
puts result.ip # "8.8.8.8"
|
|
31
|
+
puts result.location.country # "United States"
|
|
32
|
+
puts result.network.isp # "Google LLC"
|
|
33
|
+
puts result.privacy.threat_level # "low"
|
|
34
|
+
|
|
35
|
+
# 2. Look up your own IP (the caller's IP as seen by the server)
|
|
36
|
+
self_result = client.lookup_self
|
|
37
|
+
puts self_result.location.city
|
|
38
|
+
|
|
39
|
+
# 3. Batch lookup (up to 1000 IPs per request)
|
|
40
|
+
batch = client.lookup_batch(["8.8.8.8", "1.1.1.1"])
|
|
41
|
+
batch.results.each do |r|
|
|
42
|
+
if r.is_a?(Masklen::LookupResult)
|
|
43
|
+
puts "#{r.ip} -> #{r.location&.country}"
|
|
44
|
+
else
|
|
45
|
+
# r is a Masklen::BatchItemError
|
|
46
|
+
puts "#{r.ip} error: #{r.error_message}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Method signatures
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
# Initialise the client
|
|
55
|
+
client = Masklen::Client.new(
|
|
56
|
+
api_key: "your-api-key", # required
|
|
57
|
+
base_url: "https://masklen.dev" # optional, defaults to production
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Single IP lookup
|
|
61
|
+
result = client.lookup(ip, fields: nil)
|
|
62
|
+
|
|
63
|
+
# Caller's own IP
|
|
64
|
+
result = client.lookup_self(fields: nil)
|
|
65
|
+
|
|
66
|
+
# Batch lookup
|
|
67
|
+
batch = client.lookup_batch(ips, fields: nil)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Filtering fields
|
|
71
|
+
|
|
72
|
+
All three methods accept an optional `fields:` array. Pass one or more of:
|
|
73
|
+
|
|
74
|
+
- `"location"` -- city, region, country, coordinates, timezone
|
|
75
|
+
- `"network"` -- ASN, ISP, organisation, domain
|
|
76
|
+
- `"privacy"` -- VPN, proxy, Tor, hosting, threat level
|
|
77
|
+
- `"locale"` -- currency, calling code, languages, flag
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
# Fetch only location and privacy data to reduce response size
|
|
81
|
+
result = client.lookup("8.8.8.8", fields: ["location", "privacy"])
|
|
82
|
+
puts result.location.city
|
|
83
|
+
puts result.privacy.vpn
|
|
84
|
+
|
|
85
|
+
# Omit fields entirely to receive all data
|
|
86
|
+
result = client.lookup("8.8.8.8")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Response types
|
|
90
|
+
|
|
91
|
+
All types are `Struct` subclasses with keyword arguments.
|
|
92
|
+
|
|
93
|
+
### LookupResult
|
|
94
|
+
|
|
95
|
+
| Field | Type |
|
|
96
|
+
|------------|--------------|
|
|
97
|
+
| `ip` | String |
|
|
98
|
+
| `location` | Location, nil |
|
|
99
|
+
| `network` | Network, nil |
|
|
100
|
+
| `privacy` | Privacy, nil |
|
|
101
|
+
| `locale` | Locale, nil |
|
|
102
|
+
|
|
103
|
+
### Location
|
|
104
|
+
|
|
105
|
+
| Field | Type |
|
|
106
|
+
|----------------|--------------|
|
|
107
|
+
| `city` | String, nil |
|
|
108
|
+
| `region` | String, nil |
|
|
109
|
+
| `country` | String, nil |
|
|
110
|
+
| `country_code` | String, nil |
|
|
111
|
+
| `latitude` | Float, nil |
|
|
112
|
+
| `longitude` | Float, nil |
|
|
113
|
+
| `postal_code` | String, nil |
|
|
114
|
+
| `timezone` | String, nil |
|
|
115
|
+
|
|
116
|
+
### Network
|
|
117
|
+
|
|
118
|
+
| Field | Type |
|
|
119
|
+
|----------------|-------------|
|
|
120
|
+
| `asn` | String, nil |
|
|
121
|
+
| `isp` | String, nil |
|
|
122
|
+
| `organization` | String, nil |
|
|
123
|
+
| `domain` | String, nil |
|
|
124
|
+
|
|
125
|
+
### Privacy
|
|
126
|
+
|
|
127
|
+
| Field | Type |
|
|
128
|
+
|----------------|---------|
|
|
129
|
+
| `vpn` | Boolean |
|
|
130
|
+
| `proxy` | Boolean |
|
|
131
|
+
| `tor` | Boolean |
|
|
132
|
+
| `hosting` | Boolean |
|
|
133
|
+
| `threat_level` | String ("low" or "medium") |
|
|
134
|
+
|
|
135
|
+
### Locale
|
|
136
|
+
|
|
137
|
+
| Field | Type |
|
|
138
|
+
|-------------------|---------------|
|
|
139
|
+
| `currency` | String, nil |
|
|
140
|
+
| `currency_symbol` | String, nil |
|
|
141
|
+
| `calling_code` | String, nil |
|
|
142
|
+
| `languages` | Array<String> |
|
|
143
|
+
| `flag` | String, nil |
|
|
144
|
+
|
|
145
|
+
### BatchResult
|
|
146
|
+
|
|
147
|
+
| Field | Type |
|
|
148
|
+
|-----------|---------------------------------------|
|
|
149
|
+
| `results` | Array<LookupResult, BatchItemError> |
|
|
150
|
+
|
|
151
|
+
### BatchItemError
|
|
152
|
+
|
|
153
|
+
| Field | Type |
|
|
154
|
+
|-----------------|--------|
|
|
155
|
+
| `ip` | String |
|
|
156
|
+
| `error_code` | String |
|
|
157
|
+
| `error_message` | String |
|
|
158
|
+
|
|
159
|
+
## Error handling
|
|
160
|
+
|
|
161
|
+
All API errors raise `Masklen::Error`, a subclass of `StandardError`.
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
begin
|
|
165
|
+
result = client.lookup("8.8.8.8")
|
|
166
|
+
rescue Masklen::Error => e
|
|
167
|
+
puts e.status_code # HTTP status, e.g. 401
|
|
168
|
+
puts e.error_code # machine-readable code, e.g. "unauthorized"
|
|
169
|
+
puts e.error_message # human-readable message
|
|
170
|
+
puts e.message # full formatted message string
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Network errors (timeouts, connection refused, DNS failures) are also wrapped in `Masklen::Error` with `status_code: 0` and `error_code: "network_error"`.
|
|
175
|
+
|
|
176
|
+
## Requirements
|
|
177
|
+
|
|
178
|
+
- Ruby 3.0 or later
|
|
179
|
+
- No external runtime dependencies (stdlib only: `net/http`, `uri`, `json`)
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Masklen
|
|
6
|
+
# Client is the main entry point for the masklen.dev IP intelligence API.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# client = Masklen::Client.new(api_key: "your-key")
|
|
10
|
+
# result = client.lookup("8.8.8.8")
|
|
11
|
+
# puts result.location.country
|
|
12
|
+
class Client
|
|
13
|
+
DEFAULT_BASE_URL = "https://masklen.dev"
|
|
14
|
+
|
|
15
|
+
# Create a new client.
|
|
16
|
+
#
|
|
17
|
+
# api_key - Your masklen.dev API key (required).
|
|
18
|
+
# base_url - Override the default base URL (optional, useful for testing).
|
|
19
|
+
def initialize(api_key:, base_url: DEFAULT_BASE_URL)
|
|
20
|
+
raise ArgumentError, "api_key is required" if api_key.nil? || api_key.strip.empty?
|
|
21
|
+
|
|
22
|
+
@api_key = api_key
|
|
23
|
+
@base_url = base_url.chomp("/")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Look up a specific IPv4 or IPv6 address.
|
|
27
|
+
#
|
|
28
|
+
# ip - The IP address string to look up.
|
|
29
|
+
# fields - Optional array of field groups to include in the response,
|
|
30
|
+
# e.g. ["location", "privacy"]. When nil, the API returns all fields.
|
|
31
|
+
#
|
|
32
|
+
# Returns a LookupResult.
|
|
33
|
+
# Raises Masklen::Error on API errors.
|
|
34
|
+
def lookup(ip, fields: nil)
|
|
35
|
+
raise ArgumentError, "ip is required" if ip.nil? || ip.to_s.strip.empty?
|
|
36
|
+
|
|
37
|
+
query = build_fields_query(fields)
|
|
38
|
+
hash = request(:get, "/v1/lookup/#{URI.encode_www_form_component(ip.to_s)}", query: query)
|
|
39
|
+
LookupResult.from_hash(hash)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Look up the caller's own IP address (as seen by the API server).
|
|
43
|
+
#
|
|
44
|
+
# fields - Optional array of field groups to include in the response.
|
|
45
|
+
#
|
|
46
|
+
# Returns a LookupResult.
|
|
47
|
+
# Raises Masklen::Error on API errors.
|
|
48
|
+
def lookup_self(fields: nil)
|
|
49
|
+
query = build_fields_query(fields)
|
|
50
|
+
hash = request(:get, "/v1/lookup", query: query)
|
|
51
|
+
LookupResult.from_hash(hash)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Look up up to 1000 IP addresses in a single request.
|
|
55
|
+
#
|
|
56
|
+
# ips - Array of IP address strings (max 1000).
|
|
57
|
+
# fields - Optional array of field groups to include in the response.
|
|
58
|
+
#
|
|
59
|
+
# Returns a BatchResult whose results array contains LookupResult or
|
|
60
|
+
# BatchItemError objects.
|
|
61
|
+
# Raises Masklen::Error on API errors.
|
|
62
|
+
def lookup_batch(ips, fields: nil)
|
|
63
|
+
raise ArgumentError, "ips must be an Array" unless ips.is_a?(Array)
|
|
64
|
+
raise ArgumentError, "ips cannot be empty" if ips.empty?
|
|
65
|
+
raise ArgumentError, "ips cannot exceed 1000 entries" if ips.length > 1000
|
|
66
|
+
|
|
67
|
+
query = build_fields_query(fields)
|
|
68
|
+
body = JSON.generate({ "ips" => ips })
|
|
69
|
+
hash = request(:post, "/v1/lookup/batch", query: query, body: body)
|
|
70
|
+
BatchResult.from_hash(hash)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# Build the query hash for the optional fields parameter.
|
|
76
|
+
# Returns nil when fields is nil or empty.
|
|
77
|
+
def build_fields_query(fields)
|
|
78
|
+
return nil if fields.nil? || fields.empty?
|
|
79
|
+
|
|
80
|
+
{ "fields" => fields.join(",") }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Perform an HTTP request against the masklen.dev API.
|
|
84
|
+
#
|
|
85
|
+
# method - :get or :post (Symbol).
|
|
86
|
+
# path - URL path string, e.g. "/v1/lookup/8.8.8.8".
|
|
87
|
+
# query - Optional Hash of query-string parameters.
|
|
88
|
+
# body - Optional request body string (used for POST).
|
|
89
|
+
#
|
|
90
|
+
# Returns a parsed Hash from the JSON response body.
|
|
91
|
+
# Raises Masklen::Error when the server responds with a non-2xx status.
|
|
92
|
+
# Raises Masklen::Error wrapping network failures (SocketError, Timeout::Error, etc.).
|
|
93
|
+
def request(method, path, query: nil, body: nil)
|
|
94
|
+
uri = build_uri(path, query)
|
|
95
|
+
|
|
96
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
97
|
+
http.use_ssl = (uri.scheme == "https")
|
|
98
|
+
http.read_timeout = 30
|
|
99
|
+
http.open_timeout = 10
|
|
100
|
+
|
|
101
|
+
req = build_request(method, uri, body)
|
|
102
|
+
|
|
103
|
+
begin
|
|
104
|
+
response = http.start { |conn| conn.request(req) }
|
|
105
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
|
|
106
|
+
Timeout::Error, Net::OpenTimeout, Net::ReadTimeout => e
|
|
107
|
+
raise Masklen::Error.new(
|
|
108
|
+
status_code: 0,
|
|
109
|
+
error_code: "network_error",
|
|
110
|
+
error_message: e.message
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
parse_response(response)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Construct the full URI for a request, appending query parameters when present.
|
|
118
|
+
def build_uri(path, query)
|
|
119
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
120
|
+
if query && !query.empty?
|
|
121
|
+
uri.query = URI.encode_www_form(query)
|
|
122
|
+
end
|
|
123
|
+
uri
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Build a Net::HTTP request object with the required headers.
|
|
127
|
+
def build_request(method, uri, body)
|
|
128
|
+
req = case method
|
|
129
|
+
when :get then Net::HTTP::Get.new(uri)
|
|
130
|
+
when :post then Net::HTTP::Post.new(uri)
|
|
131
|
+
else raise ArgumentError, "Unsupported HTTP method: #{method}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
req["Authorization"] = "Bearer #{@api_key}"
|
|
135
|
+
req["Accept"] = "application/json"
|
|
136
|
+
req["User-Agent"] = "masklen-ruby/#{Masklen::VERSION}"
|
|
137
|
+
|
|
138
|
+
if body
|
|
139
|
+
req["Content-Type"] = "application/json"
|
|
140
|
+
req.body = body
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
req
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Parse the HTTP response into a Hash.
|
|
147
|
+
# Raises Masklen::Error for non-2xx status codes.
|
|
148
|
+
def parse_response(response)
|
|
149
|
+
status = response.code.to_i
|
|
150
|
+
|
|
151
|
+
parsed = begin
|
|
152
|
+
JSON.parse(response.body)
|
|
153
|
+
rescue JSON::ParserError
|
|
154
|
+
{}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
return parsed if status >= 200 && status < 300
|
|
158
|
+
|
|
159
|
+
# Attempt to extract API error details from the response body.
|
|
160
|
+
error_code = parsed["error_code"] || "unknown_error"
|
|
161
|
+
error_message = parsed["error_message"] || parsed["message"] || response.message || "Unknown error"
|
|
162
|
+
|
|
163
|
+
raise Masklen::Error.new(
|
|
164
|
+
status_code: status,
|
|
165
|
+
error_code: error_code,
|
|
166
|
+
error_message: error_message
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Masklen
|
|
2
|
+
# Raised when the API returns a non-2xx response or when a network error occurs.
|
|
3
|
+
#
|
|
4
|
+
# Attributes:
|
|
5
|
+
# status_code - HTTP status code returned by the server (Integer)
|
|
6
|
+
# error_code - machine-readable error code from the API response body (String)
|
|
7
|
+
# error_message - human-readable description from the API response body (String)
|
|
8
|
+
class Error < StandardError
|
|
9
|
+
attr_reader :status_code, :error_code, :error_message
|
|
10
|
+
|
|
11
|
+
def initialize(status_code:, error_code:, error_message:)
|
|
12
|
+
@status_code = status_code
|
|
13
|
+
@error_code = error_code
|
|
14
|
+
@error_message = error_message
|
|
15
|
+
super("Masklen API error #{status_code} (#{error_code}): #{error_message}")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
module Masklen
|
|
2
|
+
# Geographic location data for an IP address.
|
|
3
|
+
Location = Struct.new(
|
|
4
|
+
:city,
|
|
5
|
+
:region,
|
|
6
|
+
:country,
|
|
7
|
+
:country_code,
|
|
8
|
+
:latitude,
|
|
9
|
+
:longitude,
|
|
10
|
+
:postal_code,
|
|
11
|
+
:timezone,
|
|
12
|
+
keyword_init: true
|
|
13
|
+
) do
|
|
14
|
+
# Build a Location from a parsed JSON hash. Returns nil if hash is nil.
|
|
15
|
+
def self.from_hash(hash)
|
|
16
|
+
return nil if hash.nil?
|
|
17
|
+
|
|
18
|
+
new(
|
|
19
|
+
city: hash["city"],
|
|
20
|
+
region: hash["region"],
|
|
21
|
+
country: hash["country"],
|
|
22
|
+
country_code: hash["country_code"],
|
|
23
|
+
latitude: hash["latitude"],
|
|
24
|
+
longitude: hash["longitude"],
|
|
25
|
+
postal_code: hash["postal_code"],
|
|
26
|
+
timezone: hash["timezone"]
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Network and ASN data for an IP address.
|
|
32
|
+
Network = Struct.new(
|
|
33
|
+
:asn,
|
|
34
|
+
:isp,
|
|
35
|
+
:organization,
|
|
36
|
+
:domain,
|
|
37
|
+
keyword_init: true
|
|
38
|
+
) do
|
|
39
|
+
# Build a Network from a parsed JSON hash. Returns nil if hash is nil.
|
|
40
|
+
def self.from_hash(hash)
|
|
41
|
+
return nil if hash.nil?
|
|
42
|
+
|
|
43
|
+
new(
|
|
44
|
+
asn: hash["asn"],
|
|
45
|
+
isp: hash["isp"],
|
|
46
|
+
organization: hash["organization"],
|
|
47
|
+
domain: hash["domain"]
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Privacy and threat indicators for an IP address.
|
|
53
|
+
#
|
|
54
|
+
# threat_level is "low" or "medium".
|
|
55
|
+
Privacy = Struct.new(
|
|
56
|
+
:vpn,
|
|
57
|
+
:proxy,
|
|
58
|
+
:tor,
|
|
59
|
+
:hosting,
|
|
60
|
+
:threat_level,
|
|
61
|
+
keyword_init: true
|
|
62
|
+
) do
|
|
63
|
+
# Build a Privacy from a parsed JSON hash. Returns nil if hash is nil.
|
|
64
|
+
def self.from_hash(hash)
|
|
65
|
+
return nil if hash.nil?
|
|
66
|
+
|
|
67
|
+
new(
|
|
68
|
+
vpn: hash["vpn"],
|
|
69
|
+
proxy: hash["proxy"],
|
|
70
|
+
tor: hash["tor"],
|
|
71
|
+
hosting: hash["hosting"],
|
|
72
|
+
threat_level: hash["threat_level"]
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Locale and regional data for an IP address.
|
|
78
|
+
Locale = Struct.new(
|
|
79
|
+
:currency,
|
|
80
|
+
:currency_symbol,
|
|
81
|
+
:calling_code,
|
|
82
|
+
:languages,
|
|
83
|
+
:flag,
|
|
84
|
+
keyword_init: true
|
|
85
|
+
) do
|
|
86
|
+
# Build a Locale from a parsed JSON hash. Returns nil if hash is nil.
|
|
87
|
+
def self.from_hash(hash)
|
|
88
|
+
return nil if hash.nil?
|
|
89
|
+
|
|
90
|
+
new(
|
|
91
|
+
currency: hash["currency"],
|
|
92
|
+
currency_symbol: hash["currency_symbol"],
|
|
93
|
+
calling_code: hash["calling_code"],
|
|
94
|
+
languages: hash["languages"] || [],
|
|
95
|
+
flag: hash["flag"]
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The primary result returned for a single IP lookup.
|
|
101
|
+
LookupResult = Struct.new(
|
|
102
|
+
:ip,
|
|
103
|
+
:location,
|
|
104
|
+
:network,
|
|
105
|
+
:privacy,
|
|
106
|
+
:locale,
|
|
107
|
+
keyword_init: true
|
|
108
|
+
) do
|
|
109
|
+
# Build a LookupResult from a parsed JSON hash.
|
|
110
|
+
def self.from_hash(hash)
|
|
111
|
+
new(
|
|
112
|
+
ip: hash["ip"],
|
|
113
|
+
location: Location.from_hash(hash["location"]),
|
|
114
|
+
network: Network.from_hash(hash["network"]),
|
|
115
|
+
privacy: Privacy.from_hash(hash["privacy"]),
|
|
116
|
+
locale: Locale.from_hash(hash["locale"])
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Represents a failed entry inside a batch lookup response.
|
|
122
|
+
BatchItemError = Struct.new(
|
|
123
|
+
:ip,
|
|
124
|
+
:error_code,
|
|
125
|
+
:error_message,
|
|
126
|
+
keyword_init: true
|
|
127
|
+
) do
|
|
128
|
+
# Build a BatchItemError from a parsed JSON hash.
|
|
129
|
+
def self.from_hash(hash)
|
|
130
|
+
new(
|
|
131
|
+
ip: hash["ip"],
|
|
132
|
+
error_code: hash["error_code"],
|
|
133
|
+
error_message: hash["error_message"]
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# The result of a batch IP lookup. Each entry in results is either a
|
|
139
|
+
# LookupResult or a BatchItemError.
|
|
140
|
+
BatchResult = Struct.new(
|
|
141
|
+
:results,
|
|
142
|
+
keyword_init: true
|
|
143
|
+
) do
|
|
144
|
+
# Build a BatchResult from a parsed JSON hash.
|
|
145
|
+
# Each element in the "results" array is mapped to a LookupResult when it
|
|
146
|
+
# has an "ip" key and no "error_code", or to a BatchItemError otherwise.
|
|
147
|
+
def self.from_hash(hash)
|
|
148
|
+
items = (hash["results"] || []).map do |item|
|
|
149
|
+
if item.key?("error_code")
|
|
150
|
+
BatchItemError.from_hash(item)
|
|
151
|
+
else
|
|
152
|
+
LookupResult.from_hash(item)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
new(results: items)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
data/lib/masklen.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative "masklen/version"
|
|
2
|
+
require_relative "masklen/error"
|
|
3
|
+
require_relative "masklen/types"
|
|
4
|
+
require_relative "masklen/client"
|
|
5
|
+
|
|
6
|
+
# Masklen is the official Ruby SDK for the masklen.dev IP intelligence API.
|
|
7
|
+
#
|
|
8
|
+
# Quick start:
|
|
9
|
+
# require "masklen"
|
|
10
|
+
#
|
|
11
|
+
# client = Masklen::Client.new(api_key: "your-api-key")
|
|
12
|
+
#
|
|
13
|
+
# # Look up a specific IP
|
|
14
|
+
# result = client.lookup("8.8.8.8")
|
|
15
|
+
# puts result.location.country
|
|
16
|
+
#
|
|
17
|
+
# # Look up the caller's own IP
|
|
18
|
+
# self_result = client.lookup_self(fields: ["location", "privacy"])
|
|
19
|
+
# puts self_result.privacy.vpn
|
|
20
|
+
#
|
|
21
|
+
# # Batch lookup
|
|
22
|
+
# batch = client.lookup_batch(["8.8.8.8", "1.1.1.1"])
|
|
23
|
+
# batch.results.each { |r| puts r.ip }
|
|
24
|
+
module Masklen
|
|
25
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: masklen
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- masklen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-06-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Look up geolocation, network, privacy, and locale data for any IP address.
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- README.md
|
|
20
|
+
- lib/masklen.rb
|
|
21
|
+
- lib/masklen/client.rb
|
|
22
|
+
- lib/masklen/error.rb
|
|
23
|
+
- lib/masklen/types.rb
|
|
24
|
+
- lib/masklen/version.rb
|
|
25
|
+
homepage: https://masklen.dev
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata: {}
|
|
29
|
+
post_install_message:
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: 3.0.0
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
requirements: []
|
|
44
|
+
rubygems_version: 3.0.3.1
|
|
45
|
+
signing_key:
|
|
46
|
+
specification_version: 4
|
|
47
|
+
summary: Official Ruby SDK for the masklen.dev IP intelligence API
|
|
48
|
+
test_files: []
|