reputable 0.1.7 → 0.1.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5ea31e6c135265677697ac1411fb0d9308f6d4454f23a3d1558018eddfccb4a
4
- data.tar.gz: 0aea9d057b70a2ca763f5e1f5e6b024d25d07d783feee0447bd311526a189574
3
+ metadata.gz: e8d9854d3f0dfbf85029fa9411273ffdebad9288eaa11b91649a3aec538b6b2b
4
+ data.tar.gz: 8d7fbfaf636c06c80a1ea291a773d35c1e478f5dd1261efd6e213a473f1d0362
5
5
  SHA512:
6
- metadata.gz: f9621074b650d78f8ba44014f5df9880ee8431065f88a9cd6e3da467aef4885da7230851de5cfe316ef3d8162317bd43cba10fd943d87affbbb2ae11046901c9
7
- data.tar.gz: 59c4953b9caaf151b3b8a97d057d90b9dab88bdfe71809460a588f35cc8ddc903dacc6805bb9b14ba39d70d203e7622331bbd469e7b870be1da968a8b74b3f77
6
+ metadata.gz: a13fdb389753a6aed4bde2d9e6bc2304e184db0ded7a579adc6d7fef16d4e1e72a6032f11eec7e6e03e0e706aa2a135c07c17e3e607e227f7670864e84c91aea
7
+ data.tar.gz: fb215dbd673959e3414eaa31c0a77a3c78366db796f34b1afbef12d066041b1308b0c6312a90dc7ee953a8b578083192509a535fb3bf9b53f265f9f3b9a22f80
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reputable (0.1.6)
4
+ reputable (0.1.9)
5
5
  connection_pool (~> 2.2)
6
6
  redis (>= 4.0, < 6.0)
7
7
 
data/README.md CHANGED
@@ -4,6 +4,8 @@ Ruby gem for integrating with Reputable - bot detection and reputation scoring f
4
4
 
5
5
  **Resilience First**: This gem is designed to never break your application. All operations fail silently with safe defaults.
6
6
 
7
+ Release notes and version bumps: see `clients/ruby/RELEASING.md`.
8
+
7
9
  ## Installation
8
10
 
9
11
  Add to your Gemfile:
@@ -83,6 +85,11 @@ REPUTABLE_REDIS_URL=rediss://user:password@your-dragonfly.example.com:6379
83
85
  # Optional: Base URL for verification and API endpoints (domain only)
84
86
  REPUTABLE_BASE_URL=https://api.reputable.click
85
87
 
88
+ # Optional: Blocked page branding/support info
89
+ REPUTABLE_SITE_NAME="Example Store"
90
+ REPUTABLE_SUPPORT_EMAIL=support@example.com
91
+ REPUTABLE_SUPPORT_URL=https://example.com/support
92
+
86
93
  # Optional: Disable entirely (useful for test environments)
87
94
  REPUTABLE_ENABLED=false
88
95
 
@@ -142,6 +149,11 @@ Reputable.configure do |config|
142
149
  # Supports comma-separated list in REPUTABLE_TRUSTED_KEYS or single key in REPUTABLE_TRUSTED_KEY
143
150
  config.trusted_keys = ENV['REPUTABLE_TRUSTED_KEYS']&.split(',') || ENV['REPUTABLE_TRUSTED_KEY']
144
151
  config.base_url = ENV['REPUTABLE_BASE_URL'] # Domain only
152
+
153
+ # Optional: blocked page branding/support info
154
+ config.site_name = ENV['REPUTABLE_SITE_NAME']
155
+ config.support_email = ENV['REPUTABLE_SUPPORT_EMAIL']
156
+ config.support_url = ENV['REPUTABLE_SUPPORT_URL']
145
157
 
146
158
  # Error callback (optional)
147
159
  config.on_error = ->(error, context) {
@@ -260,6 +272,27 @@ config.middleware.use Reputable::Middleware,
260
272
  async: true
261
273
  ```
262
274
 
275
+ ### Optional Reputation Gate
276
+
277
+ If you want the middleware to enforce IP reputation decisions, enable the gate:
278
+
279
+ ```ruby
280
+ config.middleware.use Reputable::Middleware,
281
+ reputation_gate: true,
282
+ challenge_action: :verify, # Redirect to verification for untrusted_challenge
283
+ block_action: :blocked_page_remote, # Redirect to hosted blocked page (uses app UI settings)
284
+ blocked_page_path: "/_reputable/blocked" # Only used for local blocked page
285
+ ```
286
+
287
+ Notes:
288
+ - For `untrusted_challenge`, the middleware redirects to the Reputable verification URL.
289
+ - For `untrusted_block`, the default is to redirect to the hosted blocked page (`/_reputable/verify/blocked`).
290
+ - The hosted blocked page uses the same app UI settings as the verify/failure pages (`siteName`, `supportEmail`).
291
+ - To render a local blocked page instead, set `block_action: :blocked_page` and pass `blocked_page` options.
292
+ - To use a custom hosted page, set `blocked_redirect_url: "https://example.com/blocked"`.
293
+ - Use `blocked_page_path` only for local blocked pages (or to build a custom `failure_url`).
294
+ - Override `challenge_redirect_status` (default `302`) or `verification_force_challenge` if needed.
295
+
263
296
  ### Default Skipped Paths
264
297
 
265
298
  The middleware automatically skips:
@@ -305,6 +338,56 @@ rep = current_ip_reputation
305
338
  # => { status: 'trusted_verified', reason: 'payment', ... }
306
339
  ```
307
340
 
341
+ ### Verification Redirect Helpers
342
+
343
+ ```ruby
344
+ class SessionsController < ApplicationController
345
+ def new
346
+ require_reputable_verification!
347
+ # If verified, continue
348
+ end
349
+ end
350
+ ```
351
+
352
+ Optional args:
353
+ - `return_url` (default: `request.original_url`)
354
+ - `failure_url` (default: API `/verify/failure` page)
355
+ - `session_id` (default: `session.id`)
356
+ - `force_challenge` (default: `false`)
357
+ - `session_key` (default: `:reputable_verified_at`)
358
+
359
+ You can check or clear the session flag:
360
+
361
+ ```ruby
362
+ reputable_verified?
363
+ clear_reputable_verification!
364
+ ```
365
+
366
+ ### Reputation Gate Helpers
367
+
368
+ ```ruby
369
+ class SessionsController < ApplicationController
370
+ def new
371
+ require_reputable_reputation!
372
+ # If not blocked/challenged, continue
373
+ end
374
+ end
375
+ ```
376
+
377
+ `require_reputable_reputation!` will:
378
+ - Render a blocked page for `untrusted_block`
379
+ - Run verification flow for `untrusted_challenge`
380
+
381
+ You can also render the blocked page directly:
382
+
383
+ ```ruby
384
+ render_reputable_blocked_page(
385
+ site_name: "Example Store",
386
+ support_email: "support@example.com",
387
+ support_url: "https://example.com/support"
388
+ )
389
+ ```
390
+
308
391
  ---
309
392
 
310
393
  ## Manual API Usage
@@ -391,7 +474,7 @@ end
391
474
 
392
475
  **Options:**
393
476
  - `return_url` (required): Where to redirect after successful verification
394
- - `failure_url` (optional): Where to redirect on failure (defaults to return_url)
477
+ - `failure_url` (optional): Where to redirect on failure (defaults to API `/verify/failure` page)
395
478
  - `session_id` (optional): Bind verification to a specific session
396
479
  - `force_challenge` (optional): If `true`, always show CAPTCHA even for trusted users. Useful for testing the challenge flow.
397
480
 
@@ -14,7 +14,8 @@ module Reputable
14
14
  :default_ttls, :pool_size, :pool_timeout,
15
15
  :connect_timeout, :read_timeout, :write_timeout,
16
16
  :ssl_params, :trusted_proxies, :ip_header_priority,
17
- :on_error, :trusted_keys, :base_url
17
+ :on_error, :trusted_keys, :base_url,
18
+ :site_name, :support_email, :support_url
18
19
 
19
20
  # Alias for backward compatibility
20
21
  alias_method :verification_base_url, :base_url
@@ -78,6 +79,9 @@ module Reputable
78
79
  @trusted_keys = [ENV["REPUTABLE_SECRET_KEY"]]
79
80
  end
80
81
  @base_url = ENV.fetch("REPUTABLE_BASE_URL", "https://api.reputable.click")
82
+ @site_name = ENV["REPUTABLE_SITE_NAME"]
83
+ @support_email = ENV["REPUTABLE_SUPPORT_EMAIL"]
84
+ @support_url = ENV["REPUTABLE_SUPPORT_URL"]
81
85
  end
82
86
 
83
87
  # Alias for backward compatibility
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "blocked_page"
4
+
3
5
  module Reputable
4
6
  # Rack middleware for automatic request tracking
5
7
  #
@@ -42,12 +44,34 @@ module Reputable
42
44
  @skip_if = options[:skip_if]
43
45
  @tag_builder = options[:tag_builder]
44
46
  @async = options.fetch(:async, true)
47
+ @reputation_gate = options.fetch(:reputation_gate, false)
48
+ @challenge_action = options.fetch(:challenge_action, :verify)
49
+ @block_action = options.fetch(:block_action, :blocked_page_remote)
50
+ @challenge_redirect_status = options.fetch(:challenge_redirect_status, 302)
51
+ @blocked_redirect_status = options.fetch(:blocked_redirect_status, 302)
52
+ @blocked_redirect_url = options[:blocked_redirect_url]
53
+ @verification_force_challenge = options.fetch(:verification_force_challenge, false)
54
+ @verification_failure_url = options[:verification_failure_url]
55
+ @verification_session_id = options[:verification_session_id]
56
+ @verified_session_keys = Array(options.fetch(:verified_session_keys, [:reputable_verified_at, :reputable_verified]))
57
+ @blocked_page_options = options.fetch(:blocked_page, {})
58
+ @blocked_page_path = options[:blocked_page_path]
45
59
  end
46
60
 
47
61
  def call(env)
48
62
  # Check for verification return parameters and verify signature if present
49
63
  handle_verification_return(env)
50
64
 
65
+ # Optional: render blocked page path directly
66
+ if (blocked_response = handle_blocked_page_request(env))
67
+ return blocked_response
68
+ end
69
+
70
+ # Optional: enforce reputation gate before app
71
+ if (gate_response = safe_reputation_gate(env))
72
+ return gate_response
73
+ end
74
+
51
75
  # ALWAYS process the request first - tracking must never block
52
76
  status, headers, response = @app.call(env)
53
77
 
@@ -60,6 +84,33 @@ module Reputable
60
84
 
61
85
  private
62
86
 
87
+ def safe_reputation_gate(env)
88
+ return nil unless @reputation_gate
89
+ return nil unless Reputable.enabled?
90
+ return nil if skip_request?(env)
91
+
92
+ enforce_reputation_gate(env)
93
+ rescue StandardError => e
94
+ Reputable.logger&.debug("Reputable reputation gate: #{e.class} - #{e.message}")
95
+ nil
96
+ end
97
+
98
+ def enforce_reputation_gate(env)
99
+ ip = extract_ip(env)
100
+ status = Reputable::Reputation.lookup_ip(ip)
101
+ return nil if status.nil?
102
+
103
+ case status
104
+ when "untrusted_block"
105
+ handle_block_action(env, ip)
106
+ when "untrusted_challenge"
107
+ return nil if verified_session?(env)
108
+ handle_challenge_action(env)
109
+ else
110
+ nil
111
+ end
112
+ end
113
+
63
114
  def safe_track_request(env)
64
115
  # Skip if disabled globally
65
116
  return unless Reputable.enabled?
@@ -93,6 +144,145 @@ module Reputable
93
144
  Reputable.logger&.debug("Reputable verification error: #{e.message}")
94
145
  end
95
146
 
147
+ def handle_blocked_page_request(env)
148
+ return nil unless @blocked_page_path
149
+
150
+ path = env["PATH_INFO"] || "/"
151
+ return nil unless path == @blocked_page_path
152
+
153
+ ip = extract_ip(env)
154
+ build_blocked_page_response(ip)
155
+ rescue StandardError => e
156
+ Reputable.logger&.debug("Reputable blocked page: #{e.class} - #{e.message}")
157
+ nil
158
+ end
159
+
160
+ def handle_block_action(env, ip)
161
+ case @block_action
162
+ when :blocked_page
163
+ build_blocked_page_response(ip)
164
+ when :blocked_page_remote
165
+ url = resolve_blocked_redirect_url(env) || Reputable.blocked_page_url
166
+ redirect_response(url, @blocked_redirect_status)
167
+ when :forbidden
168
+ [403, { "Content-Type" => "text/plain; charset=utf-8" }, ["Forbidden"]]
169
+ when Proc
170
+ @block_action.call(env, ip)
171
+ else
172
+ build_blocked_page_response(ip)
173
+ end
174
+ end
175
+
176
+ def handle_challenge_action(env)
177
+ case @challenge_action
178
+ when :verify
179
+ keys = Reputable.configuration.trusted_keys
180
+ return nil if keys.nil? || keys.empty?
181
+
182
+ request = Rack::Request.new(env)
183
+ return_url = request.url
184
+ failure_url = build_verification_failure_url(request)
185
+ session_id = resolve_session_id(env)
186
+
187
+ redirect_url = Reputable.verification_url(
188
+ return_url: return_url,
189
+ failure_url: failure_url,
190
+ session_id: session_id,
191
+ force_challenge: @verification_force_challenge
192
+ )
193
+
194
+ return nil if redirect_url == return_url
195
+
196
+ redirect_response(redirect_url, @challenge_redirect_status)
197
+ when Proc
198
+ @challenge_action.call(env)
199
+ else
200
+ nil
201
+ end
202
+ end
203
+
204
+ def build_verification_failure_url(request)
205
+ if @verification_failure_url.respond_to?(:call)
206
+ @verification_failure_url.call(request)
207
+ elsif @verification_failure_url
208
+ @verification_failure_url
209
+ elsif @blocked_page_path
210
+ base = request.base_url
211
+ path = @blocked_page_path.start_with?("/") ? @blocked_page_path : "/#{@blocked_page_path}"
212
+ "#{base}#{path}"
213
+ end
214
+ rescue StandardError
215
+ nil
216
+ end
217
+
218
+ def resolve_session_id(env)
219
+ return @verification_session_id.call(env) if @verification_session_id.respond_to?(:call)
220
+ return @verification_session_id if @verification_session_id.is_a?(String)
221
+
222
+ if @verification_session_id.is_a?(Symbol)
223
+ session = env["rack.session"]
224
+ return session[@verification_session_id] if session
225
+ end
226
+
227
+ session = env["rack.session"]
228
+ return nil unless session
229
+
230
+ return session.id if session.respond_to?(:id) && session.id
231
+
232
+ options = env["rack.session.options"]
233
+ return options[:id] if options.is_a?(Hash) && options[:id]
234
+
235
+ session[:session_id] || session["session_id"]
236
+ rescue StandardError
237
+ nil
238
+ end
239
+
240
+ def verified_session?(env)
241
+ return true if env["reputable.verified"]
242
+
243
+ session = env["rack.session"]
244
+ return false unless session
245
+
246
+ @verified_session_keys.any? do |key|
247
+ session[key] || session[key.to_s]
248
+ end
249
+ rescue StandardError
250
+ false
251
+ end
252
+
253
+ def build_blocked_page_response(client_ip)
254
+ options = blocked_page_options.merge(client_ip: client_ip)
255
+ Reputable::BlockedPage.response(**options)
256
+ end
257
+
258
+ def blocked_page_options
259
+ config = Reputable.configuration
260
+ defaults = {
261
+ site_name: config.site_name,
262
+ support_email: config.support_email,
263
+ support_url: config.support_url
264
+ }
265
+ defaults.merge(@blocked_page_options)
266
+ end
267
+
268
+ def redirect_response(url, status)
269
+ headers = {
270
+ "Location" => url,
271
+ "Content-Type" => "text/html; charset=utf-8",
272
+ "Cache-Control" => "no-store"
273
+ }
274
+ [status, headers, ["Redirecting to verification..."]]
275
+ end
276
+
277
+ def resolve_blocked_redirect_url(env)
278
+ return @blocked_redirect_url.call(env) if @blocked_redirect_url.respond_to?(:call)
279
+ return @blocked_redirect_url if @blocked_redirect_url.is_a?(String)
280
+
281
+ nil
282
+ rescue StandardError
283
+ nil
284
+ end
285
+
96
286
  def skip_request?(env)
97
287
  return true if @skip_if&.call(env)
98
288
 
@@ -7,6 +7,10 @@ module Reputable
7
7
  module ControllerHelpers
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ included do
11
+ helper_method :reputable_verified? if respond_to?(:helper_method)
12
+ end
13
+
10
14
  # Track the current request with optional extra tags
11
15
  def track_reputable_request(tags: [], **options)
12
16
  Reputable::Tracker.track_request(
@@ -81,6 +85,139 @@ module Reputable
81
85
  def current_ip_status
82
86
  Reputable::Reputation.lookup_ip(request.remote_ip)
83
87
  end
88
+
89
+ # ========================================
90
+ # Verification redirect helpers
91
+ # ========================================
92
+
93
+ # Render the standard blocked page
94
+ def render_reputable_blocked_page(
95
+ status: 403,
96
+ site_name: nil,
97
+ support_email: nil,
98
+ support_url: nil,
99
+ heading: "Access blocked",
100
+ message: nil,
101
+ show_ip: true
102
+ )
103
+ config = Reputable.configuration
104
+ html = Reputable::BlockedPage.html(
105
+ site_name: site_name || config.site_name,
106
+ support_email: support_email || config.support_email,
107
+ support_url: support_url || config.support_url,
108
+ client_ip: request.remote_ip,
109
+ heading: heading,
110
+ message: message,
111
+ show_ip: show_ip
112
+ )
113
+
114
+ render html: html.html_safe, status: status, content_type: "text/html"
115
+ end
116
+
117
+ # Check if the current session has already passed verification
118
+ # @param session_key [Symbol]
119
+ # @return [Boolean]
120
+ def reputable_verified?(session_key: :reputable_verified_at)
121
+ session[session_key].present?
122
+ end
123
+
124
+ # Clear verification status for the current session
125
+ # @param session_key [Symbol]
126
+ def clear_reputable_verification!(session_key: :reputable_verified_at)
127
+ session.delete(session_key)
128
+ end
129
+
130
+ # Enforce verification redirect flow.
131
+ # - If already verified in this session, returns immediately.
132
+ # - If returning with signature, validates and marks session.
133
+ # - Otherwise redirects to verification URL.
134
+ #
135
+ # @param return_url [String] URL to return to after verification
136
+ # @param failure_url [String] URL to return to on failure/invalid token
137
+ # @param session_id [String] Optional session id to link
138
+ # @param force_challenge [Boolean] Force challenge even if trusted
139
+ # @param session_key [Symbol] Session key used to store verified state
140
+ def require_reputable_verification!(
141
+ return_url: request.original_url,
142
+ failure_url: nil,
143
+ session_id: session.id,
144
+ force_challenge: false,
145
+ session_key: :reputable_verified_at
146
+ )
147
+ return if reputable_verified?(session_key: session_key)
148
+
149
+ if params[:reputable_signature]
150
+ if Reputable.verify_redirect_return(params)
151
+ if params[:reputable_status] == "pass"
152
+ session[session_key] = Time.now.to_i
153
+ return
154
+ end
155
+
156
+ redirect_to failure_url and return
157
+ else
158
+ render plain: "Verification failed", status: 403 and return
159
+ end
160
+ end
161
+
162
+ redirect_to reputable_verification_url(
163
+ return_url: return_url,
164
+ failure_url: failure_url,
165
+ session_id: session_id,
166
+ force_challenge: force_challenge
167
+ ) and return
168
+ end
169
+
170
+ # Enforce reputation-based access control.
171
+ # - If IP is blocked, render a blocked page.
172
+ # - If IP is challenged, run verification flow.
173
+ def require_reputable_reputation!(
174
+ return_url: request.original_url,
175
+ failure_url: nil,
176
+ session_id: session.id,
177
+ force_challenge: false,
178
+ session_key: :reputable_verified_at,
179
+ blocked_status: 403,
180
+ blocked_site_name: nil,
181
+ blocked_support_email: nil,
182
+ blocked_support_url: nil,
183
+ blocked_heading: "Access blocked",
184
+ blocked_message: nil,
185
+ blocked_show_ip: true
186
+ )
187
+ if current_ip_blocked?
188
+ render_reputable_blocked_page(
189
+ status: blocked_status,
190
+ site_name: blocked_site_name,
191
+ support_email: blocked_support_email,
192
+ support_url: blocked_support_url,
193
+ heading: blocked_heading,
194
+ message: blocked_message,
195
+ show_ip: blocked_show_ip
196
+ )
197
+ return
198
+ end
199
+
200
+ return unless current_ip_challenged?
201
+
202
+ require_reputable_verification!(
203
+ return_url: return_url,
204
+ failure_url: failure_url,
205
+ session_id: session_id,
206
+ force_challenge: force_challenge,
207
+ session_key: session_key
208
+ )
209
+ end
210
+
211
+ private
212
+
213
+ def reputable_verification_url(return_url:, failure_url:, session_id:, force_challenge:)
214
+ Reputable.verification_url(
215
+ return_url: return_url,
216
+ failure_url: failure_url,
217
+ session_id: session_id,
218
+ force_challenge: force_challenge
219
+ )
220
+ end
84
221
  end
85
222
 
86
223
  # Railtie for automatic Rails integration (only defined when Rails is present)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reputable
4
- VERSION = "0.1.7"
4
+ VERSION = "0.1.9"
5
5
  end
data/lib/reputable.rb CHANGED
@@ -7,6 +7,7 @@ require_relative "reputable/configuration"
7
7
  require_relative "reputable/connection"
8
8
  require_relative "reputable/tracker"
9
9
  require_relative "reputable/reputation"
10
+ require_relative "reputable/blocked_page"
10
11
  require_relative "reputable/middleware"
11
12
 
12
13
  # Optional Rails integration (only load if Rails is defined)
@@ -145,6 +146,7 @@ module Reputable
145
146
  # Ensure base_url doesn't have a trailing slash, then append the verify path
146
147
  base_url = base_url.chomp("/")
147
148
  verify_url = "#{base_url}/_reputable/verify"
149
+ failure_url ||= "#{base_url}/_reputable/verify/failure"
148
150
 
149
151
  # JWT Header
150
152
  header = { alg: "HS256", typ: "JWT" }
@@ -170,6 +172,14 @@ module Reputable
170
172
  "#{verify_url}?token=#{token}"
171
173
  end
172
174
 
175
+ # Build the hosted blocked page URL
176
+ # @return [String]
177
+ def blocked_page_url
178
+ base_url = configuration.base_url
179
+ base_url = base_url.chomp("/")
180
+ "#{base_url}/_reputable/verify/blocked"
181
+ end
182
+
173
183
  # Verify the signature of a redirect return
174
184
  # @param params [Hash] Request query parameters
175
185
  # @return [Boolean] true if valid logic and passed signature check
data/reputable.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/reputable/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "reputable"
7
+ spec.version = Reputable::VERSION
8
+ spec.authors = ["Reputable"]
9
+ spec.email = ["support@reputable.click"]
10
+
11
+ spec.summary = "Ruby client for Reputable - bot detection and reputation scoring"
12
+ spec.description = "Track requests and manage IP reputation through Redis/Dragonfly integration with Reputable"
13
+ spec.homepage = "https://github.com/reputable-click/reputable-rb"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) || f.end_with?(".gem")
24
+ end
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "redis", ">= 4.0", "< 6.0"
31
+ spec.add_dependency "connection_pool", "~> 2.2"
32
+
33
+ spec.add_development_dependency "bundler", "~> 2.0"
34
+ spec.add_development_dependency "rake", "~> 13.0"
35
+ spec.add_development_dependency "rspec", "~> 3.12"
36
+ spec.add_development_dependency "rack", "~> 2.0"
37
+ spec.add_development_dependency "rubocop", "~> 1.0"
38
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reputable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reputable
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-28 00:00:00.000000000 Z
11
+ date: 2025-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -135,6 +135,7 @@ files:
135
135
  - lib/reputable/reputation.rb
136
136
  - lib/reputable/tracker.rb
137
137
  - lib/reputable/version.rb
138
+ - reputable.gemspec
138
139
  homepage: https://github.com/reputable-click/reputable-rb
139
140
  licenses:
140
141
  - MIT