faktory_worker_ruby 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- require 'faktory/client'
1
+ require "faktory/client"
2
2
 
3
3
  ##
4
4
  #
@@ -13,7 +13,6 @@ require 'faktory/client'
13
13
  # filter.matching("*uid:12345*"))
14
14
  # end
15
15
  module Faktory
16
-
17
16
  # Valid targets
18
17
  RETRIES = "retries"
19
18
  SCHEDULED = "scheduled"
@@ -46,6 +45,7 @@ module Faktory
46
45
  def jids
47
46
  @hash[:jids] ||= []
48
47
  end
48
+
49
49
  def jids=(ary)
50
50
  @hash[:jids] = Array(ary)
51
51
  end
@@ -53,30 +53,30 @@ module Faktory
53
53
 
54
54
  def discard(target, &block)
55
55
  filter = Filter.new
56
- block.call(filter) if block
57
- mutate('discard', target, filter)
56
+ block&.call(filter)
57
+ mutate("discard", target, filter)
58
58
  end
59
59
 
60
60
  def kill(target, &block)
61
61
  filter = Filter.new
62
- block.call(filter) if block
63
- mutate('kill', target, filter)
62
+ block&.call(filter)
63
+ mutate("kill", target, filter)
64
64
  end
65
65
 
66
66
  def requeue(target, &block)
67
67
  filter = Filter.new
68
- block.call(filter) if block
69
- mutate('requeue', target, filter)
68
+ block&.call(filter)
69
+ mutate("requeue", target, filter)
70
70
  end
71
71
 
72
72
  def clear(target)
73
- mutate('discard', target, nil)
73
+ mutate("discard", target, nil)
74
74
  end
75
75
 
76
76
  private
77
77
 
78
78
  def mutate(cmd, target, filter)
79
- payload = {:cmd => cmd,:target => target}
79
+ payload = {cmd: cmd, target: target}
80
80
  payload[:filter] = filter.hash if filter && !filter.hash.empty?
81
81
 
82
82
  transaction do
@@ -84,7 +84,6 @@ module Faktory
84
84
  ok
85
85
  end
86
86
  end
87
-
88
87
  end
89
88
  end
90
89
 
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'faktory/util'
3
- require 'faktory/fetch'
4
- require 'faktory/job_logger'
5
- require 'thread'
6
2
 
3
+ require "faktory/util"
4
+ require "faktory/fetch"
5
+ require "faktory/job_logger"
7
6
  module Faktory
8
7
  ##
9
8
  # The Processor is a standalone thread which:
@@ -22,7 +21,6 @@ module Faktory
22
21
  # to replace itself and exits.
23
22
  #
24
23
  class Processor
25
-
26
24
  include Util
27
25
 
28
26
  attr_reader :thread
@@ -44,13 +42,13 @@ module Faktory
44
42
  @fetcher = Faktory::Fetcher.new(mgr.options)
45
43
  end
46
44
 
47
- def terminate(wait=false)
45
+ def terminate(wait = false)
48
46
  @done = true
49
47
  return if !@thread
50
48
  @thread.value if wait
51
49
  end
52
50
 
53
- def kill(wait=false)
51
+ def kill(wait = false)
54
52
  @done = true
55
53
  return if !@thread
56
54
  # unlike the other actors, terminate does not wait
@@ -69,30 +67,28 @@ module Faktory
69
67
  private unless $TESTING
70
68
 
71
69
  def run
72
- begin
73
- while !@done
74
- process_one
75
- end
76
- @mgr.processor_stopped(self)
77
- rescue Faktory::Shutdown
78
- @mgr.processor_stopped(self)
79
- rescue Exception => ex
80
- @mgr.processor_died(self, ex)
70
+ until @done
71
+ process_one
81
72
  end
73
+ @mgr.processor_stopped(self)
74
+ rescue Faktory::Shutdown
75
+ @mgr.processor_stopped(self)
76
+ rescue Exception => ex
77
+ @mgr.processor_died(self, ex)
82
78
  end
83
79
 
84
80
  def process_one
85
81
  work = fetch
86
82
  if work
87
83
  @@busy_lock.synchronize do
88
- @@busy_count = @@busy_count + 1
84
+ @@busy_count += 1
89
85
  end
90
86
  begin
91
87
  @job = work.job
92
88
  process(work)
93
89
  ensure
94
90
  @@busy_lock.synchronize do
95
- @@busy_count = @@busy_count - 1
91
+ @@busy_count -= 1
96
92
  end
97
93
  end
98
94
  else
@@ -101,14 +97,15 @@ module Faktory
101
97
  end
102
98
 
103
99
  def fetch
104
- begin
105
- work = @fetcher.retrieve_work
106
- (logger.info { "Faktory is online, #{Time.now - @down} sec downtime" }; @down = nil) if @down
107
- work
108
- rescue Faktory::Shutdown
109
- rescue => ex
110
- handle_fetch_exception(ex)
100
+ work = @fetcher.retrieve_work
101
+ if @down
102
+ (logger.info { "Faktory is online, #{Time.now - @down} sec downtime" }
103
+ @down = nil)
111
104
  end
105
+ work
106
+ rescue Faktory::Shutdown
107
+ rescue => ex
108
+ handle_fetch_exception(ex)
112
109
  end
113
110
 
114
111
  def handle_fetch_exception(ex)
@@ -131,9 +128,9 @@ module Faktory
131
128
  # the Reloader. It handles code loading, db connection management, etc.
132
129
  # Effectively this block denotes a "unit of work" to Rails.
133
130
  @reloader.call do
134
- klass = constantize(payload['jobtype'.freeze])
131
+ klass = constantize(payload["jobtype"])
135
132
  jobinst = klass.new
136
- jobinst.jid = payload['jid'.freeze]
133
+ jobinst.jid = payload["jid"]
137
134
  yield jobinst
138
135
  end
139
136
  end
@@ -145,7 +142,7 @@ module Faktory
145
142
  begin
146
143
  dispatch(payload) do |jobinst|
147
144
  Faktory.worker_middleware.invoke(jobinst, payload) do
148
- jobinst.perform(*payload['args'.freeze])
145
+ jobinst.perform(*payload["args"])
149
146
  end
150
147
  end
151
148
  work.acknowledge
@@ -155,7 +152,7 @@ module Faktory
155
152
  # and immediately restart it.
156
153
  work.fail(shut)
157
154
  rescue Exception => ex
158
- handle_exception(ex, { :context => "Job raised exception", :job => work.job })
155
+ handle_exception(ex, {context: "Job raised exception", job: work.job})
159
156
  work.fail(ex)
160
157
  raise ex
161
158
  end
@@ -166,13 +163,12 @@ module Faktory
166
163
  end
167
164
 
168
165
  def constantize(str)
169
- names = str.split('::')
166
+ names = str.split("::")
170
167
  names.shift if names.empty? || names.first.empty?
171
168
 
172
169
  names.inject(Object) do |constant, name|
173
170
  constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
174
171
  end
175
172
  end
176
-
177
173
  end
178
174
  end
data/lib/faktory/rails.rb CHANGED
@@ -1,64 +1,67 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Faktory
3
- class Rails < ::Rails::Engine
4
- # This hook happens after `Rails::Application` is inherited within
5
- # config/application.rb and before config is touched, usually within the
6
- # class block. Definitely before config/environments/*.rb and
7
- # config/initializers/*.rb.
8
- config.before_configuration do
9
- if defined?(::ActiveJob)
10
- require 'active_job/queue_adapters/faktory_adapter'
4
+ if defined?(::Rails)
5
+ class Rails < ::Rails::Engine
6
+ # This hook happens after `Rails::Application` is inherited within
7
+ # config/application.rb and before config is touched, usually within the
8
+ # class block. Definitely before config/environments/*.rb and
9
+ # config/initializers/*.rb.
10
+ config.before_configuration do
11
+ if defined?(::ActiveJob)
12
+ require "active_job/queue_adapters/faktory_adapter"
13
+ end
11
14
  end
12
- end
13
15
 
