sucker_punch 2.0.3 → 3.0.0

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
- 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.