cutoff 0.2.0 → 0.4.2
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 +4 -4
- data/CHANGELOG.md +23 -1
- data/README.md +95 -9
- data/lib/cutoff/patch/mysql2.rb +7 -6
- data/lib/cutoff/patch/net_http.rb +28 -16
- data/lib/cutoff/rails/controller.rb +48 -0
- data/lib/cutoff/rails.rb +3 -0
- data/lib/cutoff/sidekiq.rb +40 -0
- data/lib/cutoff/timer.rb +32 -0
- data/lib/cutoff/version.rb +1 -1
- data/lib/cutoff.rb +82 -40
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00ff1f831beec4fed582b5faa9ed42c350c13b32217bdcab0b850445ba9c358
|
4
|
+
data.tar.gz: 77d466db54a822240d2d27cfdb120f98aa7f7435ec62ae15191292702580de79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9821d7010a4663ce25497a129c5a04166b5f701bd30d1d7a20f183cd274444605f3ac14099857c22117d45ac980056f4ccbaf09e8b65403e83a93abc923ced90
|
7
|
+
data.tar.gz: ec1f98a8885f8938ba421d87cb82f7f16a79161a778d2e4c230204f2243009b001e4c5de8c95ca1153746a8eebb90f969ca5efe80390cd08f901ee943a06581f
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.4.2] - 2021-10-14
|
11
|
+
|
12
|
+
- Add sidekiq middleware
|
13
|
+
- Select checkpoints to enable or disable
|
14
|
+
|
15
|
+
## [0.4.1] - 2021-10-02
|
16
|
+
|
17
|
+
- Fix Net::HTTP patch to override timeouts given to start
|
18
|
+
|
19
|
+
## [0.4.0] - 2021-10-01
|
20
|
+
|
21
|
+
- Add benchmarks and slight performance improvements
|
22
|
+
- Add Rails controller integration
|
23
|
+
|
24
|
+
## [0.3.0] - 2021-08-20
|
25
|
+
|
26
|
+
- Allow timers to be disabled globally with `Cutoff.disable!`
|
27
|
+
|
10
28
|
## [0.2.0] - 2021-07-22
|
11
29
|
|
12
30
|
### Added
|
@@ -20,6 +38,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
20
38
|
- Cutoff class
|
21
39
|
- Mysql2 patch
|
22
40
|
|
23
|
-
[Unreleased]: https://github.com/justinhoward/cutoff/compare/v0.
|
41
|
+
[Unreleased]: https://github.com/justinhoward/cutoff/compare/v0.4.1...HEAD
|
42
|
+
[0.4.2]: https://github.com/justinhoward/cutoff/compare/v0.4.1...v0.4.2
|
43
|
+
[0.4.1]: https://github.com/justinhoward/cutoff/compare/v0.4.0...v0.4.1
|
44
|
+
[0.4.0]: https://github.com/justinhoward/cutoff/compare/v0.3.0...v0.4.0
|
45
|
+
[0.3.0]: https://github.com/justinhoward/cutoff/compare/v0.2.0...v0.3.0
|
24
46
|
[0.2.0]: https://github.com/justinhoward/cutoff/compare/v0.1.0...v0.2.0
|
25
47
|
[0.1.0]: https://github.com/justinhoward/cutoff/releases/tag/v0.1.0
|
data/README.md
CHANGED
@@ -217,28 +217,78 @@ Cutoff.wrap(3) do
|
|
217
217
|
end
|
218
218
|
```
|
219
219
|
|
220
|
+
Selecting Checkpoints
|
221
|
+
---------------------------
|
222
|
+
|
223
|
+
In some cases, you may want to select some checkpoints to use, but not others.
|
224
|
+
For example, you may want to run some code that contains MySQL queries, but not
|
225
|
+
use the mysql2 patch. The `exclude` and `only` options support this.
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
Cutoff.wrap(10, exclude: :mysql2) do
|
229
|
+
# The mysql2 patch won't be used here
|
230
|
+
end
|
231
|
+
|
232
|
+
Cutoff.wrap(10, only: %i[foo bar]) do
|
233
|
+
# These checkpoints will be used
|
234
|
+
Cutoff.checkpoint!(:foo)
|
235
|
+
Cutoff.checkpoint!(:bar)
|
236
|
+
|
237
|
+
# These checkpoints will be skipped
|
238
|
+
Cutoff.checkpoint!(:asdf)
|
239
|
+
Cutoff.checkpoint!
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
220
243
|
Timing a Rails Controller
|
221
244
|
---------------------------
|
222
245
|
|
223
|
-
One use of a cutoff is to add a deadline to a Rails controller action.
|
246
|
+
One use of a cutoff is to add a deadline to a Rails controller action. This is
|
247
|
+
typically preferable to approaches like `Rack::Timeout` that use the dangerous
|
248
|
+
`Timeout` class.
|
249
|
+
|
250
|
+
Cutoff includes a built-in integration for this purpose. If Rails is installed,
|
251
|
+
the `#cutoff` class method is available in your controllers.
|
224
252
|
|
225
253
|
```ruby
|
226
|
-
|
254
|
+
class ApplicationController < ActionController::Base
|
255
|
+
# You may want to set a long global cutoff, but it's not required
|
256
|
+
cutoff 30
|
257
|
+
end
|
258
|
+
|
259
|
+
class UsersController < ApplicationController
|
260
|
+
cutoff 5.0
|
261
|
+
|
262
|
+
def index
|
263
|
+
# Now in your action, you can call `checkpoint!`, or if you're using the
|
264
|
+
# patches, checkpoints will be added automatically
|
265
|
+
Cutoff.checkpoint!
|
266
|
+
end
|
267
|
+
end
|
227
268
|
```
|
228
269
|
|
229
|
-
|
230
|
-
patch, checkpoints will be added automatically.
|
270
|
+
Just like with controller filters, you can use filters with the cutoff method.
|
231
271
|
|
232
272
|
```ruby
|
233
|
-
|
234
|
-
#
|
235
|
-
|
273
|
+
class UsersController < ApplicationController
|
274
|
+
# For example, use an :only filter
|
275
|
+
cutoff 5.0, only: :index
|
276
|
+
|
277
|
+
# Multiple calls work just fine. Last match wins
|
278
|
+
cutoff 2.5, only: :show
|
236
279
|
|
237
|
-
|
280
|
+
def index
|
281
|
+
# ...
|
282
|
+
end
|
283
|
+
|
284
|
+
def show
|
285
|
+
# ...
|
286
|
+
end
|
238
287
|
end
|
239
288
|
```
|
240
289
|
|
241
|
-
Consider adding a global error handler for the `Cutoff::CutoffExceededError`
|
290
|
+
Consider adding a global error handler for the `Cutoff::CutoffExceededError` in
|
291
|
+
case you want to display a nice error page for timeouts.
|
242
292
|
|
243
293
|
```ruby
|
244
294
|
class ApplicationController < ActionController::Base
|
@@ -250,6 +300,42 @@ class ApplicationController < ActionController::Base
|
|
250
300
|
end
|
251
301
|
```
|
252
302
|
|
303
|
+
Timing Sidekiq Workers
|
304
|
+
------------
|
305
|
+
|
306
|
+
If Sidekiq is loaded, Cutoff includes middleware to support a `:cutoff` option.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
class MyWorker
|
310
|
+
include Sidekiq::Worker
|
311
|
+
|
312
|
+
sidekiq_options cutoff: 6.0
|
313
|
+
|
314
|
+
def perform
|
315
|
+
# ...
|
316
|
+
Cutoff.checkpoint!
|
317
|
+
# ...
|
318
|
+
end
|
319
|
+
end
|
320
|
+
```
|
321
|
+
|
322
|
+
|
323
|
+
Disabling Cutoff for Testing and Development
|
324
|
+
------------
|
325
|
+
|
326
|
+
When testing or debugging an application that uses Cutoff, you may want to
|
327
|
+
disable Cutoff entirely. These methods are not thread-safe and not intended for
|
328
|
+
production.
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
# This disables all cutoff timers, for both global and local instances
|
332
|
+
Cutoff.disable!
|
333
|
+
Cutoff.disabled? # => true
|
334
|
+
|
335
|
+
# Re-enable cutoff
|
336
|
+
Cutoff.enable!
|
337
|
+
```
|
338
|
+
|
253
339
|
Multi-threading
|
254
340
|
-----------------
|
255
341
|
|
data/lib/cutoff/patch/mysql2.rb
CHANGED
@@ -6,7 +6,8 @@ require 'mysql2'
|
|
6
6
|
class Cutoff
|
7
7
|
module Patch
|
8
8
|
# Sets the max execution time for SELECT queries if there is an active
|
9
|
-
# cutoff and it has time remaining
|
9
|
+
# cutoff and it has time remaining. You can select this patch with
|
10
|
+
# `exclude` or `only` using the checkpoint name `:mysql2`.
|
10
11
|
module Mysql2
|
11
12
|
# Overrides `Mysql2::Client#query` to insert a MAX_EXECUTION_TIME query
|
12
13
|
# hint with the remaining cutoff time
|
@@ -19,9 +20,9 @@ class Cutoff
|
|
19
20
|
# be executed in this case.
|
20
21
|
def query(sql, options = {})
|
21
22
|
cutoff = Cutoff.current
|
22
|
-
return super unless cutoff
|
23
|
+
return super unless cutoff&.selected?(:mysql2)
|
23
24
|
|
24
|
-
cutoff.checkpoint!
|
25
|
+
cutoff.checkpoint!(:mysql2)
|
25
26
|
sql = QueryWithMaxTime.new(sql, cutoff.ms_remaining.ceil).to_s
|
26
27
|
super
|
27
28
|
end
|
@@ -45,8 +46,8 @@ class Cutoff
|
|
45
46
|
|
46
47
|
# Loop through tokens like "WORD " or "/* "
|
47
48
|
while @scanner.scan(/(\S+)\s+/)
|
48
|
-
# Get the word part
|
49
|
-
handle_token(@scanner[1]
|
49
|
+
# Get the word part
|
50
|
+
handle_token(@scanner[1])
|
50
51
|
end
|
51
52
|
|
52
53
|
return @scanner.string unless @found_select
|
@@ -68,7 +69,7 @@ class Cutoff
|
|
68
69
|
hint_comment
|
69
70
|
elsif token.start_with?('/*')
|
70
71
|
block_comment
|
71
|
-
elsif token.
|
72
|
+
elsif token.match?(/^select/i)
|
72
73
|
select
|
73
74
|
else
|
74
75
|
other
|
@@ -4,27 +4,39 @@ require 'net/http'
|
|
4
4
|
|
5
5
|
class Cutoff
|
6
6
|
module Patch
|
7
|
-
#
|
8
|
-
# to the remaining time
|
7
|
+
# Set checkpoints for Ruby HTTP requests. Also sets the Net::HTTP timeouts
|
8
|
+
# to the remaining cutoff time. You can select this patch with
|
9
|
+
# `exclude` or `only` using the checkpoint name `:net_http`.
|
9
10
|
module NetHttp
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def self.gen_timeout_method(name)
|
12
|
+
<<~RUBY
|
13
|
+
if #{name}.nil? || #{name} > remaining
|
14
|
+
self.#{name} = cutoff.seconds_remaining
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
@write_timeout = cutoff.seconds_remaining
|
19
|
+
def self.use_write_timeout?
|
20
|
+
Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.6')
|
19
21
|
end
|
20
22
|
|
21
|
-
# Same as the original start, but
|
23
|
+
# Same as the original start, but adds a checkpoint for starting HTTP
|
24
|
+
# requests and sets network timeouts to the remaining time
|
22
25
|
#
|
23
|
-
# @see
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
# @see Net::HTTP#start
|
27
|
+
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
28
|
+
def start
|
29
|
+
if (cutoff = Cutoff.current) && cutoff.selected?(:net_http)
|
30
|
+
remaining = cutoff.seconds_remaining
|
31
|
+
#{gen_timeout_method('open_timeout')}
|
32
|
+
#{gen_timeout_method('read_timeout')}
|
33
|
+
#{gen_timeout_method('write_timeout') if use_write_timeout?}
|
34
|
+
#{gen_timeout_method('continue_timeout')}
|
35
|
+
Cutoff.checkpoint!(:net_http)
|
36
|
+
end
|
37
|
+
super
|
38
|
+
end
|
39
|
+
RUBY
|
28
40
|
end
|
29
41
|
end
|
30
42
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_controller'
|
4
|
+
|
5
|
+
class Cutoff
|
6
|
+
module Rails
|
7
|
+
# Rails controller integration
|
8
|
+
module Controller
|
9
|
+
# Set a cutoff for the controller
|
10
|
+
#
|
11
|
+
# Can be called multiple times with different options to configure
|
12
|
+
# cutoffs for various conditions. If multiple conditions match a given
|
13
|
+
# controller, the last applied cutoff "wins".
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# class ApplicationController
|
17
|
+
# # Apply a global maximum
|
18
|
+
# cutoff 30
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class UsersController < ApplicationController
|
22
|
+
# # Override the base time limit
|
23
|
+
# cutoff 5.0
|
24
|
+
# cutoff 3.0, only: :show
|
25
|
+
# cutoff 7, if: :signed_in
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @param seconds [Float, Integer] The allowed seconds for a controller
|
29
|
+
# action
|
30
|
+
# @param options [Hash] Options to pass to `around_action`. For example,
|
31
|
+
# pass `:only`, `:except`, `:if`, to limit the scope of the cutoff.
|
32
|
+
def cutoff(seconds, options = {})
|
33
|
+
prepend_around_action(options) do |_controller, action|
|
34
|
+
next action.call if @cutoff_wrapped
|
35
|
+
|
36
|
+
begin
|
37
|
+
@cutoff_wrapped = true
|
38
|
+
Cutoff.wrap(seconds, &action)
|
39
|
+
ensure
|
40
|
+
@cutoff_wrapped = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
ActionController::Base.extend(Cutoff::Rails::Controller)
|
data/lib/cutoff/rails.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sidekiq'
|
4
|
+
|
5
|
+
class Cutoff
|
6
|
+
module Sidekiq
|
7
|
+
# Add an option `cutoff` for sidekiq workers
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class MyWorker
|
11
|
+
# include Sidekiq::Worker
|
12
|
+
#
|
13
|
+
# sidekiq_options cutoff: 6.0
|
14
|
+
#
|
15
|
+
# def perform
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
class ServerMiddleware
|
20
|
+
# @param worker [Object] the worker instance
|
21
|
+
# @param _job [Hash] the full job payload
|
22
|
+
# @param _queue [String] queue the name of the queue the job was pulled
|
23
|
+
# from
|
24
|
+
# @yield the next middleware in the chain or worker `perform` method
|
25
|
+
# @return [void]
|
26
|
+
def call(worker, _job, _queue)
|
27
|
+
allowed_seconds = worker.class.sidekiq_options['cutoff']
|
28
|
+
return yield if allowed_seconds.nil?
|
29
|
+
|
30
|
+
Cutoff.wrap(allowed_seconds) { yield }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
::Sidekiq.configure_server do |config|
|
37
|
+
config.server_middleware do |chain|
|
38
|
+
chain.add(Cutoff::Sidekiq::ServerMiddleware)
|
39
|
+
end
|
40
|
+
end
|
data/lib/cutoff/timer.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal:true
|
2
|
+
|
3
|
+
class Cutoff
|
4
|
+
module Timer
|
5
|
+
if defined?(Process::CLOCK_MONOTONIC_RAW)
|
6
|
+
# The current time
|
7
|
+
#
|
8
|
+
# If it is available, this will use a monotonic clock. This is a clock
|
9
|
+
# that always moves forward in time. If that is not available on this
|
10
|
+
# system, `Time.now` will be used
|
11
|
+
#
|
12
|
+
# @return [Float] The current time as a float
|
13
|
+
def now
|
14
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
|
15
|
+
end
|
16
|
+
elsif defined?(Process::CLOCK_MONOTONIC)
|
17
|
+
def now
|
18
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
19
|
+
end
|
20
|
+
elsif Gem.loaded_specs['concurrent-ruby']
|
21
|
+
require 'concurrent-ruby'
|
22
|
+
|
23
|
+
def now
|
24
|
+
Concurrent.monotonic_time
|
25
|
+
end
|
26
|
+
else
|
27
|
+
def now
|
28
|
+
Time.now.to_f
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/cutoff/version.rb
CHANGED
data/lib/cutoff.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
# frozen_string_literal:true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
require 'cutoff/version'
|
6
|
+
require 'cutoff/error'
|
7
|
+
require 'cutoff/patch'
|
8
|
+
require 'cutoff/timer'
|
9
|
+
|
10
|
+
require 'cutoff/rails'
|
11
|
+
require 'cutoff/sidekiq' if Gem.loaded_specs['sidekiq']
|
6
12
|
|
7
13
|
class Cutoff
|
8
14
|
CURRENT_STACK_KEY = 'cutoff_deadline_stack'
|
9
15
|
private_constant :CURRENT_STACK_KEY
|
10
16
|
|
17
|
+
extend Timer
|
18
|
+
|
11
19
|
class << self
|
12
20
|
# Get the current {Cutoff} if one is set
|
13
21
|
def current
|
@@ -21,13 +29,14 @@ class Cutoff
|
|
21
29
|
# If a cutoff is already started for this thread, then `start` uses the
|
22
30
|
# minimum of the current remaining time and the given time
|
23
31
|
#
|
24
|
-
# @param
|
25
|
-
# be overridden if there is an active cutoff and it has less remaining
|
26
|
-
# time.
|
32
|
+
# @param (see #initialize)
|
27
33
|
# @return [Cutoff] The {Cutoff} instance
|
28
|
-
def start(
|
29
|
-
|
30
|
-
|
34
|
+
def start(allowed_seconds, **options)
|
35
|
+
if current
|
36
|
+
allowed_seconds = [allowed_seconds, current.seconds_remaining].min
|
37
|
+
end
|
38
|
+
|
39
|
+
cutoff = new(allowed_seconds, **options)
|
31
40
|
Thread.current[CURRENT_STACK_KEY] ||= []
|
32
41
|
Thread.current[CURRENT_STACK_KEY] << cutoff
|
33
42
|
cutoff
|
@@ -64,9 +73,10 @@ class Cutoff
|
|
64
73
|
#
|
65
74
|
# @see .start
|
66
75
|
# @see .stop
|
76
|
+
# @param (see #initialize)
|
67
77
|
# @return The value that returned from the block
|
68
|
-
def wrap(
|
69
|
-
cutoff = start(
|
78
|
+
def wrap(allowed_seconds, **options)
|
79
|
+
cutoff = start(allowed_seconds, **options)
|
70
80
|
yield cutoff
|
71
81
|
ensure
|
72
82
|
stop(cutoff)
|
@@ -78,38 +88,36 @@ class Cutoff
|
|
78
88
|
#
|
79
89
|
# @raise CutoffExceededError If there is an active expired cutoff
|
80
90
|
# @return [void]
|
81
|
-
def checkpoint!
|
91
|
+
def checkpoint!(name = nil)
|
82
92
|
cutoff = current
|
83
93
|
return unless cutoff
|
84
94
|
|
85
|
-
cutoff.checkpoint!
|
95
|
+
cutoff.checkpoint!(name)
|
86
96
|
end
|
87
97
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def now
|
97
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
|
98
|
-
end
|
99
|
-
elsif defined?(Process::CLOCK_MONOTONIC)
|
100
|
-
def now
|
101
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
102
|
-
end
|
103
|
-
elsif Gem.loaded_specs['concurrent-ruby']
|
104
|
-
require 'concurrent-ruby'
|
98
|
+
# Disable Cutoff globally. Useful for testing and debugging
|
99
|
+
#
|
100
|
+
# Should not be used in production
|
101
|
+
#
|
102
|
+
# @return [void]
|
103
|
+
def disable!
|
104
|
+
@disabled = true
|
105
|
+
end
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
# Enable Cutoff globally if it has been disabled
|
108
|
+
#
|
109
|
+
# Should not be used in production
|
110
|
+
#
|
111
|
+
# @return [void]
|
112
|
+
def enable!
|
113
|
+
@disabled = false
|
114
|
+
end
|
115
|
+
|
116
|
+
# True if cutoff was disabled with {.disable!}
|
117
|
+
#
|
118
|
+
# @return [Boolean] True if disabled
|
119
|
+
def disabled?
|
120
|
+
@disabled == true
|
113
121
|
end
|
114
122
|
end
|
115
123
|
|
@@ -120,10 +128,22 @@ class Cutoff
|
|
120
128
|
#
|
121
129
|
# The timer starts immediately upon creation
|
122
130
|
#
|
123
|
-
# @param allowed_seconds [
|
124
|
-
|
131
|
+
# @param allowed_seconds [Float, Integer] The total number of seconds to allow
|
132
|
+
# @param exclude [Enumberable<Symbol>, Symbol, nil] If given a name or
|
133
|
+
# list of checkpoint names to skip
|
134
|
+
# @param only [Enumberable<Symbol>, Symbol, nil] If given a name or
|
135
|
+
# list of checkpoint names to allow
|
136
|
+
def initialize(allowed_seconds, exclude: nil, only: nil)
|
125
137
|
@allowed_seconds = allowed_seconds.to_f
|
126
138
|
@start_time = Cutoff.now
|
139
|
+
|
140
|
+
if exclude
|
141
|
+
@exclude = Set.new(exclude.is_a?(Enumerable) ? exclude : [exclude])
|
142
|
+
end
|
143
|
+
|
144
|
+
if only
|
145
|
+
@only = Set.new(only.is_a?(Enumerable) ? only : [only])
|
146
|
+
end
|
127
147
|
end
|
128
148
|
|
129
149
|
# The number of seconds left on the clock
|
@@ -144,6 +164,8 @@ class Cutoff
|
|
144
164
|
#
|
145
165
|
# @return [Float] The number of seconds
|
146
166
|
def elapsed_seconds
|
167
|
+
return 0 if Cutoff.disabled?
|
168
|
+
|
147
169
|
Cutoff.now - @start_time
|
148
170
|
end
|
149
171
|
|
@@ -158,9 +180,29 @@ class Cutoff
|
|
158
180
|
#
|
159
181
|
# @raise CutoffExceededError If there is an active expired cutoff
|
160
182
|
# @return [void]
|
161
|
-
def checkpoint!
|
183
|
+
def checkpoint!(name = nil)
|
184
|
+
unless name.nil? || name.is_a?(Symbol)
|
185
|
+
raise ArgumentError, 'name must be a symbol'
|
186
|
+
end
|
187
|
+
|
188
|
+
return unless selected?(name)
|
162
189
|
raise CutoffExceededError, self if exceeded?
|
163
190
|
|
164
191
|
nil
|
165
192
|
end
|
193
|
+
|
194
|
+
# True if the named checkpoint is selected by the `exclude` and `only`
|
195
|
+
# options. If these options are not given, a checkpoint is considered to be
|
196
|
+
# selected. If the checkpoint is not named, it is also considered to be
|
197
|
+
# selected.
|
198
|
+
#
|
199
|
+
# @param name [Symbol, nil] The name of the checkpoint
|
200
|
+
# @return [Boolean] True if the checkpoint is selected
|
201
|
+
def selected?(name)
|
202
|
+
return true if name.nil? && @exclude
|
203
|
+
return false if @exclude&.include?(name)
|
204
|
+
return false if @only && !@only.include?(name)
|
205
|
+
|
206
|
+
true
|
207
|
+
end
|
166
208
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cutoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Howard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rubocop
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,13 +102,17 @@ files:
|
|
88
102
|
- lib/cutoff/patch.rb
|
89
103
|
- lib/cutoff/patch/mysql2.rb
|
90
104
|
- lib/cutoff/patch/net_http.rb
|
105
|
+
- lib/cutoff/rails.rb
|
106
|
+
- lib/cutoff/rails/controller.rb
|
107
|
+
- lib/cutoff/sidekiq.rb
|
108
|
+
- lib/cutoff/timer.rb
|
91
109
|
- lib/cutoff/version.rb
|
92
110
|
homepage: https://github.com/justinhoward/cutoff
|
93
111
|
licenses:
|
94
112
|
- MIT
|
95
113
|
metadata:
|
96
114
|
changelog_uri: https://github.com/justinhoward/cutoff/blob/master/CHANGELOG.md
|
97
|
-
documentation_uri: https://www.rubydoc.info/gems/cutoff/0.2
|
115
|
+
documentation_uri: https://www.rubydoc.info/gems/cutoff/0.4.2
|
98
116
|
post_install_message:
|
99
117
|
rdoc_options: []
|
100
118
|
require_paths:
|