cutoff 0.2.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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: