sucker_punch 2.0.3 → 3.0.0

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
- SHA1:
3
- metadata.gz: f9888e735e4566c4c0d789d328c78553caa09e35
4
- data.tar.gz: 5e70f216542d0bc65fb5fbb1757dfce2ce529100
2
+ SHA256:
3
+ metadata.gz: e1edad787473a077f23f2fae95150cdb92e3834a12150fbd4d910fade5692516
4
+ data.tar.gz: 4ba7b7fa6cf3a1c99706a3bda43b0fd2fc291ae1825a23466b00a5b7a3354003
5
5
  SHA512:
6
- metadata.gz: 295aceab156ca6df316d095be72b2052591a0a4895893ce75e4751db160b8eb675dac3385757c94f0776b90587c292bcf4a1b666df6a3c387d471a4569e1c520
7
- data.tar.gz: 49db7fbff4f68c9015566162ba26794365d535a5dfc0a9957dc107e4006ec7baae125ad52c1b0ff29c813f080aaf1919c0b2b0bec4e8ac6498adda43fa254d18
6
+ metadata.gz: a8184cafe29937ad818920951c3c760e8435b4e35a348e27629053bae1b56e2f44ab412154fb3769e6878034a9351ad5e60fb44d9385bf5bb4f2bf58cc91c71c
7
+ data.tar.gz: fde16057612312085f59a955e783007b6a0cf0b4ff7d08312963bc0c76b3be2e5d254e5eb1df1625db3c554403485e5df8a1c995ab50b71dda4e1358b00e8d23
data/CHANGES.md CHANGED
@@ -1,3 +1,38 @@
1
+ 3.0.0
2
+ -------
3
+ - Add support for keyword arguments in ruby `>= 3.0`. More details in [release notes](https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/).
4
+
5
+ 2.1.1
6
+ -------
7
+ - Relax versioning constraint of `concurrent-ruby` to make way for 1.1 release
8
+
9
+ 2.1.0
10
+ -------
11
+ - Add `max_jobs` configuration option to set the maximum number of tasks that
12
+ may be waiting in the work queue
13
+
14
+ ```ruby
15
+ class JobWithLimit
16
+ include SuckerPunch::Job
17
+ max_jobs 2
18
+
19
+ def perform(data)
20
+ # work...
21
+ end
22
+ end
23
+
24
+ 10.times do
25
+ begin
26
+ JobWithLimit.perform_async('work') }
27
+ rescue Concurrent::RejectedExecutionError => e
28
+ # Queue maxed out, this job didn't get queued
29
+ end
30
+ end
31
+
32
+ 2.0.4
33
+ -------
34
+ - Better initialization of variables and names to remove warnings
35
+
1
36
  2.0.3
2
37
  -------
3
38
  - Rewrite shutdown strategy to fix bug related to premature shutdown when only
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Sucker Punch
2
2
 
3
- [![Build Status](https://travis-ci.org/brandonhilkert/sucker_punch.png?branch=master)](https://travis-ci.org/brandonhilkert/sucker_punch)
4
- [![Code Climate](https://codeclimate.com/github/brandonhilkert/sucker_punch.png)](https://codeclimate.com/github/brandonhilkert/sucker_punch)
3
+ [![Build Status](https://travis-ci.org/brandonhilkert/sucker_punch.svg?branch=master)](https://travis-ci.org/brandonhilkert/sucker_punch)
4
+ [![Code Climate](https://codeclimate.com/github/brandonhilkert/sucker_punch.svg)](https://codeclimate.com/github/brandonhilkert/sucker_punch)
5
5
 
6
6
  Sucker Punch is a single-process Ruby asynchronous processing library.
7
7
  This reduces costs
@@ -114,6 +114,22 @@ class LogJob
114
114
  end
115
115
  ```
116
116
 
117
+ #### Configure the Queue Size
118
+
119
+ The default number of jobs that can be queued is unlimited. If you wish to restrict this you can set
120
+ max\_jobs as follows:
121
+
122
+ ```Ruby
123
+ class LogJob
124
+ include SuckerPunch::Job
125
+ max_jobs 10
126
+
127
+ def perform(event)
128
+ Log.new(event).track
129
+ end
130
+ end
131
+ ```
132
+
117
133
  #### Executing Jobs in the Future