14
- config.after_initialize do
15
- # This hook happens after all initializers are run, just before returning
16
- # from config/environment.rb back to faktory/cli.rb.
17
- # We have to add the reloader after initialize to see if cache_classes has
18
- # been turned on.
19
- #
20
- # None of this matters on the client-side, only within the Faktory executor itself.
21
- #
22
- Faktory.configure_worker do |_|
23
- if ::Rails::VERSION::MAJOR < 5
24
- raise "Your current version of Rails, #{::Rails::VERSION::STRING}, is not supported"
25
- end
16
+ config.after_initialize do
17
+ # This hook happens after all initializers are run, just before returning
18
+ # from config/environment.rb back to faktory/cli.rb.
19
+ # We have to add the reloader after initialize to see if cache_classes has
20
+ # been turned on.
21
+ #
22
+ # None of this matters on the client-side, only within the Faktory executor itself.
23
+ #
24
+ Faktory.configure_worker do |_|
25
+ if ::Rails::VERSION::MAJOR < 5
26
+ raise "Your current version of Rails, #{::Rails::VERSION::STRING}, is not supported"
27
+ end
26
28
 
27
- Faktory.options[:reloader] = Faktory::Rails::Reloader.new
28
- end
29
+ Faktory.options[:reloader] = Faktory::Rails::Reloader.new
30
+ end
29
31
 
30
- begin
31
- # https://github.com/rails/rails/pull/41248
32
- if defined?(::Mail::SMTP)
33
- ::Mail::SMTP::DEFAULTS[:read_timeout] ||= 5
34
- ::Mail::SMTP::DEFAULTS[:open_timeout] ||= 5
32
+ begin
33
+ # https://github.com/rails/rails/pull/41248
34
+ if defined?(::Mail::SMTP)
35
+ ::Mail::SMTP::DEFAULTS[:read_timeout] ||= 5
36
+ ::Mail::SMTP::DEFAULTS[:open_timeout] ||= 5
37
+ end
38
+ rescue
39
+ # ignore
35
40
  end
36
- rescue => ex
37
- # ignore
38
41
  end
39
- end
40
42
 
41
- class Reloader
42
- def initialize(app = ::Rails.application)
43
- @app = app
44
- end
43
+ class Reloader
44
+ def initialize(app = ::Rails.application)
45
+ @app = app
46
+ end
45
47
 
46
- def call
47
- @app.reloader.wrap do
48
- yield
48
+ def call
49
+ @app.reloader.wrap do
50
+ yield
51
+ end
49
52
  end
50
- end
51
53
 
52
- def inspect
53
- "#<Faktory::Rails::Reloader @app=#{@app.class.name}>"
54
+ def inspect
55
+ "#<Faktory::Rails::Reloader @app=#{@app.class.name}>"
56
+ end
54
57
  end
55
58
  end
56
- end if defined?(::Rails)
59
+ end
57
60
 
58
61
  if defined?(::Rails) && ::Rails::VERSION::MAJOR < 5
59
- $stderr.puts("**************************************************")
60
- $stderr.puts("🚫 ERROR: Faktory Worker does not support Rails versions under 5.x - please ensure your workers are updated")
61
- $stderr.puts("**************************************************")
62
- $stderr.puts("")
62
+ warn("**************************************************")
63
+ warn("🚫 ERROR: Faktory Worker does not support Rails versions under 5.x - please ensure your workers are updated")
64
+ warn("**************************************************")
65
+ warn("")
63
66
  end
64
67
  end
@@ -10,7 +10,7 @@ module Faktory
10
10
 
11
11
  def self.__set_test_mode(mode)
12
12
  if block_given?
13
- current_mode = self.__test_mode
13
+ current_mode = __test_mode
14
14
  begin
15
15
  self.__test_mode = mode
16
16
  yield
@@ -33,31 +33,31 @@ module Faktory
33
33
  def self.inline!(&block)
34
34
  # Only allow inline testing inside of a block
35
35
  # https://github.com/mperham/sidekiq/issues/3495
