verifip 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: dacef2f215d3e573f691c0919822285a87362ad9dad135d2976669b2e7585c8d
4
+ data.tar.gz: 17051feb5eb6c39f5c7a8a2bfafaac1ea6fa9ee31279c7f51a593f8c922a8dcf
5
+ SHA512:
6
+ metadata.gz: 5c9314aac5692163a7d465eda51e45dc46c7751b6044f4a4fcf1ce8205ca044819cbcc19ae9a4273dbba06aace7b66b6ba471d4ade68071cf20a0eb66cf35185
7
+ data.tar.gz: 56b179f20e5ad461f7bf2641a6d6535be366f97f11481db5a658d503efc7167054fa196322a74b76aab48b9802c95a26b148989f0cedd05f2158b5f11b0467e9
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Qubit HQ
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # VerifIP Ruby SDK
2
+
3
+ Official Ruby SDK for the [VerifIP](https://verifip.com) IP fraud scoring API.
4
+
5
+ Requires **Ruby 3.1+**. Zero runtime dependencies -- uses only Ruby stdlib (`net/http`, `json`, `uri`).
6
+
7
+ ## Installation
8
+
9
+ ### Gemfile
10
+
11
+ ```ruby
12
+ gem "verifip", "~> 0.1.0"
13
+ ```
14
+
15
+ ### Manual
16
+
17
+ ```bash
18
+ gem install verifip
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```ruby
24
+ require "verifip"
25
+
26
+ client = VerifIP::Client.new(api_key: "vip_your_key")
27
+ result = client.check("185.220.101.1")
28
+
29
+ puts result.fraud_score # 0-100
30
+ puts result.vpn? # true/false
31
+ puts result.country_code # "DE"
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ ```ruby
37
+ client = VerifIP::Client.new(
38
+ api_key: "vip_your_key",
39
+ base_url: "https://api.verifip.com", # optional
40
+ timeout: 15, # seconds, default: 30
41
+ max_retries: 5 # default: 3
42
+ )
43
+ ```
44
+
45
+ ## Methods
46
+
47
+ ### `check(ip)` -- Single IP Check
48
+
49
+ ```ruby
50
+ result = client.check("185.220.101.1")
51
+
52
+ result.request_id # unique request ID
53
+ result.ip # queried IP
54
+ result.fraud_score # 0-100 risk score
55
+ result.is_proxy # proxy detected
56
+ result.proxy? # alias for is_proxy
57
+ result.is_vpn # VPN detected
58
+ result.vpn? # alias for is_vpn
59
+ result.is_tor # Tor exit node
60
+ result.tor? # alias for is_tor
61
+ result.is_datacenter # datacenter IP
62
+ result.datacenter? # alias for is_datacenter
63
+ result.country_code # "US", "DE", etc.
64
+ result.country_name # "United States", etc.
65
+ result.region # state/province
66
+ result.city # city name
67
+ result.isp # ISP name
68
+ result.asn # AS number
69
+ result.connection_type # "residential", "datacenter", etc.
70
+ result.hostname # reverse DNS hostname
71
+ result.signal_breakdown # Hash of signal name => score
72
+ result.error # error message if check failed, nil otherwise
73
+ ```
74
+
75
+ ### `check_batch(ips)` -- Batch Check
76
+
77
+ Check up to 100 IPs in a single request (Starter plan or higher):
78
+
79
+ ```ruby
80
+ batch = client.check_batch(["8.8.8.8", "1.1.1.1", "185.220.101.1"])
81
+
82
+ batch.results.each do |r|
83
+ puts "#{r.ip} -> score=#{r.fraud_score}"
84
+ end
85
+ ```
86
+
87
+ ### `health` -- Health Check
88
+
89
+ Check API server status (no authentication required):
90
+
91
+ ```ruby
92
+ health = client.health
93
+ puts health.status # "ok"
94
+ puts health.version # API version
95
+ puts health.uptime_seconds # server uptime
96
+ puts health.redis # "connected"
97
+ puts health.postgres # "connected"
98
+ ```
99
+
100
+ ### Rate Limit Info
101
+
102
+ After any API call, inspect the most recent rate limit headers:
103
+
104
+ ```ruby
105
+ info = client.rate_limit_info
106
+ if info
107
+ puts info.limit # max requests
108
+ puts info.remaining # remaining
109
+ puts info.reset # Time object
110
+ end
111
+ ```
112
+
113
+ ## Error Handling
114
+
115
+ All errors inherit from `VerifIP::VerifIPError` (which inherits from `StandardError`):
116
+
117
+ | Error | HTTP Status | When |
118
+ |---|---|---|
119
+ | `AuthenticationError` | 401, 403 | Invalid or disabled API key |
120
+ | `RateLimitError` | 429 | Daily request limit exceeded |
121
+ | `InvalidRequestError` | 400 | Malformed IP, bad request body |
122
+ | `ServerError` | 5xx | Server-side errors |
123
+
124
+ ```ruby
125
+ begin
126
+ result = client.check("185.220.101.1")
127
+ rescue VerifIP::AuthenticationError => e
128
+ puts "Bad API key: #{e.message}"
129
+ rescue VerifIP::RateLimitError => e
130
+ puts "Rate limited, retry after: #{e.retry_after}s"
131
+ rescue VerifIP::InvalidRequestError => e
132
+ puts "Bad request: #{e.message}"
133
+ rescue VerifIP::ServerError => e
134
+ puts "Server error: #{e.status_code}"
135
+ rescue VerifIP::VerifIPError => e
136
+ puts "API error: #{e.message}"
137
+ end
138
+ ```
139
+
140
+ All errors expose:
141
+ - `status_code` -- HTTP status code (0 for connection errors)
142
+ - `error_code` -- machine-readable error code
143
+ - `retry_after` -- seconds to wait before retrying (may be nil)
144
+
145
+ ## Automatic Retries
146
+
147
+ The SDK automatically retries on 429 and 5xx errors with exponential backoff and jitter. Configure with `max_retries:` (default 3, set to 0 to disable).
148
+
149
+ ## License
150
+
151
+ MIT
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ module VerifIP
8
+ # Client for the VerifIP IP fraud scoring API.
9
+ #
10
+ # @example
11
+ # client = VerifIP::Client.new(api_key: "vip_your_key")
12
+ # result = client.check("185.220.101.1")
13
+ # puts result.fraud_score # => 70
14
+ #
15
+ class Client
16
+ DEFAULT_BASE_URL = "https://api.verifip.com"
17
+ DEFAULT_TIMEOUT = 30
18
+ DEFAULT_MAX_RETRIES = 3
19
+ RETRYABLE_STATUSES = [429, 500, 502, 503, 504].freeze
20
+ USER_AGENT = "verifip-ruby/#{VerifIP::VERSION}"
21
+
22
+ # @return [RateLimitInfo, nil] most recently observed rate limit info
23
+ def rate_limit_info
24
+ @mutex.synchronize { @rate_limit_info }
25
+ end
26
+
27
+ # Create a new VerifIP client.
28
+ #
29
+ # @param api_key [String] your VerifIP API key (starts with "vip_")
30
+ # @param base_url [String] API base URL (default: https://api.verifip.com)
31
+ # @param timeout [Integer] request timeout in seconds (default: 30)
32
+ # @param max_retries [Integer] max retry attempts on 429/5xx (default: 3)
33
+ def initialize(api_key:, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES)
34
+ raise ArgumentError, "api_key is required" if api_key.nil? || api_key.empty?
35
+
36
+ @api_key = api_key
37
+ @base_url = base_url.chomp("/")
38
+ @timeout = timeout
39
+ @max_retries = max_retries
40
+ @rate_limit_info = nil
41
+ @mutex = Mutex.new
42
+ end
43
+
44
+ # Check a single IP address for fraud risk.
45
+ #
46
+ # @param ip [String] IPv4 or IPv6 address
47
+ # @return [CheckResponse]
48
+ # @raise [InvalidRequestError] if the IP is malformed or reserved
49
+ # @raise [AuthenticationError] if the API key is invalid or disabled
50
+ # @raise [RateLimitError] if the daily limit is exceeded
51
+ def check(ip)
52
+ raise ArgumentError, "ip is required" if ip.nil? || ip.empty?
53
+
54
+ data = request(:get, "/v1/check?ip=#{URI.encode_uri_component(ip)}", auth: true)
55
+ CheckResponse.from_hash(data)
56
+ end
57
+
58
+ # Check multiple IP addresses in a single request.
59
+ #
60
+ # Requires Starter plan or higher. Maximum 100 IPs per request.
61
+ #
62
+ # @param ips [Array<String>] list of IPv4/IPv6 addresses (1-100)
63
+ # @return [BatchResponse]
64
+ def check_batch(ips)
65
+ raise ArgumentError, "ips list is required and cannot be empty" if ips.nil? || ips.empty?
66
+ raise ArgumentError, "Maximum 100 IPs per batch request" if ips.size > 100
67
+
68
+ body = JSON.generate({ ips: ips })
69
+ data = request(:post, "/v1/check/batch", body: body, auth: true)
70
+ BatchResponse.from_hash(data)
71
+ end
72
+
73
+ # Check API server health status. Does not require authentication.
74
+ #
75
+ # @return [HealthResponse]
76
+ def health
77
+ data = request(:get, "/health", auth: false)
78
+ HealthResponse.from_hash(data)
79
+ end
80
+
81
+ def to_s = "VerifIP::Client(base_url=#{@base_url})"
82
+ def inspect = to_s
83
+
84
+ private
85
+
86
+ def request(method, path, body: nil, auth: true)
87
+ uri = URI("#{@base_url}#{path}")
88
+ last_error = nil
89
+
90
+ (@max_retries + 1).times do |attempt|
91
+ http = Net::HTTP.new(uri.host, uri.port)
92
+ http.use_ssl = (uri.scheme == "https")
93
+ http.open_timeout = @timeout
94
+ http.read_timeout = @timeout
95
+
96
+ req = build_request(method, uri, body: body, auth: auth)
97
+
98
+ begin
99
+ response = http.request(req)
100
+ rescue StandardError => e
101
+ last_error = VerifIPError.new(
102
+ "Connection error: #{e.message}",
103
+ status_code: 0,
104
+ error_code: "connection_error"
105
+ )
106
+ if attempt < @max_retries
107
+ sleep(backoff_delay(attempt))
108
+ next
109
+ end
110
+ raise last_error
111
+ end
112
+
113
+ status = response.code.to_i
114
+ update_rate_limit(response)
115
+
116
+ if status >= 200 && status < 300
117
+ resp_body = response.body
118
+ return (resp_body && !resp_body.empty?) ? JSON.parse(resp_body) : {}
119
+ end
120
+
121
+ # Parse error response
122
+ error_data = begin
123
+ JSON.parse(response.body || "")
124
+ rescue JSON::ParserError
125
+ {}
126
+ end
127
+
128
+ error_code = error_data["error"] || ""
129
+ message = error_data["message"] || response.body || ""
130
+ retry_after = error_data["retry_after"]
131
+
132
+ err = make_error(status, error_code, message, retry_after)
133
+
134
+ if RETRYABLE_STATUSES.include?(status) && attempt < @max_retries
135
+ last_error = err
136
+ delay = retry_after || (0.5 * (2**attempt))
137
+ delay = [delay, 30].min
138
+ delay += rand * 0.25 * delay
139
+ sleep(delay)
140
+ next
141
+ end
142
+
143
+ raise err
144
+ end
145
+
146
+ raise last_error || VerifIPError.new("Request failed after retries")
147
+ end
148
+
149
+ def build_request(method, uri, body: nil, auth: true)
150
+ req = case method
151
+ when :get
152
+ Net::HTTP::Get.new(uri)
153
+ when :post
154
+ Net::HTTP::Post.new(uri)
155
+ else
156
+ raise ArgumentError, "Unsupported method: #{method}"
157
+ end
158
+
159
+ req["User-Agent"] = USER_AGENT
160
+ req["Accept"] = "application/json"
161
+ req["Authorization"] = "Bearer #{@api_key}" if auth
162
+
163
+ if body
164
+ req["Content-Type"] = "application/json"
165
+ req.body = body
166
+ end
167
+
168
+ req
169
+ end
170
+
171
+ def update_rate_limit(response)
172
+ info = RateLimitInfo.from_headers(response)
173
+ @mutex.synchronize { @rate_limit_info = info } if info
174
+ end
175
+
176
+ def make_error(status, code, message, retry_after)
177
+ kwargs = { status_code: status, error_code: code, retry_after: retry_after }
178
+ case status
179
+ when 400
180
+ InvalidRequestError.new(message, **kwargs)
181
+ when 401, 403
182
+ AuthenticationError.new(message, **kwargs)
183
+ when 429
184
+ RateLimitError.new(message, **kwargs)
185
+ when 500..599
186
+ ServerError.new(message, **kwargs)
187
+ else
188
+ VerifIPError.new(message, **kwargs)
189
+ end
190
+ end
191
+
192
+ def backoff_delay(attempt)
193
+ 0.5 * (2**attempt)
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VerifIP
4
+ # Base error class for all VerifIP API errors.
5
+ class VerifIPError < StandardError
6
+ # @return [Integer] HTTP status code (0 for connection errors)
7
+ attr_reader :status_code
8
+
9
+ # @return [String] machine-readable error code from the API
10
+ attr_reader :error_code
11
+
12
+ # @return [Integer, nil] suggested seconds to wait before retrying
13
+ attr_reader :retry_after
14
+
15
+ def initialize(message = nil, status_code: 0, error_code: "", retry_after: nil)
16
+ super(message)
17
+ @status_code = status_code
18
+ @error_code = error_code || ""
19
+ @retry_after = retry_after
20
+ end
21
+
22
+ def to_s
23
+ "#{self.class.name}(#{status_code}, '#{error_code}', '#{message}')"
24
+ end
25
+ end
26
+
27
+ # Raised on 401 (invalid API key) or 403 (key disabled).
28
+ class AuthenticationError < VerifIPError; end
29
+
30
+ # Raised on 429 (rate limit exceeded). Check {#retry_after} for wait time.
31
+ class RateLimitError < VerifIPError; end
32
+
33
+ # Raised on 400 (invalid IP, bad request body).
34
+ class InvalidRequestError < VerifIPError; end
35
+
36
+ # Raised on 5xx server errors.
37
+ class ServerError < VerifIPError; end
38
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VerifIP
4
+ # Response from a single IP check containing fraud score, threat flags,
5
+ # geolocation data, and signal breakdown.
6
+ class CheckResponse
7
+ FIELDS = %i[
8
+ request_id ip fraud_score is_proxy is_vpn is_tor is_datacenter
9
+ country_code country_name region city isp asn connection_type
10
+ hostname signal_breakdown error
11
+ ].freeze
12
+
13
+ attr_reader(*FIELDS)
14
+
15
+ def initialize(**kwargs)
16
+ @request_id = kwargs.fetch(:request_id, "")
17
+ @ip = kwargs.fetch(:ip, "")
18
+ @fraud_score = kwargs.fetch(:fraud_score, 0)
19
+ @is_proxy = kwargs.fetch(:is_proxy, false)
20
+ @is_vpn = kwargs.fetch(:is_vpn, false)
21
+ @is_tor = kwargs.fetch(:is_tor, false)
22
+ @is_datacenter = kwargs.fetch(:is_datacenter, false)
23
+ @country_code = kwargs.fetch(:country_code, "")
24
+ @country_name = kwargs.fetch(:country_name, "")
25
+ @region = kwargs.fetch(:region, "")
26
+ @city = kwargs.fetch(:city, "")
27
+ @isp = kwargs.fetch(:isp, "")
28
+ @asn = kwargs.fetch(:asn, 0)
29
+ @connection_type = kwargs.fetch(:connection_type, "")
30
+ @hostname = kwargs.fetch(:hostname, "")
31
+ @signal_breakdown = kwargs.fetch(:signal_breakdown, {})
32
+ @error = kwargs.fetch(:error, nil)
33
+ end
34
+
35
+ # Build a CheckResponse from a parsed JSON hash.
36
+ #
37
+ # @param hash [Hash] parsed API response
38
+ # @return [CheckResponse]
39
+ def self.from_hash(hash)
40
+ hash = _symbolize(hash)
41
+ new(**hash.slice(*FIELDS))
42
+ end
43
+
44
+ def proxy? = @is_proxy
45
+ def vpn? = @is_vpn
46
+ def tor? = @is_tor
47
+ def datacenter? = @is_datacenter
48
+
49
+ def to_h
50
+ FIELDS.each_with_object({}) { |f, h| h[f] = send(f) }
51
+ end
52
+
53
+ def to_s
54
+ "CheckResponse(ip=#{@ip}, fraud_score=#{@fraud_score}, proxy=#{@is_proxy}, vpn=#{@is_vpn})"
55
+ end
56
+
57
+ def inspect = to_s
58
+
59
+ # @api private
60
+ def self._symbolize(hash)
61
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
62
+ end
63
+ private_class_method :_symbolize
64
+ end
65
+
66
+ # Response from a batch IP check.
67
+ class BatchResponse
68
+ # @return [Array<CheckResponse>]
69
+ attr_reader :results
70
+
71
+ def initialize(results = [])
72
+ @results = results.freeze
73
+ end
74
+
75
+ # Build a BatchResponse from a parsed JSON hash.
76
+ #
77
+ # @param hash [Hash] parsed API response with "results" key
78
+ # @return [BatchResponse]
79
+ def self.from_hash(hash)
80
+ items = (hash["results"] || hash[:results] || []).map do |r|
81
+ CheckResponse.from_hash(r)
82
+ end
83
+ new(items)
84
+ end
85
+
86
+ def size = @results.size
87
+
88
+ def to_s = "BatchResponse(results=#{size})"
89
+ def inspect = to_s
90
+ end
91
+
92
+ # Response from the health check endpoint.
93
+ class HealthResponse
94
+ attr_reader :status, :version, :data_loaded_at, :redis, :postgres, :uptime_seconds
95
+
96
+ def initialize(**kwargs)
97
+ @status = kwargs.fetch(:status, "")
98
+ @version = kwargs.fetch(:version, "")
99
+ @data_loaded_at = kwargs.fetch(:data_loaded_at, "")
100
+ @redis = kwargs.fetch(:redis, "")
101
+ @postgres = kwargs.fetch(:postgres, "")
102
+ @uptime_seconds = kwargs.fetch(:uptime_seconds, 0)
103
+ end
104
+
105
+ # Build a HealthResponse from a parsed JSON hash.
106
+ #
107
+ # @param hash [Hash] parsed API response
108
+ # @return [HealthResponse]
109
+ def self.from_hash(hash)
110
+ hash = hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
111
+ new(**hash.slice(:status, :version, :data_loaded_at, :redis, :postgres, :uptime_seconds))
112
+ end
113
+
114
+ def to_s = "HealthResponse(status=#{@status}, version=#{@version})"
115
+ def inspect = to_s
116
+ end
117
+
118
+ # Rate limit information parsed from response headers.
119
+ class RateLimitInfo
120
+ # @return [Integer] maximum requests in the current window
121
+ attr_reader :limit
122
+
123
+ # @return [Integer] remaining requests in the current window
124
+ attr_reader :remaining
125
+
126
+ # @return [Time, nil] time at which the window resets
127
+ attr_reader :reset
128
+
129
+ def initialize(limit:, remaining:, reset: nil)
130
+ @limit = limit
131
+ @remaining = remaining
132
+ @reset = reset
133
+ end
134
+
135
+ # Parse rate limit info from HTTP response headers.
136
+ #
137
+ # @param headers [Hash, Net::HTTPResponse] response headers
138
+ # @return [RateLimitInfo, nil] nil if rate limit headers are absent
139
+ def self.from_headers(headers)
140
+ limit_str = headers["X-RateLimit-Limit"] || headers["x-ratelimit-limit"]
141
+ return nil if limit_str.nil?
142
+
143
+ limit = limit_str.to_i
144
+ remaining = (headers["X-RateLimit-Remaining"] || headers["x-ratelimit-remaining"] || "0").to_i
145
+
146
+ reset_str = headers["X-RateLimit-Reset"] || headers["x-ratelimit-reset"]
147
+ reset = nil
148
+ if reset_str
149
+ begin
150
+ reset = Time.at(reset_str.to_i).utc
151
+ rescue ArgumentError, TypeError
152
+ # ignore
153
+ end
154
+ end
155
+
156
+ new(limit: limit, remaining: remaining, reset: reset)
157
+ end
158
+
159
+ def to_s = "RateLimitInfo(limit=#{@limit}, remaining=#{@remaining}, reset=#{@reset})"
160
+ def inspect = to_s
161
+ end
162
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VerifIP
4
+ VERSION = "0.1.0"
5
+ end
data/lib/verifip.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "verifip/version"
4
+ require_relative "verifip/errors"
5
+ require_relative "verifip/models"
6
+ require_relative "verifip/client"
7
+
8
+ module VerifIP
9
+ # Convenience method to create a new client.
10
+ #
11
+ # @param api_key [String] your VerifIP API key
12
+ # @param kwargs [Hash] additional options passed to {Client#initialize}
13
+ # @return [Client]
14
+ def self.client(api_key:, **kwargs)
15
+ Client.new(api_key: api_key, **kwargs)
16
+ end
17
+ end
data/verifip.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "verifip"
3
+ s.version = "0.1.0"
4
+ s.summary = "Official Ruby SDK for the VerifIP IP fraud scoring API"
5
+ s.description = "Ruby client library for the VerifIP API. Check IP addresses " \
6
+ "for fraud risk, VPN/proxy/Tor detection, geolocation, and more."
7
+ s.authors = ["VerifIP"]
8
+ s.email = ["support@verifip.com"]
9
+ s.homepage = "https://github.com/verifip/verifip-ruby"
10
+ s.license = "MIT"
11
+
12
+ s.required_ruby_version = ">= 3.1.0"
13
+
14
+ s.files = Dir["lib/**/*.rb", "README.md", "LICENSE", "verifip.gemspec"]
15
+ s.require_paths = ["lib"]
16
+
17
+ # Zero runtime dependencies — uses only Ruby stdlib (net/http, json, uri)
18
+
19
+ s.add_development_dependency "rspec", "~> 3.13"
20
+ s.add_development_dependency "webmock", "~> 3.23"
21
+
22
+ s.metadata = {
23
+ "bug_tracker_uri" => "https://github.com/verifip/verifip-ruby/issues",
24
+ "changelog_uri" => "https://github.com/verifip/verifip-ruby/blob/main/CHANGELOG.md",
25
+ "documentation_uri" => "https://docs.verifip.com",
26
+ "homepage_uri" => s.homepage,
27
+ "source_code_uri" => "https://github.com/verifip/verifip-ruby"
28
+ }
29
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: verifip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - VerifIP
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rspec
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.13'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.13'
26
+ - !ruby/object:Gem::Dependency
27
+ name: webmock
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.23'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.23'
40
+ description: Ruby client library for the VerifIP API. Check IP addresses for fraud
41
+ risk, VPN/proxy/Tor detection, geolocation, and more.
42
+ email:
43
+ - support@verifip.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE
49
+ - README.md
50
+ - lib/verifip.rb
51
+ - lib/verifip/client.rb
52
+ - lib/verifip/errors.rb
53
+ - lib/verifip/models.rb
54
+ - lib/verifip/version.rb
55
+ - verifip.gemspec
56
+ homepage: https://github.com/verifip/verifip-ruby
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ bug_tracker_uri: https://github.com/verifip/verifip-ruby/issues
61
+ changelog_uri: https://github.com/verifip/verifip-ruby/blob/main/CHANGELOG.md
62
+ documentation_uri: https://docs.verifip.com
63
+ homepage_uri: https://github.com/verifip/verifip-ruby
64
+ source_code_uri: https://github.com/verifip/verifip-ruby
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.1.0
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 4.0.2
80
+ specification_version: 4
81
+ summary: Official Ruby SDK for the VerifIP IP fraud scoring API
82
+ test_files: []