118
134
 
119
135
  Many background processing libraries have methods to perform operations after a
@@ -131,7 +147,7 @@ class DataJob
131
147
  end
132
148
 
133
149
  DataJob.perform_async("asdf") # immediately perform asynchronously
134
- DataJob.perform_in(60, "asdf") # `perform` will be excuted 60 sec. later
150
+ DataJob.perform_in(60, "asdf") # `perform` will be executed 60 sec. later
135
151
  ```
136
152
 
137
153
  #### `ActiveRecord` Connection Pool Connections
@@ -254,6 +270,19 @@ $ rails g sucker_punch:job logger
254
270
  would create the file `app/jobs/logger_job.rb` with a unimplemented `#perform`
255
271
  method.
256
272
 
273
+ ## Sinatra
274
+
275
+ If you're using Sucker Punch with Sinatra, you must require Sucker Punch before Sinatra:
276
+
277
+ ```ruby
278
+ # app.rb
279
+
280
+ require 'sucker_punch'
281
+ require 'sinatra'
282
+ ```
283
+
284
+ This will ensure Sucker Punch's `at_exit()` handler to clean up and shutdown queues does not happen **before** Sinatra *starts up* via its own `at_exit()` handler.
285
+
257
286
  ## Active Job
258
287
 
259
288
  Sucker Punch has been added as an Active Job adapter in Rails 4.2.
data/lib/sucker_punch.rb CHANGED
@@ -10,12 +10,12 @@ module SuckerPunch
10
10
  RUNNING = Concurrent::AtomicBoolean.new(true)
11
11
 
12
12
  class << self
13
- def exception_handler=(handler)
14
- @exception_handler = handler
13
+ def exception_handler
14
+ @exception_handler ||= method(:default_exception_handler)
15
15
  end
16
16
 
17
- def exception_handler
18
- @exception_handler || method(:default_exception_handler)
17
+ def exception_handler=(handler)
18
+ @exception_handler = handler
19
19
  end
20
20
 
21
21
  def default_exception_handler(ex, klass, args)
@@ -26,11 +26,7 @@ module SuckerPunch
26
26
  end
27
27
 
28
28
  def logger
29
- @logger || default_logger
30
- end
31
-
32
- def logger=(log)
33
- @logger = (log ? log : Logger.new('/dev/null'))
29
+ @logger ||= default_logger
34
30
  end
35
31
 
36
32
  def default_logger
@@ -39,9 +35,13 @@ module SuckerPunch
39
35
  l
40
36
  end
41
37
 
38
+ def logger=(log)
39
+ @logger = (log ? log : Logger.new('/dev/null'))
40
+ end
41
+
42
42
  def shutdown_timeout
43
43
  # 10 seconds on heroku, minus a grace period
44
- @shutdown_timeout || 8
44
+ @shutdown_timeout ||= 8
45
45
  end
46
46
 
47
47
  def shutdown_timeout=(timeout)
@@ -10,9 +10,8 @@ module SuckerPunch
10
10
  @job = job
11
11
  end
12
12
 
13
- def perform(*args)
14
- @job.class.perform_async(*args)
13
+ def perform(*args, **kwargs)
14
+ @job.class.perform_async(*args, **kwargs)
15
15
  end
16
16
  end
17
17
  end
18
-
@@ -14,15 +14,17 @@ module SuckerPunch
14
14
  # To trigger asynchronous job:
15
15
  #
16
16
  # LogJob.perform_async(1, 2, 3)
17
- # LogJob.perform_in(60, 1, 2, 3) # `perform` will be excuted 60 sec. later
17
+ # LogJob.perform_in(60, 1, 2, 3) # `perform` will be executed 60 sec. later
18
18
  #
19
19
  # Note that perform_async is a class method, perform is an instance method.
20
20
  module Job
21
21
  def self.included(base)
22
22
  base.extend(ClassMethods)
23
23
  base.class_attribute :num_workers
24
+ base.class_attribute :num_jobs_max
24
25
 
