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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64171df8b5550b99b603ba4bae4874174cc5e4bcce2632318be777ecc932dce3
4
- data.tar.gz: 39ee62f1f743fdce6d9dd3e873bd3acf4d7efd09907d0f9e265e98c786cb7dc9
3
+ metadata.gz: d00ff1f831beec4fed582b5faa9ed42c350c13b32217bdcab0b850445ba9c358
4
+ data.tar.gz: 77d466db54a822240d2d27cfdb120f98aa7f7435ec62ae15191292702580de79
5
5
  SHA512:
6
- metadata.gz: bb41605b534178706b45f3e6921aad80ff0ff50606038d95d9d50267910a590df59969cb77b1c7879d0b2cd429c60205a418e1ff590b1dd7b4e7f4394b0f4f3c
7
- data.tar.gz: 713d75df722a2f82414373d482d4a981e9a579069de55262afe12004287abdd5b6f2e7e09052130a3b0ce875658e8177ce519bdadb549dbb92dc8970f9cd0813
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.2.0...HEAD
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
- around_action { |_controller, action| Cutoff.wrap(2.5) { action.call } }
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
- Now in your action, you can call `checkpoint!`, or if you're using the Mysql2
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
- def index
234
- # Do thing one
235
- Cutoff.checkpoint!
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
- # Do something else
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
 
@@ -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. None of our tokens care about case
49
- handle_token(@scanner[1].downcase)
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.start_with?('select')
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
- # Adds a checkpoint for starting HTTP requests and sets network timeouts
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
- # Construct a {Net::HTTP}, but with the timeouts set to the remaining
11
- # cutoff time if one is active
12
- def initialize(address, port = nil)
13
- super
14
- return unless (cutoff = Cutoff.current)
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
- @open_timeout = cutoff.seconds_remaining
17
- @read_timeout = cutoff.seconds_remaining
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 with a cutoff checkpoint
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 {Net::HTTP#start}
24
- def start
25
- Cutoff.checkpoint!
26
- super
27
- end
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)
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cutoff/rails/controller' if Gem.loaded_specs['actionpack']
@@ -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
@@ -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
@@ -3,6 +3,6 @@
3
3
  class Cutoff
4
4
  # @return [Gem::Version] The current version of the cutoff gem
5
5
  def self.version
6
- Gem::Version.new('0.2.0')
6
+ Gem::Version.new('0.4.2')
7
7
  end
8
8
  end
data/lib/cutoff.rb CHANGED
@@ -1,13 +1,21 @@
1
1
  # frozen_string_literal:true
2
2
 
3
- require_relative 'cutoff/version'
4
- require_relative 'cutoff/error'
5
- require_relative 'cutoff/patch'
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 seconds [Float, Integer] The number of seconds for the cutoff. May
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(seconds)
29
- seconds = [seconds, current.seconds_remaining].min if current
30
- cutoff = Cutoff.new(seconds)
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(seconds)
69
- cutoff = start(seconds)
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
- if defined?(Process::CLOCK_MONOTONIC_RAW)
89
- # The current time
90
- #
91
- # If it is available, this will use a monotonic clock. This is a clock
92
- # that always moves forward in time. If that is not available on this
93
- # system, `Time.now` will be used
94
- #
95
- # @return [Float] The current time as a float
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
- def now
107
- Concurrent.monotonic_time
108
- end
109
- else
110
- def now
111
- Time.now.to_f
112
- end
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 [Integer, Float] The total number of seconds to allow
124
- def initialize(allowed_seconds)
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.0
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-07-23 00:00:00.000000000 Z
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.0
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: