sidekiq 7.3.8 → 7.3.10
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/Changes.md +15 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +0 -13
- data/lib/sidekiq/fetch.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +1 -1
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/rails.rb +12 -2
- data/lib/sidekiq/redis_connection.rb +14 -3
- data/lib/sidekiq/ring_buffer.rb +1 -0
- data/lib/sidekiq/transaction_aware_client.rb +13 -5
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +2 -2
- data/lib/sidekiq/web/helpers.rb +1 -1
- data/sidekiq.gemspec +3 -3
- data/web/assets/javascripts/dashboard-charts.js +2 -0
- data/web/assets/javascripts/dashboard.js +6 -0
- data/web/locales/en.yml +3 -0
- data/web/views/_footer.erb +1 -2
- data/web/views/dashboard.erb +4 -1
- data/web/views/queue.erb +1 -1
- metadata +23 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c8347f3f2281d531c67cac5b8d00a38baa08c2fa144d111e0d380c9520e1d88
|
|
4
|
+
data.tar.gz: 70f09f133c258fa1239d98f294e66ba9fba21dbb55573a76510d128b795b0180
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 308446f60faf0b6c180c62562f8e787e68f7654d9339e4c43b0219013b952a945e728117c8b8121d57a91fc1cb71eeecb560779f29579801d76f9a33fdaac963
|
|
7
|
+
data.tar.gz: '0392876234c063d8d9a2ecc10c6e2a79bd8dfea10fa4d547fb9046c134917d6aa958b9c7946c95797c6dc75ebfc694c52cd8e30602a5598d093e75281ce0f03f'
|
data/Changes.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
[Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
|
|
4
4
|
|
|
5
|
+
7.3.10
|
|
6
|
+
----------
|
|
7
|
+
|
|
8
|
+
- Allow Redis :password as a Proc [#6625]
|
|
9
|
+
`config.redis = { password: ->(username) { "password" } }`
|
|
10
|
+
- Bump required redis-client version to 0.23
|
|
11
|
+
- Lock dependencies to known good major versions
|
|
12
|
+
|
|
13
|
+
7.3.9
|
|
14
|
+
----------
|
|
15
|
+
|
|
16
|
+
- Only require activejob if necessary [#6584]
|
|
17
|
+
- Fix iterable job cancellation [#6589]
|
|
18
|
+
- Web UI accessibility improvements [#6604]
|
|
19
|
+
|
|
5
20
|
7.3.8
|
|
6
21
|
----------
|
|
7
22
|
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module Sidekiq
|
|
4
|
-
module ActiveJob
|
|
5
|
-
# @api private
|
|
6
|
-
class Wrapper
|
|
7
|
-
include Sidekiq::Job
|
|
8
|
-
|
|
9
|
-
def perform(job_data)
|
|
10
|
-
::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
3
|
module ActiveJob
|
|
17
4
|
module QueueAdapters
|
|
18
5
|
# Explicitly remove the implementation existing in older rails'.
|
data/lib/sidekiq/fetch.rb
CHANGED
data/lib/sidekiq/job/iterable.rb
CHANGED
|
@@ -200,7 +200,7 @@ module Sidekiq
|
|
|
200
200
|
if ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - state_flushed_at >= STATE_FLUSH_INTERVAL || is_interrupted
|
|
201
201
|
_, _, cancelled = flush_state
|
|
202
202
|
state_flushed_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
203
|
-
if cancelled
|
|
203
|
+
if cancelled
|
|
204
204
|
@_cancelled = true
|
|
205
205
|
logger.info { "Job cancelled" }
|
|
206
206
|
return true
|
|
@@ -11,6 +11,7 @@ module Sidekiq::Middleware::I18n
|
|
|
11
11
|
# to be sent to Sidekiq.
|
|
12
12
|
class Client
|
|
13
13
|
include Sidekiq::ClientMiddleware
|
|
14
|
+
|
|
14
15
|
def call(_jobclass, job, _queue, _redis)
|
|
15
16
|
job["locale"] ||= I18n.locale
|
|
16
17
|
yield
|
|
@@ -20,6 +21,7 @@ module Sidekiq::Middleware::I18n
|
|
|
20
21
|
# Pull the msg locale out and set the current thread to use it.
|
|
21
22
|
class Server
|
|
22
23
|
include Sidekiq::ServerMiddleware
|
|
24
|
+
|
|
23
25
|
def call(_jobclass, job, _queue, &block)
|
|
24
26
|
I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
|
|
25
27
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
|
@@ -4,6 +4,17 @@ require "sidekiq/job"
|
|
|
4
4
|
require "rails"
|
|
5
5
|
|
|
6
6
|
module Sidekiq
|
|
7
|
+
module ActiveJob
|
|
8
|
+
# @api private
|
|
9
|
+
class Wrapper
|
|
10
|
+
include Sidekiq::Job
|
|
11
|
+
|
|
12
|
+
def perform(job_data)
|
|
13
|
+
::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
7
18
|
class Rails < ::Rails::Engine
|
|
8
19
|
class Reloader
|
|
9
20
|
def initialize(app = ::Rails.application)
|
|
@@ -38,9 +49,8 @@ module Sidekiq
|
|
|
38
49
|
# end
|
|
39
50
|
# end
|
|
40
51
|
initializer "sidekiq.active_job_integration" do
|
|
41
|
-
require "active_job/queue_adapters/sidekiq_adapter"
|
|
42
|
-
|
|
43
52
|
ActiveSupport.on_load(:active_job) do
|
|
53
|
+
require_relative "../active_job/queue_adapters/sidekiq_adapter"
|
|
44
54
|
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
|
45
55
|
end
|
|
46
56
|
end
|
|
@@ -10,6 +10,8 @@ module Sidekiq
|
|
|
10
10
|
def create(options = {})
|
|
11
11
|
symbolized_options = deep_symbolize_keys(options)
|
|
12
12
|
symbolized_options[:url] ||= determine_redis_provider
|
|
13
|
+
symbolized_options[:password] = wrap(symbolized_options[:password]) if symbolized_options.key?(:password)
|
|
14
|
+
symbolized_options[:sentinel_password] = wrap(symbolized_options[:sentinel_password]) if symbolized_options.key?(:sentinel_password)
|
|
13
15
|
|
|
14
16
|
logger = symbolized_options.delete(:logger)
|
|
15
17
|
logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
|
|
@@ -38,6 +40,15 @@ module Sidekiq
|
|
|
38
40
|
|
|
39
41
|
private
|
|
40
42
|
|
|
43
|
+
# Wrap hard-coded passwords in a Proc to avoid logging the value
|
|
44
|
+
def wrap(pwd)
|
|
45
|
+
if pwd.is_a?(String)
|
|
46
|
+
->(username) { pwd }
|
|
47
|
+
else
|
|
48
|
+
pwd
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
41
52
|
def deep_symbolize_keys(object)
|
|
42
53
|
case object
|
|
43
54
|
when Hash
|
|
@@ -57,14 +68,14 @@ module Sidekiq
|
|
|
57
68
|
# Deep clone so we can muck with these options all we want and exclude
|
|
58
69
|
# params from dump-and-load that may contain objects that Marshal is
|
|
59
70
|
# unable to safely dump.
|
|
60
|
-
keys = options.keys - [:logger, :ssl_params]
|
|
71
|
+
keys = options.keys - [:logger, :ssl_params, :password, :sentinel_password]
|
|
61
72
|
scrubbed_options = Marshal.load(Marshal.dump(options.slice(*keys)))
|
|
62
73
|
if scrubbed_options[:url] && (uri = URI.parse(scrubbed_options[:url])) && uri.password
|
|
63
74
|
uri.password = redacted
|
|
64
75
|
scrubbed_options[:url] = uri.to_s
|
|
65
76
|
end
|
|
66
|
-
scrubbed_options[:password] = redacted if
|
|
67
|
-
scrubbed_options[:sentinel_password] = redacted if
|
|
77
|
+
scrubbed_options[:password] = redacted if options.key?(:password)
|
|
78
|
+
scrubbed_options[:sentinel_password] = redacted if options.key?(:sentinel_password)
|
|
68
79
|
scrubbed_options[:sentinels]&.each do |sentinel|
|
|
69
80
|
if sentinel.is_a?(String)
|
|
70
81
|
if (uri = URI(sentinel)) && uri.password
|
data/lib/sidekiq/ring_buffer.rb
CHANGED
|
@@ -7,6 +7,12 @@ module Sidekiq
|
|
|
7
7
|
class TransactionAwareClient
|
|
8
8
|
def initialize(pool: nil, config: nil)
|
|
9
9
|
@redis_client = Client.new(pool: pool, config: config)
|
|
10
|
+
@transaction_backend =
|
|
11
|
+
if ActiveRecord.version >= Gem::Version.new("7.2")
|
|
12
|
+
ActiveRecord.method(:after_all_transactions_commit)
|
|
13
|
+
else
|
|
14
|
+
AfterCommitEverywhere.method(:after_commit)
|
|
15
|
+
end
|
|
10
16
|
end
|
|
11
17
|
|
|
12
18
|
def batching?
|
|
@@ -20,7 +26,7 @@ module Sidekiq
|
|
|
20
26
|
# pre-allocate the JID so we can return it immediately and
|
|
21
27
|
# save it to the database as part of the transaction.
|
|
22
28
|
item["jid"] ||= SecureRandom.hex(12)
|
|
23
|
-
|
|
29
|
+
@transaction_backend.call { @redis_client.push(item) }
|
|
24
30
|
item["jid"]
|
|
25
31
|
end
|
|
26
32
|
|
|
@@ -38,10 +44,12 @@ end
|
|
|
38
44
|
# Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
|
|
39
45
|
module Sidekiq
|
|
40
46
|
def self.transactional_push!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
if ActiveRecord.version < Gem::Version.new("7.2")
|
|
48
|
+
begin
|
|
49
|
+
require "after_commit_everywhere"
|
|
50
|
+
rescue LoadError
|
|
51
|
+
raise %q(You need ActiveRecord >= 7.2 or to add `gem "after_commit_everywhere"` to your Gemfile to use Sidekiq's transactional client)
|
|
52
|
+
end
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
Sidekiq.default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
data/lib/sidekiq/version.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Sidekiq
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
get "/" do
|
|
58
|
-
@redis_info = redis_info.
|
|
58
|
+
@redis_info = redis_info.slice(*REDIS_KEYS)
|
|
59
59
|
days = (params["days"] || 30).to_i
|
|
60
60
|
return halt(401) if days < 1 || days > 180
|
|
61
61
|
|
|
@@ -328,7 +328,7 @@ module Sidekiq
|
|
|
328
328
|
|
|
329
329
|
get "/stats" do
|
|
330
330
|
sidekiq_stats = Sidekiq::Stats.new
|
|
331
|
-
redis_stats = redis_info.
|
|
331
|
+
redis_stats = redis_info.slice(*REDIS_KEYS)
|
|
332
332
|
json(
|
|
333
333
|
sidekiq: {
|
|
334
334
|
processed: sidekiq_stats.processed,
|
data/lib/sidekiq/web/helpers.rb
CHANGED
|
@@ -347,7 +347,7 @@ module Sidekiq
|
|
|
347
347
|
elsif rss_kb < 10_000_000
|
|
348
348
|
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
|
349
349
|
else
|
|
350
|
-
"#{number_with_delimiter(
|
|
350
|
+
"#{number_with_delimiter(rss_kb / (1024.0 * 1024.0), precision: 1)} GB"
|
|
351
351
|
end
|
|
352
352
|
end
|
|
353
353
|
|
data/sidekiq.gemspec
CHANGED
|
@@ -23,9 +23,9 @@ Gem::Specification.new do |gem|
|
|
|
23
23
|
"rubygems_mfa_required" => "true"
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
gem.add_dependency "redis-client", ">= 0.
|
|
27
|
-
gem.add_dependency "connection_pool", ">= 2.3.0"
|
|
28
|
-
gem.add_dependency "rack", ">= 2.2.4"
|
|
26
|
+
gem.add_dependency "redis-client", ">= 0.23.0", "<1"
|
|
27
|
+
gem.add_dependency "connection_pool", ">= 2.3.0", "<3"
|
|
28
|
+
gem.add_dependency "rack", ">= 2.2.4", "<3.3"
|
|
29
29
|
gem.add_dependency "logger"
|
|
30
30
|
gem.add_dependency "base64"
|
|
31
31
|
end
|
|
@@ -83,6 +83,8 @@ class RealtimeChart extends DashboardChart {
|
|
|
83
83
|
this.chart.data.datasets[1].data.push(failed);
|
|
84
84
|
this.chart.update();
|
|
85
85
|
|
|
86
|
+
updateScreenReaderDashboardValues(processed, failed);
|
|
87
|
+
|
|
86
88
|
updateStatsSummary(this.stats.sidekiq);
|
|
87
89
|
updateRedisStats(this.stats.redis);
|
|
88
90
|
updateFooterUTCTime(this.stats.server_utc_time);
|
|
@@ -36,6 +36,12 @@ var ready = (callback) => {
|
|
|
36
36
|
else document.addEventListener("DOMContentLoaded", callback);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
var updateScreenReaderDashboardValues = function(processed, failed) {
|
|
40
|
+
let lastDashboardUpdateSpan = document.getElementById("sr-last-dashboard-update");
|
|
41
|
+
var updateText = document.getElementById("sr-last-dashboard-update-template").innerText;
|
|
42
|
+
lastDashboardUpdateSpan.innerText = updateText.replace("PROCESSED_COUNT", processed).replace("FAILED_COUNT", failed);
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
ready(() => {
|
|
40
46
|
var sldr = document.getElementById('sldr');
|
|
41
47
|
if (typeof localStorage.sidekiqTimeInterval !== 'undefined') {
|
data/web/locales/en.yml
CHANGED
|
@@ -34,6 +34,8 @@ en:
|
|
|
34
34
|
Jobs: Jobs
|
|
35
35
|
Kill: Kill
|
|
36
36
|
KillAll: Kill All
|
|
37
|
+
Language: Language
|
|
38
|
+
LastDashboardUpdateTemplateLiteral: "Latest poll: Processed: PROCESSED_COUNT. Failed: FAILED_COUNT."
|
|
37
39
|
LastRetry: Last Retry
|
|
38
40
|
Latency: Latency
|
|
39
41
|
LivePoll: Live Poll
|
|
@@ -53,6 +55,7 @@ en:
|
|
|
53
55
|
PeakMemoryUsage: Peak Memory Usage
|
|
54
56
|
Plugins: Plugins
|
|
55
57
|
PollingInterval: Polling interval
|
|
58
|
+
PollingIntervalMilliseconds: Polling interval milliseconds
|
|
56
59
|
Process: Process
|
|
57
60
|
Processed: Processed
|
|
58
61
|
Processes: Processes
|
data/web/views/_footer.erb
CHANGED
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
<li>
|
|
18
18
|
<form id="locale-form" class="form-inline" action="<%= root_path %>change_locale" method="post">
|
|
19
19
|
<%= csrf_tag %>
|
|
20
|
-
<
|
|
21
|
-
<select id="locale-select" class="form-control" name="locale">
|
|
20
|
+
<select id="locale-select" class="form-control" aria-label="<%= t("Language") %>" name="locale">
|
|
22
21
|
<% available_locales.each do |locale_option| %>
|
|
23
22
|
<% if locale_option == locale %>
|
|
24
23
|
<option selected value="<%= locale_option %>"><%= locale_option %></option>
|
data/web/views/dashboard.erb
CHANGED
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
<span class="interval-slider-label"><%= t('PollingInterval') %>:</span>
|
|
12
12
|
<span id="sldr-text" class="current-interval">5 s</span>
|
|
13
13
|
<br/>
|
|
14
|
-
<input id="sldr" type="range" min="2000" max="20000" step="1000" value="5000"/>
|
|
14
|
+
<input id="sldr" aria-label="<%= t("PollingIntervalMilliseconds") %>" type="range" min="2000" max="20000" step="1000" value="5000"/>
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div class="row chart">
|
|
19
|
+
<span id="sr-last-dashboard-update-template" hidden="hidden"><%= t("LastDashboardUpdateTemplateLiteral") %></span>
|
|
20
|
+
<span id="sr-last-dashboard-update" class="sr-only" role="status"></span>
|
|
21
|
+
|
|
19
22
|
<canvas id="realtime-chart">
|
|
20
23
|
<%= to_json({
|
|
21
24
|
processedLabel: t('Processed'),
|
data/web/views/queue.erb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sidekiq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.3.
|
|
4
|
+
version: 7.3.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Perham
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: redis-client
|
|
@@ -16,14 +15,20 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
18
|
+
version: 0.23.0
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1'
|
|
20
22
|
type: :runtime
|
|
21
23
|
prerelease: false
|
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
25
|
requirements:
|
|
24
26
|
- - ">="
|
|
25
27
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
28
|
+
version: 0.23.0
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '1'
|
|
27
32
|
- !ruby/object:Gem::Dependency
|
|
28
33
|
name: connection_pool
|
|
29
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -31,6 +36,9 @@ dependencies:
|
|
|
31
36
|
- - ">="
|
|
32
37
|
- !ruby/object:Gem::Version
|
|
33
38
|
version: 2.3.0
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '3'
|
|
34
42
|
type: :runtime
|
|
35
43
|
prerelease: false
|
|
36
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -38,6 +46,9 @@ dependencies:
|
|
|
38
46
|
- - ">="
|
|
39
47
|
- !ruby/object:Gem::Version
|
|
40
48
|
version: 2.3.0
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '3'
|
|
41
52
|
- !ruby/object:Gem::Dependency
|
|
42
53
|
name: rack
|
|
43
54
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -45,6 +56,9 @@ dependencies:
|
|
|
45
56
|
- - ">="
|
|
46
57
|
- !ruby/object:Gem::Version
|
|
47
58
|
version: 2.2.4
|
|
59
|
+
- - "<"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.3'
|
|
48
62
|
type: :runtime
|
|
49
63
|
prerelease: false
|
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +66,9 @@ dependencies:
|
|
|
52
66
|
- - ">="
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: 2.2.4
|
|
69
|
+
- - "<"
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '3.3'
|
|
55
72
|
- !ruby/object:Gem::Dependency
|
|
56
73
|
name: logger
|
|
57
74
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -231,7 +248,6 @@ metadata:
|
|
|
231
248
|
changelog_uri: https://github.com/sidekiq/sidekiq/blob/main/Changes.md
|
|
232
249
|
source_code_uri: https://github.com/sidekiq/sidekiq
|
|
233
250
|
rubygems_mfa_required: 'true'
|
|
234
|
-
post_install_message:
|
|
235
251
|
rdoc_options: []
|
|
236
252
|
require_paths:
|
|
237
253
|
- lib
|
|
@@ -246,8 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
246
262
|
- !ruby/object:Gem::Version
|
|
247
263
|
version: '0'
|
|
248
264
|
requirements: []
|
|
249
|
-
rubygems_version: 3.
|
|
250
|
-
signing_key:
|
|
265
|
+
rubygems_version: 3.6.9
|
|
251
266
|
specification_version: 4
|
|
252
267
|
summary: Simple, efficient background processing for Ruby
|
|
253
268
|
test_files: []
|