25
26
  base.num_workers = 2
27
+ base.num_jobs_max = nil
26
28
  end
27
29
 
28
30
  def logger
@@ -30,17 +32,17 @@ module SuckerPunch
30
32
  end
31
33
 
32
34
  module ClassMethods
33
- def perform_async(*args)
35
+ def perform_async(*args, **kwargs)
34
36
  return unless SuckerPunch::RUNNING.true?
35
- queue = SuckerPunch::Queue.find_or_create(self.to_s, num_workers)
36
- queue.post(args) { |args| __run_perform(*args) }
37
+ queue = SuckerPunch::Queue.find_or_create(self.to_s, num_workers, num_jobs_max)
38
+ queue.post { __run_perform(*args, **kwargs) }
37
39
  end
38
40
 
39
- def perform_in(interval, *args)
41
+ def perform_in(interval, *args, **kwargs)
40
42
  return unless SuckerPunch::RUNNING.true?
41
- queue = SuckerPunch::Queue.find_or_create(self.to_s, num_workers)
43
+ queue = SuckerPunch::Queue.find_or_create(self.to_s, num_workers, num_jobs_max)
42
44
  job = Concurrent::ScheduledTask.execute(interval.to_f, args: args, executor: queue) do
43
- __run_perform(*args)
45
+ __run_perform(*args, **kwargs)
44
46
  end
45
47
  job.pending?
46
48
  end
@@ -49,14 +51,24 @@ module SuckerPunch
49
51
  self.num_workers = num
50
52
  end
51
53
 
52
- def __run_perform(*args)
54
+ def max_jobs(num)
55
+ self.num_jobs_max = num
56
+ end
57
+
58
+ def __run_perform(*args, **kwargs)
53
59
  SuckerPunch::Counter::Busy.new(self.to_s).increment
54
- result = self.new.perform(*args)
60
+
61
+ result = if RUBY_VERSION >= '2.5'
62
+ self.new.perform(*args, **kwargs)
63
+ else
64
+ self.new.perform(*args, *(kwargs.any? ? [kwargs] : nil))
65
+ end
66
+
55
67
  SuckerPunch::Counter::Processed.new(self.to_s).increment
56
68
  result
57
69
  rescue => ex
58
70
  SuckerPunch::Counter::Failed.new(self.to_s).increment
59
- SuckerPunch.exception_handler.call(ex, self, args)
71
+ SuckerPunch.exception_handler.call(ex, self, [*args, **kwargs])
60
72
  ensure
61
73
  SuckerPunch::Counter::Busy.new(self.to_s).decrement
62
74
  end
@@ -5,23 +5,26 @@ module SuckerPunch
5
5
  extend Forwardable
6
6
  include Concurrent::ExecutorService
7
7
 
8
+ DEFAULT_MAX_QUEUE_SIZE = 0 # Unlimited
9
+
8
10
  DEFAULT_EXECUTOR_OPTIONS = {
9
11
  min_threads: 2,
10
12
  max_threads: 2,
11
13
  idletime: 60, # 1 minute
12
- max_queue: 0, # unlimited
13
14
  auto_terminate: false # Let shutdown modes handle thread termination
14
15
  }.freeze
15
16
 
16
17
  QUEUES = Concurrent::Map.new
17
18
 
18
- def self.find_or_create(name, num_workers = 2)
19
+ def self.find_or_create(name, num_workers = 2, num_jobs_max = nil)
19
20
  pool = QUEUES.fetch_or_store(name) do
20
- options = DEFAULT_EXECUTOR_OPTIONS.merge({
21
- min_threads: num_workers,
22
- max_threads: num_workers
23
- })
24
- Concurrent::ThreadPoolExecutor.new(options)
21
+ options = DEFAULT_EXECUTOR_OPTIONS
22
+ .merge(
23
+ min_threads: num_workers,
24
+ max_threads: num_workers,
25
+ max_queue: num_jobs_max || DEFAULT_MAX_QUEUE_SIZE
26
+ )
27
+ Concurrent::ThreadPoolExecutor.new(**options)
25
28
  end
26
29
 
27
30
  new(name, pool)