36
- unless block_given?
37
- raise 'Must provide a block to Faktory::Testing.inline!'
36
+ unless block
37
+ raise "Must provide a block to Faktory::Testing.inline!"
38
38
  end
39
39
 
40
40
  __set_test_mode(:inline, &block)
41
41
  end
42
42
 
43
43
  def self.enabled?
44
- self.__test_mode != :disable
44
+ __test_mode != :disable
45
45
  end
46
46
 
47
47
  def self.disabled?
48
- self.__test_mode == :disable
48
+ __test_mode == :disable
49
49
  end
50
50
 
51
51
  def self.fake?
52
- self.__test_mode == :fake
52
+ __test_mode == :fake
53
53
  end
54
54
 
55
55
  def self.inline?
56
- self.__test_mode == :inline
56
+ __test_mode == :inline
57
57
  end
58
58
 
59
59
  def self.constantize(str)
60
- names = str.split('::')
60
+ names = str.split("::")
61
61
  names.shift if names.empty? || names.first.empty?
62
62
 
63
63
  names.inject(Object) do |constant, name|
@@ -81,14 +81,14 @@ module Faktory
81
81
  def push(job)
82
82
  if Faktory::Testing.inline?
83
83
  job = Faktory.load_json(Faktory.dump_json(job))
84
- job_class = Faktory::Testing.constantize(job['jobtype'])
85
- job_class.new.perform(*job['args'])
86
- return job['jid']
84
+ job_class = Faktory::Testing.constantize(job["jobtype"])
85
+ job_class.new.perform(*job["args"])
86
+ job["jid"]
87
87
  elsif Faktory::Testing.fake?
88
88
  job = Faktory.load_json(Faktory.dump_json(job))
89
- job.merge!('enqueued_at' => Time.now.to_f) unless job['at']
90
- Queues.push(job['queue'], job['jobtype'], job)
91
- return job['jid']
89
+ job["enqueued_at"] = Time.now.to_f unless job["at"]
90
+ Queues.push(job["queue"], job["jobtype"], job)
91
+ job["jid"]
92
92
  else
93
93
  real_push(job)
94
94
  end
@@ -259,27 +259,26 @@ module Faktory
259
259
  # Then I should receive a welcome email to "foo@example.com"
260
260
  #
261
261
  module ClassMethods
262
-
263
262
  # Queue for this worker
264
263
  def queue
265
- self.faktory_options["queue"]
264
+ faktory_options["queue"]
266
265
  end
267
266
 
268
267
  # Jobs queued for this worker
269
268
  def jobs
270
- Queues.jobs_by_worker[self.to_s]
269
+ Queues.jobs_by_worker[to_s]
271
270
  end
272
271
 
273
272
  # Clear all jobs for this worker
274
273
  def clear
275
- Queues.clear_for(queue, self.to_s)
274
+ Queues.clear_for(queue, to_s)
276
275
  end
277
276
 
278
277
  # Drain and run all jobs for this worker
279
278
  def drain
280
279
  while jobs.any?
281
280
  next_job = jobs.first
282
- Queues.delete_for(next_job["jid"], next_job["queue"], self.to_s)
281
+ Queues.delete_for(next_job["jid"], next_job["queue"], to_s)
283
282
  process_job(next_job)
284
283
  end
285
284
  end
@@ -288,17 +287,17 @@ module Faktory
288
287
  def perform_one
289
288
  raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
290
289
  next_job = jobs.first
291
- Queues.delete_for(next_job["jid"], queue, self.to_s)
290
+ Queues.delete_for(next_job["jid"], queue, to_s)
292
291
  process_job(next_job)
293
292
  end
294
293
 
295
294
  def process_job(job)
296
295
  worker = new
297
- worker.jid = job['jid']
298
- worker.bid = job['bid'] if worker.respond_to?(:bid=)
299
- #Faktory::Testing.server_middleware.invoke(worker, job, job['queue']) do
300
- execute_job(worker, job['args'])
301
- #end
296
+ worker.jid = job["jid"]
297
+ worker.bid = job["bid"] if worker.respond_to?(:bid=)
298
+ # Faktory::Testing.server_middleware.invoke(worker, job, job['queue']) do
299
+ execute_job(worker, job["args"])
300
+ # end
302
301
  end
