faktory_worker_ruby 0.6.1 → 0.7.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 +4 -4
- data/Changes.md +5 -0
- data/Gemfile.lock +2 -2
- data/faktory_worker_ruby.gemspec +1 -1
- data/lib/faktory/cli.rb +12 -1
- data/lib/faktory/client.rb +11 -10
- data/lib/faktory/launcher.rb +3 -2
- data/lib/faktory/middleware/chain.rb +4 -4
- data/lib/faktory/processor.rb +3 -3
- data/lib/faktory/testing.rb +336 -0
- data/lib/faktory/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 672affacf993528eedff478961a96bbc222e62bc
|
4
|
+
data.tar.gz: 8b58b6e46e4d6965690bc4a17f3067548671edd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7210a8933baf0a671a608d41b5bb183fec3ea93b5c233c937c3882e921634d1baedb5985be1ecd6142ee1ca9257d3483012a7511f40c4f58fc4c13d0f34a4f4f
|
7
|
+
data.tar.gz: 1a2e769ad7b96d539842e29c7056e36523d878cb81ccda8610247b99e8f239877dfbe7caee93be6d194213801c2a31fbfb876c69f5a5850bad6b15461661c4e4
|
data/Changes.md
CHANGED
data/Gemfile.lock
CHANGED
data/faktory_worker_ruby.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.email = ["mike@contribsys.com"]
|
8
8
|
gem.summary = "Ruby worker for Faktory"
|
9
9
|
gem.description = "Ruby worker for Faktory."
|
10
|
-
gem.homepage = "
|
10
|
+
gem.homepage = "https://github.com/contribsys/faktory_worker_ruby"
|
11
11
|
gem.license = "LGPL-3.0"
|
12
12
|
|
13
13
|
gem.executables = ['faktory-worker']
|
data/lib/faktory/cli.rb
CHANGED
@@ -99,7 +99,18 @@ module Faktory
|
|
99
99
|
|
100
100
|
def self.banner
|
101
101
|
%q{
|
102
|
-
|
102
|
+
,,,,
|
103
|
+
,,,, | |
|
104
|
+
| | | |
|
105
|
+
| | | |
|
106
|
+
| |,,~~' '~, ___ _
|
107
|
+
,,~~'' ,~~, '~, / __) | | _
|
108
|
+
,,~~'' ,~~, | | | _| |__ _____| | _ _| |_ ___ ____ _ _
|
109
|
+
| ,~~, | | | | | (_ __|____ | |_/ |_ _) _ \ / ___) | | |
|
110
|
+
| | | | | | | | | | / ___ | _ ( | || |_| | | | |_| |
|
111
|
+
| |__| |__| |__| | |_| \_____|_| \_) \__)___/|_| \__ |
|
112
|
+
|__________________________| (____/
|
113
|
+
|
103
114
|
}
|
104
115
|
end
|
105
116
|
|
data/lib/faktory/client.rb
CHANGED
@@ -35,9 +35,9 @@ module Faktory
|
|
35
35
|
# MY_FAKTORY_URL=tcp://:somepass@my-server.example.com:7419
|
36
36
|
#
|
37
37
|
# Note above, the URL can contain the password for secure installations.
|
38
|
-
def initialize(url: 'tcp://localhost:7419', debug: false)
|
38
|
+
def initialize(url: uri_from_env || 'tcp://localhost:7419', debug: false)
|
39
39
|
@debug = debug
|
40
|
-
@location =
|
40
|
+
@location = URI(url)
|
41
41
|
open
|
42
42
|
end
|
43
43
|
|
@@ -235,14 +235,15 @@ module Faktory
|
|
235
235
|
# MY_FAKTORY_URL=tcp://:some-pass@some-hostname:7419
|
236
236
|
def uri_from_env
|
237
237
|
prov = ENV['FAKTORY_PROVIDER']
|
238
|
-
|
239
|
-
|
240
|
-
Invalid FAKTORY_PROVIDER '#{prov}', it should be the name of the ENV variable that contains the URL
|
241
|
-
|
242
|
-
|
243
|
-
EOM
|
244
|
-
|
245
|
-
|
238
|
+
if prov
|
239
|
+
raise(ArgumentError, <<-EOM) if prov.index(":")
|
240
|
+
Invalid FAKTORY_PROVIDER '#{prov}', it should be the name of the ENV variable that contains the URL
|
241
|
+
FAKTORY_PROVIDER=MY_FAKTORY_URL
|
242
|
+
MY_FAKTORY_URL=tcp://:some-pass@some-hostname:7419
|
243
|
+
EOM
|
244
|
+
val = ENV[prov]
|
245
|
+
return URI(val) if val
|
246
|
+
end
|
246
247
|
|
247
248
|
val = ENV['FAKTORY_URL']
|
248
249
|
return URI(val) if val
|
data/lib/faktory/launcher.rb
CHANGED
@@ -9,9 +9,10 @@ module Faktory
|
|
9
9
|
attr_accessor :manager
|
10
10
|
|
11
11
|
def initialize(options)
|
12
|
-
|
12
|
+
merged_options = Faktory.options.merge(options)
|
13
|
+
@manager = Faktory::Manager.new(merged_options)
|
13
14
|
@done = false
|
14
|
-
@options =
|
15
|
+
@options = merged_options
|
15
16
|
end
|
16
17
|
|
17
18
|
def run
|
@@ -9,7 +9,7 @@ module Faktory
|
|
9
9
|
# To add middleware to run when a job is pushed to Faktory:
|
10
10
|
#
|
11
11
|
# Faktory.configure_client do |config|
|
12
|
-
# config.
|
12
|
+
# config.client_middleware do |chain|
|
13
13
|
# chain.add MyClientHook
|
14
14
|
# end
|
15
15
|
# end
|
@@ -27,15 +27,15 @@ module Faktory
|
|
27
27
|
# To insert immediately preceding another entry:
|
28
28
|
#
|
29
29
|
# Faktory.configure_client do |config|
|
30
|
-
# config.
|
31
|
-
# chain.insert_before
|
30
|
+
# config.client_middleware do |chain|
|
31
|
+
# chain.insert_before SomeOtherMiddleware, MyClientHook
|
32
32
|
# end
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
# To insert immediately after another entry:
|
36
36
|
#
|
37
37
|
# Faktory.configure_client do |config|
|
38
|
-
# config.
|
38
|
+
# config.client_middleware do |chain|
|
39
39
|
# chain.insert_after ActiveRecord, MyClientHook
|
40
40
|
# end
|
41
41
|
# end
|
data/lib/faktory/processor.rb
CHANGED
@@ -39,9 +39,9 @@ module Faktory
|
|
39
39
|
@down = false
|
40
40
|
@done = false
|
41
41
|
@thread = nil
|
42
|
-
@reloader =
|
42
|
+
@reloader = mgr.options[:reloader]
|
43
43
|
@logging = (mgr.options[:job_logger] || Faktory::JobLogger).new
|
44
|
-
@fetcher = Faktory::Fetcher.new(
|
44
|
+
@fetcher = Faktory::Fetcher.new(mgr.options)
|
45
45
|
end
|
46
46
|
|
47
47
|
def terminate(wait=false)
|
@@ -153,7 +153,7 @@ module Faktory
|
|
153
153
|
# within the timeout. Don't acknowledge the work since
|
154
154
|
# we didn't properly finish it.
|
155
155
|
rescue Exception => ex
|
156
|
-
handle_exception(ex, { :context => "Job raised exception", :job => job })
|
156
|
+
handle_exception(ex, { :context => "Job raised exception", :job => work.job })
|
157
157
|
work.fail(ex)
|
158
158
|
raise ex
|
159
159
|
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
module Faktory
|
2
|
+
module Testing
|
3
|
+
def self.__test_mode
|
4
|
+
@__test_mode
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.__test_mode=(mode)
|
8
|
+
@__test_mode = mode
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.__set_test_mode(mode)
|
12
|
+
if block_given?
|
13
|
+
current_mode = self.__test_mode
|
14
|
+
begin
|
15
|
+
self.__test_mode = mode
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
self.__test_mode = current_mode
|
19
|
+
end
|
20
|
+
else
|
21
|
+
self.__test_mode = mode
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.disable!(&block)
|
26
|
+
__set_test_mode(:disable, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.fake!(&block)
|
30
|
+
__set_test_mode(:fake, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.inline!(&block)
|
34
|
+
# Only allow blockless inline via `blockless_inline_is_a_bad_idea_but_I_wanna_do_it_anyway!`
|
35
|
+
# https://github.com/mperham/sidekiq/issues/3495
|
36
|
+
unless block_given?
|
37
|
+
raise 'Must provide a block to Faktory::Testing.inline!'
|
38
|
+
end
|
39
|
+
|
40
|
+
__set_test_mode(:inline, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.blockless_inline_is_a_bad_idea_but_I_wanna_do_it_anyway!
|
44
|
+
__set_test_mode(:inline)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.enabled?
|
48
|
+
self.__test_mode != :disable
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.disabled?
|
52
|
+
self.__test_mode == :disable
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.fake?
|
56
|
+
self.__test_mode == :fake
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.inline?
|
60
|
+
self.__test_mode == :inline
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.constantize(str)
|
64
|
+
names = str.split('::')
|
65
|
+
names.shift if names.empty? || names.first.empty?
|
66
|
+
|
67
|
+
names.inject(Object) do |constant, name|
|
68
|
+
# the false flag limits search for name to under the constant namespace
|
69
|
+
# which mimics Rails' behaviour
|
70
|
+
constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Test modes have to be opted into explicitly.
|
76
|
+
# Just requiring the testing module shouldn't automatically change the testing mode.
|
77
|
+
Faktory::Testing.disable!
|
78
|
+
|
79
|
+
class EmptyQueueError < RuntimeError; end
|
80
|
+
|
81
|
+
class Client
|
82
|
+
alias_method :real_push, :push
|
83
|
+
alias_method :real_open, :open
|
84
|
+
|
85
|
+
def push(job)
|
86
|
+
if Faktory::Testing.inline?
|
87
|
+
job = Faktory.load_json(Faktory.dump_json(job))
|
88
|
+
job_class = Faktory::Testing.constantize(job['jobtype'])
|
89
|
+
job_class.new.perform(*job['args'])
|
90
|
+
return job['jid']
|
91
|
+
elsif Faktory::Testing.fake?
|
92
|
+
job = Faktory.load_json(Faktory.dump_json(job))
|
93
|
+
job.merge!('enqueued_at' => Time.now.to_f) unless job['at']
|
94
|
+
Queues.push(job['queue'], job['jobtype'], job)
|
95
|
+
return job['jid']
|
96
|
+
else
|
97
|
+
real_push(job)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def open
|
102
|
+
unless Faktory::Testing.enabled?
|
103
|
+
real_open
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module Queues
|
109
|
+
##
|
110
|
+
# The Queues class is only for testing the fake queue implementation.
|
111
|
+
# There are 2 data structures involved in tandem. This is due to the
|
112
|
+
# Rspec syntax of change(QueueWorker.jobs, :size). It keeps a reference
|
113
|
+
# to the array. Because the array was dervied from a filter of the total
|
114
|
+
# jobs enqueued, it appeared as though the array didn't change.
|
115
|
+
#
|
116
|
+
# To solve this, we'll keep 2 hashes containing the jobs. One with keys based
|
117
|
+
# on the queue, and another with keys of the worker names, so the array for
|
118
|
+
# QueueWorker.jobs is a straight reference to a real array.
|
119
|
+
#
|
120
|
+
# Queue-based hash:
|
121
|
+
#
|
122
|
+
# {
|
123
|
+
# "default"=>[
|
124
|
+
# {
|
125
|
+
# "class"=>"TestTesting::QueueWorker",
|
126
|
+
# "args"=>[1, 2],
|
127
|
+
# "retry"=>true,
|
128
|
+
# "queue"=>"default",
|
129
|
+
# "jid"=>"abc5b065c5c4b27fc1102833",
|
130
|
+
# "created_at"=>1447445554.419934
|
131
|
+
# }
|
132
|
+
# ]
|
133
|
+
# }
|
134
|
+
#
|
135
|
+
# Worker-based hash:
|
136
|
+
#
|
137
|
+
# {
|
138
|
+
# "TestTesting::QueueWorker"=>[
|
139
|
+
# {
|
140
|
+
# "class"=>"TestTesting::QueueWorker",
|
141
|
+
# "args"=>[1, 2],
|
142
|
+
# "retry"=>true,
|
143
|
+
# "queue"=>"default",
|
144
|
+
# "jid"=>"abc5b065c5c4b27fc1102833",
|
145
|
+
# "created_at"=>1447445554.419934
|
146
|
+
# }
|
147
|
+
# ]
|
148
|
+
# }
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
#
|
152
|
+
# require 'faktory/testing'
|
153
|
+
#
|
154
|
+
# assert_equal 0, Faktory::Queues["default"].size
|
155
|
+
# HardWorker.perform_async(:something)
|
156
|
+
# assert_equal 1, Faktory::Queues["default"].size
|
157
|
+
# assert_equal :something, Faktory::Queues["default"].first['args'][0]
|
158
|
+
#
|
159
|
+
# You can also clear all workers' jobs:
|
160
|
+
#
|
161
|
+
# assert_equal 0, Faktory::Queues["default"].size
|
162
|
+
# HardWorker.perform_async(:something)
|
163
|
+
# Faktory::Queues.clear_all
|
164
|
+
# assert_equal 0, Faktory::Queues["default"].size
|
165
|
+
#
|
166
|
+
# This can be useful to make sure jobs don't linger between tests:
|
167
|
+
#
|
168
|
+
# RSpec.configure do |config|
|
169
|
+
# config.before(:each) do
|
170
|
+
# Faktory::Queues.clear_all
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
class << self
|
175
|
+
def [](queue)
|
176
|
+
jobs_by_queue[queue]
|
177
|
+
end
|
178
|
+
|
179
|
+
def push(queue, klass, job)
|
180
|
+
jobs_by_queue[queue] << job
|
181
|
+
jobs_by_worker[klass] << job
|
182
|
+
end
|
183
|
+
|
184
|
+
def jobs_by_queue
|
185
|
+
@jobs_by_queue ||= Hash.new { |hash, key| hash[key] = [] }
|
186
|
+
end
|
187
|
+
|
188
|
+
def jobs_by_worker
|
189
|
+
@jobs_by_worker ||= Hash.new { |hash, key| hash[key] = [] }
|
190
|
+
end
|
191
|
+
|
192
|
+
def delete_for(jid, queue, klass)
|
193
|
+
jobs_by_queue[queue.to_s].delete_if { |job| job["jid"] == jid }
|
194
|
+
jobs_by_worker[klass].delete_if { |job| job["jid"] == jid }
|
195
|
+
end
|
196
|
+
|
197
|
+
def clear_for(queue, klass)
|
198
|
+
jobs_by_queue[queue].clear
|
199
|
+
jobs_by_worker[klass].clear
|
200
|
+
end
|
201
|
+
|
202
|
+
def clear_all
|
203
|
+
jobs_by_queue.clear
|
204
|
+
jobs_by_worker.clear
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
module Job
|
210
|
+
##
|
211
|
+
# The Faktory testing infrastructure overrides perform_async
|
212
|
+
# so that it does not actually touch the network. Instead it
|
213
|
+
# stores the asynchronous jobs in a per-class array so that
|
214
|
+
# their presence/absence can be asserted by your tests.
|
215
|
+
#
|
216
|
+
# This is similar to ActionMailer's :test delivery_method and its
|
217
|
+
# ActionMailer::Base.deliveries array.
|
218
|
+
#
|
219
|
+
# Example:
|
220
|
+
#
|
221
|
+
# require 'faktory/testing'
|
222
|
+
#
|
223
|
+
# assert_equal 0, HardWorker.jobs.size
|
224
|
+
# HardWorker.perform_async(:something)
|
225
|
+
# assert_equal 1, HardWorker.jobs.size
|
226
|
+
# assert_equal :something, HardWorker.jobs[0]['args'][0]
|
227
|
+
#
|
228
|
+
# assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
|
229
|
+
# MyMailer.delay.send_welcome_email('foo@example.com')
|
230
|
+
# assert_equal 1, Faktory::Extensions::DelayedMailer.jobs.size
|
231
|
+
#
|
232
|
+
# You can also clear and drain all workers' jobs:
|
233
|
+
#
|
234
|
+
# assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
|
235
|
+
# assert_equal 0, Faktory::Extensions::DelayedModel.jobs.size
|
236
|
+
#
|
237
|
+
# MyMailer.delay.send_welcome_email('foo@example.com')
|
238
|
+
# MyModel.delay.do_something_hard
|
239
|
+
#
|
240
|
+
# assert_equal 1, Faktory::Extensions::DelayedMailer.jobs.size
|
241
|
+
# assert_equal 1, Faktory::Extensions::DelayedModel.jobs.size
|
242
|
+
#
|
243
|
+
# Faktory::Worker.clear_all # or .drain_all
|
244
|
+
#
|
245
|
+
# assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
|
246
|
+
# assert_equal 0, Faktory::Extensions::DelayedModel.jobs.size
|
247
|
+
#
|
248
|
+
# This can be useful to make sure jobs don't linger between tests:
|
249
|
+
#
|
250
|
+
# RSpec.configure do |config|
|
251
|
+
# config.before(:each) do
|
252
|
+
# Faktory::Worker.clear_all
|
253
|
+
# end
|
254
|
+
# end
|
255
|
+
#
|
256
|
+
# or for acceptance testing, i.e. with cucumber:
|
257
|
+
#
|
258
|
+
# AfterStep do
|
259
|
+
# Faktory::Worker.drain_all
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# When I sign up as "foo@example.com"
|
263
|
+
# Then I should receive a welcome email to "foo@example.com"
|
264
|
+
#
|
265
|
+
module ClassMethods
|
266
|
+
|
267
|
+
# Queue for this worker
|
268
|
+
def queue
|
269
|
+
self.faktory_options["queue"]
|
270
|
+
end
|
271
|
+
|
272
|
+
# Jobs queued for this worker
|
273
|
+
def jobs
|
274
|
+
Queues.jobs_by_worker[self.to_s]
|
275
|
+
end
|
276
|
+
|
277
|
+
# Clear all jobs for this worker
|
278
|
+
def clear
|
279
|
+
Queues.clear_for(queue, self.to_s)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Drain and run all jobs for this worker
|
283
|
+
def drain
|
284
|
+
while jobs.any?
|
285
|
+
next_job = jobs.first
|
286
|
+
Queues.delete_for(next_job["jid"], next_job["queue"], self.to_s)
|
287
|
+
process_job(next_job)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Pop out a single job and perform it
|
292
|
+
def perform_one
|
293
|
+
raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
|
294
|
+
next_job = jobs.first
|
295
|
+
Queues.delete_for(next_job["jid"], queue, self.to_s)
|
296
|
+
process_job(next_job)
|
297
|
+
end
|
298
|
+
|
299
|
+
def process_job(job)
|
300
|
+
worker = new
|
301
|
+
worker.jid = job['jid']
|
302
|
+
worker.bid = job['bid'] if worker.respond_to?(:bid=)
|
303
|
+
#Faktory::Testing.server_middleware.invoke(worker, job, job['queue']) do
|
304
|
+
execute_job(worker, job['args'])
|
305
|
+
#end
|
306
|
+
end
|
307
|
+
|
308
|
+
def execute_job(worker, args)
|
309
|
+
worker.perform(*args)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
class << self
|
314
|
+
def jobs # :nodoc:
|
315
|
+
Queues.jobs_by_queue.values.flatten
|
316
|
+
end
|
317
|
+
|
318
|
+
# Clear all queued jobs across all workers
|
319
|
+
def clear_all
|
320
|
+
Queues.clear_all
|
321
|
+
end
|
322
|
+
|
323
|
+
# Drain all queued jobs across all workers
|
324
|
+
def drain_all
|
325
|
+
while jobs.any?
|
326
|
+
worker_classes = jobs.map { |job| job["jobtype"] }.uniq
|
327
|
+
|
328
|
+
worker_classes.each do |worker_class|
|
329
|
+
Faktory::Testing.constantize(worker_class).drain
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
data/lib/faktory/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faktory_worker_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -104,10 +104,11 @@ files:
|
|
104
104
|
- lib/faktory/middleware/i18n.rb
|
105
105
|
- lib/faktory/processor.rb
|
106
106
|
- lib/faktory/rails.rb
|
107
|
+
- lib/faktory/testing.rb
|
107
108
|
- lib/faktory/util.rb
|
108
109
|
- lib/faktory/version.rb
|
109
110
|
- lib/faktory_worker_ruby.rb
|
110
|
-
homepage:
|
111
|
+
homepage: https://github.com/contribsys/faktory_worker_ruby
|
111
112
|
licenses:
|
112
113
|
- LGPL-3.0
|
113
114
|
metadata: {}
|
@@ -127,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
128
|
version: '0'
|
128
129
|
requirements: []
|
129
130
|
rubyforge_project:
|
130
|
-
rubygems_version: 2.6.
|
131
|
+
rubygems_version: 2.6.13
|
131
132
|
signing_key:
|
132
133
|
specification_version: 4
|
133
134
|
summary: Ruby worker for Faktory
|