@@ -111,6 +114,7 @@ module SuckerPunch
111
114
  def_delegators :@pool,
112
115
  :max_length,
113
116
  :min_length,
117
+ :max_queue,
114
118
  :length,
115
119
  :queue_length,
116
120
  :wait_for_termination#,
@@ -161,10 +165,10 @@ module SuckerPunch
161
165
  SuckerPunch::Counter::Failed.new(name).value
162
166
  end
163
167
 
164
- def post(*args, &block)
168
+ def post(*args, **kwargs, &block)
165
169
  synchronize do
166
170
  if @running
167
- @pool.post(*args, &block)
171
+ @pool.post(*args, **kwargs, &block)
168
172
  else
169
173
  false
170
174
  end
@@ -187,4 +191,3 @@ module SuckerPunch
187
191
  end
188
192
  end
189
193
  end
190
-
@@ -0,0 +1,44 @@
1
+ require 'sucker_punch'
2
+
3
+ # Include this in your tests to simulate
4
+ # a fake job queue. Jobs won't be executed
5
+ # as they normal would be the thread pool.
6
+ # They'll instead be pushed to a fake queue
7
+ # to be checked in a test environment.
8
+ #
9
+ # Include in your test_helper.rb:
10
+ #
11
+ # require 'sucker_punch/testing'
12
+ #
13
+ # In your application code:
14
+ #
15
+ # LogJob.perform_async(1, 2, 3)
16
+ #
17
+ # In your tests:
18
+ #
19
+ # LogJob.jobs => [{ "args" => [1, 2, 3]]
20
+
21
+ module SuckerPunch
22
+ module Job
23
+ def self.jobs
24
+ SuckerPunch::Queue.find_or_create(self.to_s)
25
+ end
26
+
27
+ def self.clear_all
28
+
29
+ end
30
+ end
31
+
32
+ class Queue
33
+ def self.find_or_create(name, _number_workers = 2, _num_jobs_max = nil)
34
+ QUEUES.fetch_or_store(name) do
35
+ []
36
+ end
37
+ end
38
+
39
+
40
+ def kill
41
+ true
42
+ end
43
+ end
44
+ end
@@ -17,7 +17,7 @@ require 'sucker_punch'
17
17
  #
18
18
  # Include inline testing lib:
19
19
  #
20
- # require 'sucker_punch/testing/inline"
20
+ # require 'sucker_punch/testing/inline'
21
21
  #
22
22
  # LogJob.perform_async(1, 2, 3) is now synchronous
23
23
  # LogJob.perform_in(1, 2, 3) is now synchronous
@@ -25,12 +25,12 @@ require 'sucker_punch'
25
25
  module SuckerPunch
26
26
  module Job
27
27
  module ClassMethods
28
- def perform_async(*args)
29
- self.new.perform(*args)
28
+ def perform_async(*args, **kwargs)
29
+ self.new.perform(*args, **kwargs)
30
30
  end
31
31
 
32
- def perform_in(_, *args)
33
- self.new.perform(*args)
32
+ def perform_in(_, *args, **kwargs)
33
+ self.new.perform(*args, **kwargs)
34
34
  end
35
35
  end
36
36
  end
@@ -1,3 +1,3 @@
1
1
  module SuckerPunch
2
- VERSION = "2.0.3"
2
+ VERSION = "3.0.0"
3
3
  end
data/sucker_punch.gemspec CHANGED
@@ -24,5 +24,11 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency "minitest"
25
25
  gem.add_development_dependency "pry"
26
26
 
27
- gem.add_dependency "concurrent-ruby", "~> 1.0.0"
27
+ gem.add_dependency "concurrent-ruby", "~> 1.0"
28
+
29
+ if gem.respond_to?(:metadata)
30
+ gem.metadata['changelog_uri'] = 'https://github.com/brandonhilkert/sucker_punch/blob/master/CHANGES.md'
31
+ gem.metadata['source_code_uri'] = 'https://github.com/brandonhilkert/sucker_punch'
32
+ gem.metadata['bug_tracker_uri'] = 'https://github.com/brandonhilkert/sucker_punch/issues'
33
+ end
28
34
  end
