safe_request_timeout 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 742cdb842214b5b66fa2fb127c79005ce2b3072776c59a20564433a9f5e82a8c
4
+ data.tar.gz: 592e4127ace138dadeabcf7e9dc5bbb129a20e8895e600a009e588249cea668f
5
+ SHA512:
6
+ metadata.gz: 61be0a04e1f993c83dd520c151d7a76817be66eff64200898d336cfda5e70668c53a320462a5c161818e017ad17c6ab39c11fc2ceae1405c5ca1682eb511b17e
7
+ data.tar.gz: bcf57aafa0db0db508283d5c6f49ec611a6b3366e19542a014015f731e623c3dfc1f9eda4b239661f24b673a9310887355a37dbed568489fe7961ebda8d108dc
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 1.0.0
8
+
9
+ ### Added
10
+ - Initial release.
data/MIT_LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2023 Brian Durand
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # Request Timeout
2
+
3
+ [![Continuous Integration](https://github.com/bdurand/safe_request_timeout/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/safe_request_timeout/actions/workflows/continuous_integration.yml)
4
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
5
+
6
+ This gem provides a safe and convenient mechanism for adding a timeout mechanism to a block of code. The gem ensures that the timeout is safe to call and will not raise timeout errors from random places in your code which can leave your application in an indeterminate state.
7
+
8
+ It is designed to work in situations where there is a general timeout needed on some kind of request. For instance, consider a Rack HTTP request. This request may be behind a web server running in a separate process with it's own timeout where it sends an error back to the client when the application is taking too long to process the request. However, your Ruby application won't know anything about this and will continue processing the request and generating a response for a client that is no longer going to receive the response which just wastes server resources.
9
+
10
+ When requests start timing out due to an external issue like a slow database query, then it is more difficult to recover and can cascade an isolated issue into a general site outage. Often the timeouts you have on resources like database connections won't cover this case either since individual queries might never hit the timeout limit.
11
+
12
+ Unlike the `Timeout` class in the Ruby standard library, this code is very explicit about where timeout errors can be raised, so you don't need to worry about a timeout error in an unexpected place leaving your application in an indeterminate state.
13
+
14
+ There is built in support for Rails applications. For other frameworks you will need to add some middleware and hooks to implement the timeout mechanism.
15
+
16
+ ## Usage
17
+
18
+ You can wrap code in a timout block.
19
+
20
+ ```ruby
21
+ SafeRequestTimeout.timeout(15) do
22
+ ...
23
+ end
24
+ ```
25
+
26
+ By itself, this won't do anything. Unlike normal timeouts, there is no background process that will kill the operation after a defined period. Instead, you will need to periodically call `SafeRequestTimeout.check_timeout!` from within your code. Calling this method within a timeout block will raise an error if the time spent in that block has exceeded the max allowed. Calling it outside of a timeout block will do nothing.
27
+
28
+ It is generally best to call the `check_timeout!` method before doing an expensive operation since there's no point in timing out after the work has already been done. This method will also clear the current timeout, so you don't have to worry about it generating a cascading series of timeout errors in any error handling code.
29
+
30
+ ```ruby
31
+ SafeRequestTimeout.timeout(5) do
32
+ 1000.times do
33
+ # This will raise an error if the loop takes longer than 5 seconds.
34
+ SafeRequestTimeout.check_timeout!
35
+ do_somthing
36
+ end
37
+ end
38
+ ```
39
+
40
+ You can also set a timeout value retroactively from within a `timeout` block. You can use this feature to change the timeout based on application state.
41
+
42
+ ```ruby
43
+ # Setting a timeout of nil will set up a block that will never timout.
44
+ SafeRequestTimeout.timeout(nil) do
45
+ # Set the timeout duration to 5 seconds for non-admin users
46
+ SafeRequestTimeout.set_timeout(5) unless current_user.admin?
47
+
48
+ do_something
49
+ end
50
+ ```
51
+
52
+ You can also set the timeout duration with a `Proc` that will be evaluated at runtime.
53
+
54
+ ```ruby
55
+ SafeRequestTimeout.timeout(lambda { CurrentUser.new.admin? ? nil : 5 })
56
+ ...
57
+ end
58
+ ```
59
+
60
+ You can clear the timeout if you want to ensure a block of code can run without begin timed out (i.e. if you need to run cleanup code).
61
+
62
+ ```ruby
63
+ SafeRequestTimeout.timeout(5) do
64
+ begin
65
+ do_something
66
+ ensure
67
+ SafeRequestTimeout.clear_timeout
68
+ cleanup_request
69
+ end
70
+ end
71
+ ```
72
+
73
+ ### Hooks
74
+
75
+ You can add hooks into other classes to check the current timeout if you don't want to have to sprinkle `SafeRequestTimeout.check_timeout!` throughout your code. To do this, use the `SafeRequestTimeout.add_timeout!` method. You need to specify the class and methods where you want to add the timeout hooks:
76
+
77
+ ```ruby
78
+ # Add a timeout check to the MyDriver#make_request method.
79
+ SafeRequestTimeout::Hooks.add_timeout!(MyDriver, [:make_request])
80
+ ```
81
+
82
+ ### Rack Middleware
83
+
84
+ This gem ships with Rack middleware that can set up a timeout block on all Rack requests. In a Rack application you would use this code to add a 15 second timeout to all requests to `app`.
85
+
86
+ ```ruby
87
+ RackBuilder.new do
88
+ use SafeRequestTimeout::RackMiddleware, 15
89
+ run app
90
+ end
91
+ ```
92
+
93
+ If you want to customize the timeout per request, you can call `SafeRequestTimeout.set_timeout` inside your request handling to change the value for the current request. You can also define the timeout duration with a `Proc` which will be called at runtime with the `env` object passed for the request.
94
+
95
+ ```ruby
96
+ RackBuilder.new do
97
+ use SafeRequestTimeout::RackMiddleware, lambda { |env|
98
+ 10 unless Rack::Request.new(env).path.start_with?("/admin")
99
+ }
100
+ run app
101
+ end
102
+ ```
103
+
104
+ ### Sidekiq Middleware
105
+
106
+ This gem ships with Sidekiq middleware that can add timeout support to Sidekiq workers. The middleware needs to be added to the server middleware in the Sidekiq initialization.
107
+
108
+ ```ruby
109
+ Sidekiq.configure_server do |config|
110
+ config.server_middleware do |chain|
111
+ chain.add SafeRequestTimeout::SidekiqMiddleware
112
+ end
113
+ end
114
+ ```
115
+
116
+ You can then specify a timeout per worker with the `safe_request_timeout` sidekiq option.
117
+
118
+ ```
119
+ class SlowWorker
120
+ include Sidekiq::Worker
121
+
122
+ # Set a 15 second timeout for the worker to finish.
123
+ sidekiq_options safe_request_timeout: 15
124
+ end
125
+ ```
126
+
127
+ ### Rails
128
+
129
+ This gem comes with built in support for Rails applications.
130
+
131
+ - The Rack middleware is added to the middleware chain. There is no timeout value set by default. You can specify a global one by setting `safe_request_timout.rack_timeout` in your Rails configuration.
132
+
133
+ - If Sidekiq is being used, then the Sidekiq middleware is added. Sidekiq workers can specify a timeout with the `safe_request_timeout` option.
134
+
135
+ - A timeout block is added around ActiveJob execution. Jobs can specify a timeout by calling `SafeRequestTimeout.set_timeout` in the `perform` method or in a `before_perform` callback.
136
+
137
+ - A timeout check is added on all ActiveRecord queries. The timeout is cleared when a database transaction is committed so that you won't unexpectedly timeout a request after making persistent changes. You can disable these hooks by setting `safe_request_timeout.active_record_hook` to false in your Rails configuration.
138
+
139
+ ## Installation
140
+
141
+ Add this line to your application's Gemfile:
142
+
143
+ ```ruby
144
+ gem 'safe_request_timeout'
145
+ ```
146
+
147
+ And then execute:
148
+ ```bash
149
+ $ bundle
150
+ ```
151
+
152
+ Or install it yourself as:
153
+ ```bash
154
+ $ gem install safe_request_timeout
155
+ ```
156
+
157
+ ## Contributing
158
+
159
+ Open a pull request on GitHub.
160
+
161
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
162
+
163
+ ## License
164
+
165
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/dependabot.yml ADDED
@@ -0,0 +1,12 @@
1
+ # Dependabot update strategy
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: bundler
5
+ directory: "/"
6
+ schedule:
7
+ interval: daily
8
+ allow:
9
+ # Automatically keep all runtime dependencies updated
10
+ - dependency-name: "*"
11
+ dependency-type: "production"
12
+ versioning-strategy: lockfile-only
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ class ActiveRecordHook
5
+ class << self
6
+ # Add the timeout hook to the connection class.
7
+ #
8
+ # @param connection_class [Class] The class to add the timeout hook to.
9
+ # @return [void]
10
+ def add_timeout!(connection_class = nil)
11
+ connection_class ||= ::ActiveRecord::Base.connection.class
12
+
13
+ SafeRequestTimeout::Hooks.add_timeout!(connection_class, [:exec_query])
14
+
15
+ SafeRequestTimeout::Hooks.clear_timeout!(connection_class, [:commit_db_transaction])
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ # Hooks into other classes from other libraries with timeout blocks. This allows
5
+ # timeouts to be automatically checked before making requests to external services.
6
+ module Hooks
7
+ class << self
8
+ # Hooks into a class by surrounding specified instance methods with timeout checks.
9
+ def add_timeout!(klass, methods, module_name = nil)
10
+ hooks_module = create_module(klass, module_name, "AddTimeout")
11
+
12
+ Array(methods).each do |method_name|
13
+ hooks_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
14
+ def #{method_name}(#{splat_args})
15
+ SafeRequestTimeout.check_timeout!
16
+ super(#{splat_args})
17
+ end
18
+ RUBY
19
+ end
20
+
21
+ klass.prepend(hooks_module)
22
+ end
23
+
24
+ def clear_timeout!(klass, methods, module_name = nil)
25
+ hooks_module = create_module(klass, module_name, "ClearTimeout")
26
+
27
+ Array(methods).each do |method_name|
28
+ hooks_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
29
+ def #{method_name}(#{splat_args})
30
+ SafeRequestTimeout.clear_timeout
31
+ super(#{splat_args})
32
+ end
33
+ RUBY
34
+ end
35
+
36
+ klass.prepend(hooks_module)
37
+ end
38
+
39
+ private
40
+
41
+ def create_module(klass, module_name, module_type)
42
+ # Create a module that will be prepended to the specified class.
43
+ unless module_name
44
+ camelized_name = name.to_s.gsub(/[^a-z0-9]+([a-z0-9])/i) { |m| m[m.length - 1, m.length].upcase }
45
+ camelized_name = "#{camelized_name[0].upcase}#{camelized_name[1, camelized_name.length]}"
46
+ module_name = "#{klass.name.split("::").join}#{camelized_name}#{module_type}"
47
+ end
48
+
49
+ if const_defined?(module_name)
50
+ raise ArgumentError.new("Cannot create duplicate #{module_name} for hooking #{name} into #{klass.name}")
51
+ end
52
+
53
+ # Dark arts & witchery to dynamically generate the module methods.
54
+ const_set(module_name, Module.new)
55
+ end
56
+
57
+ def splat_args
58
+ # The method of overriding kwargs changed in ruby 2.7
59
+ ruby_major, ruby_minor, _ = RUBY_VERSION.split(".").collect(&:to_i)
60
+ ruby_3_args = (ruby_major >= 3 || (ruby_major == 2 && ruby_minor >= 7))
61
+ (ruby_3_args ? "..." : "*args, &block")
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ # Rack middleware that adds a timeout block to all requests.
5
+ class RackMiddleware
6
+ # @param app [Object] The Rack application to wrap.
7
+ # @param timeout [Integer, Proc, nil] The timeout in seconds.
8
+ def initialize(app, timeout = nil)
9
+ @app = app
10
+ @timeout = timeout
11
+ @timeout_block = true if timeout.is_a?(Proc) && timeout.arity == 1
12
+ end
13
+
14
+ def call(env)
15
+ SafeRequestTimeout.timeout(@timeout_block ? @timeout.call(env) : @timeout) do
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ class Railtie < Rails::Railtie
5
+ config.safe_request_timeout = ActiveSupport::OrderedOptions.new
6
+ config.safe_request_timeout.active_record_hook = true
7
+ config.safe_request_timeout.rack_timeout = nil
8
+
9
+ initializer "safe_request_timeout" do |app|
10
+ if app.config.safe_request_timeout.active_record_hook
11
+ ActiveSupport.on_load(:active_record) do
12
+ SafeRequestTimeout::ActiveRecordHook.add_timeout!
13
+ end
14
+ end
15
+
16
+ if defined?(ActiveJob::Base.around_perform)
17
+ ActiveJob::Base.around_perform do |job, block|
18
+ SafeRequestTimeout.timeout(nil, &block)
19
+ end
20
+ end
21
+
22
+ app.middleware.use SafeRequestTimeout::RackMiddleware, app.config.safe_request_timeout.rack_timeout
23
+
24
+ if defined?(Sidekiq.server?) && Sidekiq.server?
25
+ Sidekiq.configure_server do |sidekiq_config|
26
+ sidekiq_config.server_middleware do |chain|
27
+ chain.add SafeRequestTimeout::SidekiqMiddleware
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ # Sidekiq server middleware that wraps job execution with a timeout. The timeout
5
+ # is set in a job's "safe_request_timeout" option.
6
+ class SidekiqMiddleware
7
+ if defined?(Sidekiq::ServerMiddleware)
8
+ include Sidekiq::ServerMiddleware
9
+ end
10
+
11
+ def call(job_instance, job_payload, queue)
12
+ SafeRequestTimeout.timeout(job_payload["safe_request_timeout"]) do
13
+ yield
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeRequestTimeout
4
+ VERSION = File.read(File.expand_path("../../VERSION", __dir__)).chomp.freeze
5
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+
5
+ # This module adds the capability to add a general timeout to any block of code.
6
+ # Unlike the Timeout module, an error is not raised to indicate a timeout.
7
+ # Instead, the `timed_out?` method can be used to check if the block of code
8
+ # has taken longer than the specified duration so the application can take
9
+ # the appropriate action.
10
+ #
11
+ # This is a safer alternative to the Timeout module because it does not fork new
12
+ # threads or risk raising errors from unexpected places.
13
+ #
14
+ # @example
15
+ # SafeRequestTimeout.timeout(5) do
16
+ # # calling check_timeout! will raise an error if the block has taken
17
+ # # longer than 5 seconds to execute.
18
+ # SafeRequestTimeout.check_timeout!
19
+ # end
20
+ module SafeRequestTimeout
21
+ class TimeoutError < ::Timeout::Error
22
+ end
23
+
24
+ class << self
25
+ # Execute the given block with a timeout. If the block takes longer than the specified
26
+ # duration to execute, then the `timed_out?`` method will return true within the block.
27
+ #
28
+ # @param duration [Integer] the number of seconds to wait before timing out
29
+ # @yield the block to execute
30
+ # @yieldreturn [Object] the result of the block
31
+ def timeout(duration, &block)
32
+ duration = duration.call if duration.respond_to?(:call)
33
+
34
+ previous_start_at = Thread.current[:safe_request_timeout_started_at]
35
+ previous_timeout_at = Thread.current[:safe_request_timeout_timeout_at]
36
+
37
+ start_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
38
+ timeout_at = start_at + duration if duration
39
+ if timeout_at && previous_timeout_at && previous_timeout_at < timeout_at
40
+ timeout_at = previous_timeout_at
41
+ end
42
+
43
+ begin
44
+ Thread.current[:safe_request_timeout_started_at] = start_at
45
+ Thread.current[:safe_request_timeout_timeout_at] = timeout_at
46
+ yield
47
+ ensure
48
+ Thread.current[:safe_request_timeout_started_at] = previous_start_at
49
+ Thread.current[:safe_request_timeout_timeout_at] = previous_timeout_at
50
+ end
51
+ end
52
+
53
+ # Check if the current timeout block has timed out.
54
+ #
55
+ # @return [Boolean] true if the current timeout block has timed out
56
+ def timed_out?
57
+ timeout_at = Thread.current[:safe_request_timeout_timeout_at]
58
+ !!timeout_at && Process.clock_gettime(Process::CLOCK_MONOTONIC) > timeout_at
59
+ end
60
+
61
+ # Raise an error if the current timeout block has timed out. If there is no timeout block,
62
+ # then this method does nothing. If an error is raised, then the current timeout
63
+ # is cleared to prevent the error from being raised multiple times.
64
+ #
65
+ # @return [void]
66
+ # @raise [SafeRequestTimeout::TimeoutError] if the current timeout block has timed out
67
+ def check_timeout!
68
+ if timed_out?
69
+ Thread.current[:safe_request_timeout_timeout_at] = nil
70
+ raise TimeoutError.new("after #{time_elapsed.round(6)} seconds")
71
+ end
72
+ end
73
+
74
+ # Get the number of seconds remaining in the current timeout block or nil if there is no
75
+ # timeout block.
76
+ #
77
+ # @return [Float, nil] the number of seconds remaining in the current timeout block
78
+ def time_remaining
79
+ timeout_at = Thread.current[:safe_request_timeout_timeout_at]
80
+ [timeout_at - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0.0].max if timeout_at
81
+ end
82
+
83
+ # Get the number of seconds elapsed in the current timeout block or nil if there is no
84
+ # timeout block.
85
+ #
86
+ # @return [Float, nil] the number of seconds elapsed in the current timeout block began
87
+ def time_elapsed
88
+ start_at = Thread.current[:safe_request_timeout_started_at]
89
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_at if start_at
90
+ end
91
+
92
+ # Set the duration for the current timeout block. This is useful if you want to set the duration
93
+ # after the timeout block has started. The timer for the timeout block will restart whenever
94
+ # a new duration is set.
95
+ #
96
+ # @return [void]
97
+ def set_timeout(duration)
98
+ if Thread.current[:safe_request_timeout_started_at]
99
+ start_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
100
+ duration = duration.call if duration.respond_to?(:call)
101
+ timeout_at = start_at + duration if duration
102
+ Thread.current[:safe_request_timeout_started_at] = start_at
103
+ Thread.current[:safe_request_timeout_timeout_at] = timeout_at
104
+ end
105
+ end
106
+
107
+ # Clear the current timeout. If a block is passed, then the timeout will be cleared
108
+ # only for the duration of the block.
109
+ #
110
+ # @yield the block to execute if one is given
111
+ # @yieldreturn [Object] the result of the block
112
+ def clear_timeout(&block)
113
+ if block
114
+ timeout(nil, &block)
115
+ else
116
+ set_timeout(nil)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ require_relative "safe_request_timeout/hooks"
123
+ require_relative "safe_request_timeout/active_record_hook"
124
+ require_relative "safe_request_timeout/rack_middleware"
125
+ require_relative "safe_request_timeout/sidekiq_middleware"
126
+ require_relative "safe_request_timeout/version"
127
+
128
+ if defined?(Rails::Railtie)
129
+ require_relative "safe_request_timeout/railtie"
130
+ end
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "safe_request_timeout"
3
+ spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
4
+ spec.authors = ["Brian Durand"]
5
+ spec.email = ["bbdurand@gmail.com"]
6
+
7
+ spec.summary = "Mechanism for safely aborting long-running requests after a specified timeout."
8
+ spec.homepage = "https://github.com/bdurand/safe_request_timeout"
9
+ spec.license = "MIT"
10
+
11
+ # Specify which files should be added to the gem when it is released.
12
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
+ ignore_files = %w[
14
+ .
15
+ Appraisals
16
+ Gemfile
17
+ Gemfile.lock
18
+ Rakefile
19
+ bin/
20
+ gemfiles/
21
+ spec/
22
+ ]
23
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
+ end
26
+
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "redis"
30
+
31
+ spec.add_development_dependency "bundler"
32
+
33
+ spec.required_ruby_version = ">= 2.5"
34
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_request_timeout
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Durand
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - bbdurand@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - MIT_LICENSE.txt
50
+ - README.md
51
+ - VERSION
52
+ - dependabot.yml
53
+ - lib/safe_request_timeout.rb
54
+ - lib/safe_request_timeout/active_record_hook.rb
55
+ - lib/safe_request_timeout/hooks.rb
56
+ - lib/safe_request_timeout/rack_middleware.rb
57
+ - lib/safe_request_timeout/railtie.rb
58
+ - lib/safe_request_timeout/sidekiq_middleware.rb
59
+ - lib/safe_request_timeout/version.rb
60
+ - safe_request_timeout.gemspec
61
+ homepage: https://github.com/bdurand/safe_request_timeout
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '2.5'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.4.12
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Mechanism for safely aborting long-running requests after a specified timeout.
84
+ test_files: []