303
302
 
304
303
  def execute_job(worker, args)
@@ -328,5 +327,4 @@ module Faktory
328
327
  end
329
328
  end
330
329
  end
331
-
332
330
  end
@@ -1,6 +1,5 @@
1
1
  module Faktory
2
2
  module Trackable
3
-
4
3
  ##
5
4
  # Tracking allows a long-running Faktory job to report its progress:
6
5
  #
@@ -22,10 +21,10 @@ module Faktory
22
21
  # 4. reserve_until allows a job to dynamically extend its reservation so it is not garbage collected by Faktory while running
23
22
  # 5. you can only reserve up to 24 hours.
24
23
  #
25
- def track_progress(percent, desc=nil, reserve_until:nil)
26
- hash = { 'jid' => jid, 'percent' => percent.to_i, 'desc' => desc }
24
+ def track_progress(percent, desc = nil, reserve_until: nil)
25
+ hash = {"jid" => jid, "percent" => percent.to_i, "desc" => desc}
27
26
  hash["reserve_until"] = convert(reserve_until) if reserve_until
28
- Faktory.server {|c| c.set_track(hash) }
27
+ Faktory.server { |c| c.set_track(hash) }
29
28
  end
30
29
 
31
30
  private
data/lib/faktory/util.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'socket'
3
- require 'securerandom'
4
- require 'faktory/exception_handler'
2
+
3
+ require "socket"
4
+ require "securerandom"
5
+ require "faktory/exception_handler"
5
6
 
6
7
  module Faktory
7
8
  ##
@@ -15,13 +16,13 @@ module Faktory
15
16
  def watchdog(last_words)
16
17
  yield
17
18
  rescue Exception => ex
18
- handle_exception(ex, { context: last_words })
19
+ handle_exception(ex, {context: last_words})
19
20
  raise ex
20
21
  end
21
22
 
22
23
  def safe_thread(name, &block)
23
24
  Thread.new do
24
- Thread.current['faktory_label'.freeze] = name
25
+ Thread.current["faktory_label"] = name
25
26
  watchdog(name, &block)
26
27
  end
27
28
  end
@@ -35,7 +36,7 @@ module Faktory
35
36
  end
36
37
 
37
38
  def hostname
38
- ENV['DYNO'] || Socket.gethostname
39
+ ENV["DYNO"] || Socket.gethostname
39
40
  end
40
41
 
41
42
  def process_nonce
@@ -46,15 +47,13 @@ module Faktory
46
47
  @@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
47
48
  end
48
49
 
49
- def fire_event(event, reverse=false)
50
+ def fire_event(event, reverse = false)
50
51
  arr = Faktory.options[:lifecycle_events][event]
51
52
  arr.reverse! if reverse
52
53
  arr.each do |block|
53
- begin
54
- block.call
55
- rescue => ex
56
- handle_exception(ex, { context: "Exception during Faktory lifecycle event.", event: event })
57
- end
54
+ block.call
55
+ rescue => ex
56
+ handle_exception(ex, {context: "Exception during Faktory lifecycle event.", event: event})
58
57
  end
59
58
  arr.clear
60
59
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Faktory
3
- VERSION = "1.1.1"
4
+ VERSION = "1.2.0"
4
5
  end
data/lib/faktory.rb CHANGED
@@ -1,24 +1,24 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
- require 'faktory/version'
4
2
 
5
- require 'faktory/logging'
6
- require 'faktory/client'
7
- require 'faktory/middleware/chain'
8
- require 'faktory/job'
9
- require 'faktory/connection'
3
+ require "faktory/version"
10
4
 
11
- require 'json'
5
+ require "faktory/logging"
6
+ require "faktory/client"
7
+ require "faktory/middleware/chain"
8
+ require "faktory/job"
9
+ require "faktory/connection"
10
+
11
+ require "json"
12
12
 
13
13
  module Faktory