@@ -55,6 +55,11 @@ module SuckerPunch
55
55
  FakeLogJob.workers(2)
56
56
  end
57
57
 
58
+ def test_can_set_max_jobs
59
+ FakeLogJob.max_jobs(10)
60
+ assert_equal 10, FakeLogJob.num_jobs_max
61
+ end
62
+
58
63
  def test_logger_is_accessible_from_instance
59
64
  SuckerPunch.logger = SuckerPunch.default_logger
60
65
  assert_equal SuckerPunch.logger, FakeLogJob.new.logger
@@ -177,4 +182,3 @@ module SuckerPunch
177
182
  end
178
183
  end
179
184
  end
180
-
@@ -29,6 +29,11 @@ module SuckerPunch
29
29
  assert_equal 4, queue.min_length
30
30
  end
31
31
 
32
+ def test_queue_max_queue_can_be_set
33
+ queue = SuckerPunch::Queue.find_or_create(@queue, 4, 10)
34
+ assert_equal(10, queue.max_queue)
35
+ end
36
+
32
37
  def test_same_queue_is_returned_on_subsequent_queries
33
38
  queue = SuckerPunch::Queue.find_or_create(@queue)
34
39
  assert_equal queue, SuckerPunch::Queue.find_or_create(@queue)
@@ -131,9 +136,9 @@ module SuckerPunch
131
136
  @signals = []
132
137
  end
133
138
 
134
- def post(*args, &block)
139
+ def post(*args, **kwargs, &block)
135
140
  if block_given?
136
- block.call(args)
141
+ block.call(args, **kwargs)
137
142
  end
138
143
  end
139
144
 
@@ -41,7 +41,7 @@ class SuckerPunchTest < Minitest::Test
41
41
  end
42
42
 
43
43
  def test_exception_handler_can_be_set
44
- SuckerPunch.exception_handler = -> (ex, _, _) { raise "bad stuff" }
44
+ SuckerPunch.exception_handler = -> (_ex, _, _) { raise "bad stuff" }
45
45
  assert_raises(::RuntimeError) { SuckerPunch.exception_handler.call(StandardError.new("bad"), nil, nil) }
46
46
  end
47
47
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sucker_punch
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Hilkert
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-23 00:00:00.000000000 Z
11
+ date: 2021-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.0
61
+ version: '1.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.0
68
+ version: '1.0'
69
69
  description: Asynchronous processing library for Ruby
70
70
  email:
71
71
  - brandonhilkert@gmail.com
@@ -89,6 +89,7 @@ files:
89
89
  - lib/sucker_punch/job.rb
90
90
  - lib/sucker_punch/queue.rb
91
91
  - lib/sucker_punch/railtie.rb
92
+ - lib/sucker_punch/testing.rb
92
93
  - lib/sucker_punch/testing/inline.rb
93
94
  - lib/sucker_punch/version.rb
94
95
  - sucker_punch.gemspec
@@ -101,7 +102,10 @@ files:
101
102
  homepage: https://github.com/brandonhilkert/sucker_punch
102
103
  licenses:
103
104
  - MIT
104
- metadata: {}
105
+ metadata:
106
+ changelog_uri: https://github.com/brandonhilkert/sucker_punch/blob/master/CHANGES.md
107
+ source_code_uri: https://github.com/brandonhilkert/sucker_punch
108
+ bug_tracker_uri: https://github.com/brandonhilkert/sucker_punch/issues
105
109
  post_install_message: |-
106
110
  Sucker Punch v2.0 introduces backwards-incompatible changes.
107
111
  Please see https://github.com/brandonhilkert/sucker_punch/blob/master/CHANGES.md#200 for details.
@@ -119,9 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
123
  - !ruby/object:Gem::Version
120
124
  version: '0'
121
125
  requirements: []
122
- rubyforge_project:
123
- rubygems_version: 2.6.11
124
- signing_key:
126
+ rubygems_version: 3.1.2
127
+ signing_key:
125
128
  specification_version: 4
126
129
  summary: Sucker Punch is a Ruby asynchronous processing using concurrent-ruby, heavily
127
130
  influenced by Sidekiq and girl_friday.