14
- NAME = 'Faktory'.freeze
15
- LICENSE = 'See LICENSE and the LGPL-3.0 for licensing details.'
14
+ NAME = "Faktory"
15
+ LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
16
16
 
17
17
  DEFAULTS = {
18
- queues: ['default'],
18
+ queues: ["default"],
19
19
  concurrency: 10,
20
- require: '.',
21
- environment: 'development',
20
+ require: ".",
21
+ environment: "development",
22
22
  # As of 2017, Heroku's process timeout is 30 seconds.
23
23
  # After 30 seconds, processes are KILLed so assume 25
24
24
  # seconds to gracefully shutdown and 5 seconds to hard
@@ -28,19 +28,20 @@ module Faktory
28
28
  lifecycle_events: {
29
29
  startup: [],
30
30
  quiet: [],
31
- shutdown: [],
31
+ shutdown: []
32
32
  },
33
- reloader: proc { |&block| block.call },
33
+ reloader: proc { |&block| block.call }
34
34
  }
35
35
 
36
36
  DEFAULT_JOB_OPTIONS = {
37
- 'retry' => 25,
38
- 'queue' => 'default'
37
+ "retry" => 25,
38
+ "queue" => "default"
39
39
  }
40
40
 
41
41
  def self.options
42
42
  @options ||= DEFAULTS.dup
43
43
  end
44
+
44
45
  def self.options=(opts)
45
46
  @options = opts
46
47
  end
@@ -101,8 +102,9 @@ module Faktory
101
102
 
102
103
  def self.default_job_options=(hash)
103
104
  # stringify
104
- @default_job_options = default_job_options.merge(Hash[hash.map{|k, v| [k.to_s, v]}])
105
+ @default_job_options = default_job_options.merge(hash.map { |k, v| [k.to_s, v] }.to_h)
105
106
  end
107
+
106
108
  def self.default_job_options
107
109
  defined?(@default_job_options) ? @default_job_options : DEFAULT_JOB_OPTIONS
108
110
  end
@@ -110,6 +112,7 @@ module Faktory
110
112
  def self.load_json(string)
111
113
  JSON.parse(string)
112
114
  end
115
+
113
116
  def self.dump_json(object)
114
117
  JSON.generate(object)
115
118
  end
@@ -117,6 +120,7 @@ module Faktory
117
120
  def self.logger
118
121
  Faktory::Logging.logger
119
122
  end
123
+
120
124
  def self.logger=(log)
121
125
  Faktory::Logging.logger = log
122
126
  end
@@ -138,7 +142,7 @@ module Faktory
138
142
  #
139
143
  # The default error handler logs errors to Faktory.logger.
140
144
  def self.error_handlers
141
- self.options[:error_handlers]
145
+ options[:error_handlers]
142
146
  end
143
147
 
144
148
  # Register a block to run at a point in the Faktory lifecycle.
@@ -164,5 +168,5 @@ module Faktory
164
168
  class Shutdown < Interrupt; end
165
169
  end
166
170
 
167
- require 'faktory/rails' if defined?(::Rails::Engine)
168
- require 'faktory/batch'
171
+ require "faktory/rails" if defined?(::Rails::Engine)
172
+ require "faktory/batch"
@@ -1 +1 @@
1
- require 'faktory'
1
+ require "faktory"
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: 1.1.1
4
+ version: 1.2.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: 2021-11-15 00:00:00.000000000 Z
11
+ date: 2023-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -96,6 +96,7 @@ extra_rdoc_files: []
96
96
  files:
97
97
  - ".github/workflows/ci.yml"
98
98
  - ".gitignore"
99
+ - ".standard.yml"
99
100
  - Changes.md
100
101
  - Gemfile
101
102
  - Gemfile.lock
@@ -153,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  - !ruby/object:Gem::Version
154
155
  version: '0'
155
156
  requirements: []
156
- rubygems_version: 3.1.4
157
+ rubygems_version: 3.4.4
157
158
  signing_key:
158
159
  specification_version: 4
159
160
  summary: Ruby worker for Faktory