sidekiq-status 0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .idea/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Evgeniy Tsvigun
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Sidekiq::Status
2
+
3
+ An extension to [Sidekiq](http://github.com/mperham/sidekiq) message processing to track your jobs. Inspired
4
+ by [resque-status](http://github.com/quirkey/resque-status) and mostly copying its features, using Sidekiq's middleware.
5
+
6
+ ## Installation
7
+
8
+ gem install sidekiq-status
9
+
10
+ ## Usage
11
+
12
+ Configure your middleware chains, lookup [Middleware usage](https://github.com/mperham/sidekiq/wiki/Middleware)
13
+ on Sidekiq wiki for more info.
14
+
15
+ ``` ruby
16
+ require 'sidekiq'
17
+ require 'sidekiq-status'
18
+
19
+ Sidekiq.configure_client do |config|
20
+ config.client_middleware do |chain|
21
+ chain.add Sidekiq::Status::ClientMiddleware
22
+ end
23
+ end
24
+
25
+ Sidekiq.configure_server do |config|
26
+ config.server_middleware do |chain|
27
+ chain.add Sidekiq::Status::ServerMiddleware
28
+ end
29
+ end
30
+ ```
31
+
32
+ When defining those jobs you want to track later, include one more module. Jobs defined without Sidekiq::Status::Worker
33
+ will be processed as usual.
34
+
35
+ ``` ruby
36
+ class MyJob
37
+ include Sidekiq::Worker
38
+ include Sidekiq::Status::Worker
39
+
40
+ def perform(*args)
41
+ # your code goes here
42
+ end
43
+ end
44
+ ```
45
+
46
+ Query for job status any time later:
47
+
48
+ ``` ruby
49
+ job_id = MyJob.perform_async(*args)
50
+ # "queued", "working", "complete" or "failed" , nil after expiry (30 minutes)
51
+ status = Sidekiq::Status::get(job_id)
52
+ ```
53
+
54
+ ### Features coming
55
+ * Progress tracking, messages from running jobs
56
+ * Stopping jobs by id
57
+ * Minimal web UI
58
+
59
+ ## License
60
+ MIT License , see LICENSE for more details.
61
+ © 2012 Evgeniy Tsvigun
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task :test => :spec
8
+
9
+ task :default => :spec
@@ -0,0 +1,16 @@
1
+ module Sidekiq::Status
2
+ # Should be in the client middleware chain
3
+ class ClientMiddleware
4
+ include Storage
5
+ # Uses the first argument as id and puts :queued status in the job's Redis hash
6
+ # @param [Class] worker_class if includes Sidekiq::Status::Worker, the job gets processed with the plugin
7
+ # @param [Array] msg job arguments, the firs one becomes the id
8
+ # @param [String] queue the queue's name
9
+ def call(worker_class, msg, queue)
10
+ if worker_class.include? Worker
11
+ store_for_id(msg['args'][0], :status => :queued)
12
+ end
13
+ yield
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ module Sidekiq::Status
2
+ # Should be in the server middleware chain
3
+ class ServerMiddleware
4
+ # Parameterized initialization, use it when adding middleware to server chain
5
+ # chain.add Sidekiq::Status::ServerMiddleware, :expiration => 60 * 5
6
+ # @param [Hash] opts middleware initialization options
7
+ # @option opts [Fixnum] :expiration ttl for complete jobs
8
+ def initialize(opts = {:expiration => 30 * 60})
9
+ @expiration = opts[:expiration]
10
+ end
11
+
12
+ # Takes out the first job argument to use as id
13
+ # puts :working status into Redis hash
14
+ # initializes worker instance with id
15
+ #
16
+ # Exception handler sets :failed status, re-inserts worker it to job args and re-throws the exception
17
+ # Worker::Stopped exception type are processed separately - :stopped status is set, no re-throwing
18
+ #
19
+ # @param [Worker] worker worker instance, processed here if its class includes Status::Worker
20
+ # @param [Array] msg job args, first of them used as job id, should have uuid format
21
+ # @param [String] queue queue name
22
+ def call(worker, msg, queue)
23
+ if worker.is_a? Worker
24
+ worker.id = msg['args'].shift
25
+ unless worker.id.is_a?(String) && UUID_REGEXP.match(worker.id)
26
+ raise ArgumentError, "First job argument for a #{worker.class.name} should have uuid format"
27
+ end
28
+ worker.store 'status' => 'working'
29
+ yield
30
+ worker.store 'status' => 'complete'
31
+ else
32
+ yield
33
+ end
34
+ rescue Worker::Stopped
35
+ worker.store 'status' => 'stopped'
36
+ rescue
37
+ if worker.is_a? Worker
38
+ worker.store 'status' => 'failed'
39
+ msg['args'].unshift worker.id
40
+ end
41
+ raise
42
+ ensure
43
+ Sidekiq.redis { |conn| conn.expire worker.id, @expiration } if worker.is_a? Worker
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ module Sidekiq::Status::Storage
2
+ RESERVED_FIELDS=%w(status stop update_time).freeze
3
+
4
+ protected
5
+
6
+ # Stores multiple values into a job's status hash,
7
+ # sets last update time
8
+ # @param [String] id job id
9
+ # @param [Hash] status_updates updated values
10
+ # @return [String] Redis operation status code
11
+ def store_for_id(id, status_updates)
12
+ Sidekiq.redis do |conn|
13
+ answers = conn.multi do
14
+ conn.hmset id, 'update_time', Time.now.to_i, *(status_updates.to_a.flatten)
15
+ conn.expire id, Sidekiq::Status::DEFAULT_EXPIRY
16
+ conn.publish "status_updates", id
17
+ end
18
+ answers[0]
19
+ end
20
+ end
21
+
22
+ # Gets a single valued from job status hash
23
+ # @param [String] id job id
24
+ # @param [String] Symbol field fetched field name
25
+ # @return [String] Redis operation status code
26
+ def read_field_for_id(uuid, field)
27
+ Sidekiq.redis do |conn|
28
+ conn.hmget(uuid, field)[0]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Sidekiq
2
+ module Status
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,47 @@
1
+ require "active_support"
2
+
3
+ module Sidekiq::Status::Worker
4
+ include Sidekiq::Status::Storage
5
+ extend ActiveSupport::Concern
6
+
7
+ # Adding ID generation to .perform_async
8
+ module ClassMethods
9
+ # :nodoc:
10
+ def self.extended(base)
11
+ class << base
12
+ alias_method_chain :perform_async, :uuid
13
+ end
14
+ end
15
+
16
+ # Add an id to job arguments
17
+ def perform_async_with_uuid(*args)
18
+ id = SecureRandom.uuid
19
+ args.unshift id
20
+ perform_async_without_uuid(*args)
21
+ id
22
+ end
23
+ end
24
+
25
+ class Stopped < StandardError
26
+ end
27
+
28
+ attr_reader :id
29
+
30
+ # Worker id initialization
31
+ # @param [String] id id generated on client-side
32
+ # @raise [RuntimeError] raised in case of second id initialization attempt
33
+ # @return [String] id
34
+ def id=(id)
35
+ raise RuntimeError("Worker ID is already set : #@id") if @id
36
+ @id=id
37
+ end
38
+
39
+ # Stores multiple values into a job's status hash,
40
+ # sets last update time
41
+ # @param [Hash] status_updates updated values
42
+ # @return [String] Redis operation status code
43
+ def store(hash)
44
+ store_for_id(@id, hash)
45
+ end
46
+
47
+ end
@@ -0,0 +1,20 @@
1
+ require "sidekiq-status/version"
2
+ require 'sidekiq-status/storage'
3
+ require 'sidekiq-status/worker'
4
+ require 'sidekiq-status/client_middleware'
5
+ require 'sidekiq-status/server_middleware'
6
+
7
+ module Sidekiq
8
+ module Status
9
+ extend Storage
10
+ DEFAULT_EXPIRY = 60 * 30
11
+ UUID_REGEXP = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
12
+
13
+ # Job status by id
14
+ # @param [String] id job id returned by async_perform
15
+ # @return [String] job status, possible values: "queued" , "working" , "complete"
16
+ def self.get(id)
17
+ read_field_for_id(id, :status)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/sidekiq-status/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Evgeniy Tsvigun']
6
+ gem.email = ['utgarda@gmail.com']
7
+ gem.summary = 'An extension to the sidekiq message processing to track your jobs'
8
+ gem.homepage = 'http://github.com/utgarda/sidekiq-status'
9
+ gem.license = 'MIT'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = 'sidekiq-status'
14
+ gem.require_paths = ['lib']
15
+ gem.version = Sidekiq::Status::VERSION
16
+
17
+ gem.add_dependency 'sidekiq', '~> 2.2'
18
+ gem.add_development_dependency 'rspec'
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Status::ClientMiddleware do
4
+
5
+ let!(:redis) { Sidekiq.redis { |conn| conn } }
6
+ let!(:job_id) { SecureRandom.uuid }
7
+
8
+ # Clean Redis before each test
9
+ before { redis.flushall }
10
+
11
+ describe "#call" do
12
+ it "sets queued status" do
13
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
14
+ StubJob.perform_async(:arg1 => 'val1').should == job_id
15
+ redis.hget(job_id, :status).should == 'queued'
16
+ end
17
+
18
+ it "sets status hash ttl" do
19
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
20
+ StubJob.perform_async(:arg1 => 'val1').should == job_id
21
+ (1..Sidekiq::Status::DEFAULT_EXPIRY).should cover redis.ttl(job_id)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Status::ServerMiddleware do
4
+
5
+ let!(:redis) { Sidekiq.redis { |conn| conn } }
6
+ let!(:job_id) { SecureRandom.uuid }
7
+
8
+ # Clean Redis before each test
9
+ # Seems like flushall has no effect on recently published messages,
10
+ # so we should wait till they expire
11
+ before { redis.flushall; sleep 0.1 }
12
+
13
+ describe "#call" do
14
+ it "sets working/complete status" do
15
+ thread = confirmations_thread 4, "status_updates", "job_messages_#{job_id}"
16
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
17
+ start_server do
18
+ ConfirmationJob.perform_async(:arg1 => 'val1').should == job_id
19
+ thread.value.should == [job_id, job_id,
20
+ "while in #perform, status = working",
21
+ job_id]
22
+ end
23
+ redis.hget(job_id, :status).should == 'complete'
24
+ end
25
+
26
+ it "sets failed status" do
27
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
28
+ start_server do
29
+ capture_status_updates(3) {
30
+ FailingJob.perform_async.should == job_id
31
+ }.should == [job_id]*3
32
+ end
33
+ redis.hget(job_id, :status).should == 'failed'
34
+ end
35
+
36
+ it "sets status hash ttl" do
37
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
38
+ StubJob.perform_async(:arg1 => 'val1').should == job_id
39
+ (1..Sidekiq::Status::DEFAULT_EXPIRY).should cover redis.ttl(job_id)
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Status::Worker do
4
+
5
+ let!(:job_id) { SecureRandom.uuid }
6
+
7
+ describe ".perform_async" do
8
+ it "generates and returns job id" do
9
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
10
+ StubJob.perform_async().should == job_id
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Status do
4
+
5
+ let!(:redis) { Sidekiq.redis { |conn| conn } }
6
+ let!(:job_id) { SecureRandom.uuid }
7
+ let!(:job_id_1) { SecureRandom.uuid }
8
+
9
+ # Clean Redis before each test
10
+ # Seems like flushall has no effect on recently published messages,
11
+ # so we should wait till they expire
12
+ before { redis.flushall; sleep 0.1 }
13
+
14
+ describe ".get" do
15
+ it "gets job status by id" do
16
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
17
+
18
+ start_server do
19
+ capture_status_updates(2) {
20
+ LongJob.perform_async(1).should == job_id
21
+ }.should == [job_id]*2
22
+ Sidekiq::Status.get(job_id).should == "working"
23
+ end
24
+ Sidekiq::Status.get(job_id).should == 'complete'
25
+ end
26
+ end
27
+
28
+ context "keeps normal Sidekiq functionality" do
29
+ it "does jobs with and without status processing" do
30
+ SecureRandom.should_receive(:uuid).twice.and_return(job_id, job_id_1)
31
+ start_server do
32
+ capture_status_updates(6) {
33
+ StubJob.perform_async.should == job_id
34
+ NoStatusConfirmationJob.perform_async(1)
35
+ StubJob.perform_async.should == job_id_1
36
+ NoStatusConfirmationJob.perform_async(2)
37
+ }.should =~ [job_id, job_id_1] * 3
38
+ end
39
+ redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2').should == %w(done)*2
40
+ Sidekiq::Status.get(job_id).should == 'complete'
41
+ Sidekiq::Status.get(job_id_1).should == 'complete'
42
+ end
43
+
44
+ it "retries failed jobs" do
45
+ SecureRandom.should_receive(:uuid).once.and_return(job_id)
46
+ start_server do
47
+ capture_status_updates(5) {
48
+ RetriedJob.perform_async().should == job_id
49
+ }.should == [job_id] * 5
50
+ end
51
+ Sidekiq::Status.get(job_id).should == 'complete'
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,66 @@
1
+ require "rspec"
2
+
3
+ require 'sidekiq'
4
+ require 'sidekiq-status'
5
+
6
+
7
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
8
+
9
+ Sidekiq.configure_client do |config|
10
+ config.client_middleware do |chain|
11
+ chain.add Sidekiq::Status::ClientMiddleware
12
+ end
13
+ end
14
+
15
+ def confirmations_thread(messages_limit, *channels)
16
+ parent = Thread.current
17
+ thread = Thread.new {
18
+ confirmations = []
19
+ Sidekiq.redis do |conn|
20
+ conn.subscribe *channels do |on|
21
+ on.subscribe do |ch, subscriptions|
22
+ if subscriptions == channels.size
23
+ sleep 0.1 while parent.status != "sleep"
24
+ parent.run
25
+ end
26
+ end
27
+ on.message do |ch, msg|
28
+ confirmations << msg
29
+ conn.unsubscribe if confirmations.length >= messages_limit
30
+ end
31
+ end
32
+ end
33
+ confirmations
34
+ }
35
+ Thread.stop
36
+ yield if block_given?
37
+ thread
38
+ end
39
+
40
+ def capture_status_updates(n, &block)
41
+ confirmations_thread(n, "status_updates", &block).value
42
+ end
43
+
44
+ def start_server()
45
+ pid = Process.fork do
46
+ $stdout.reopen File::NULL, 'w'
47
+ $stderr.reopen File::NULL, 'w'
48
+ require 'sidekiq/cli'
49
+ Sidekiq.options[:queues] << 'default'
50
+ Sidekiq.configure_server do |config|
51
+ config.redis = {:url => 'redis://localhost:6379'}
52
+ config.server_middleware do |chain|
53
+ chain.add Sidekiq::Status::ServerMiddleware
54
+ end
55
+ end
56
+ Sidekiq::CLI.instance.run
57
+ end
58
+
59
+ yield
60
+
61
+ sleep 0.1
62
+ Process.kill 'TERM', pid
63
+ Timeout::timeout(10) { Process.wait pid } rescue Timeout::Error
64
+ ensure
65
+ Process.kill 'KILL', pid rescue "OK" # it's OK if the process is gone already
66
+ end
@@ -0,0 +1,52 @@
1
+ class StubJob
2
+ include Sidekiq::Worker
3
+ include Sidekiq::Status::Worker
4
+
5
+ sidekiq_options 'retry' => 'false'
6
+
7
+ def perform(*args)
8
+ end
9
+ end
10
+
11
+ class LongJob < StubJob
12
+ def perform(*args)
13
+ sleep args[0] || 1
14
+ end
15
+ end
16
+
17
+ class ConfirmationJob < StubJob
18
+ def perform(*args)
19
+ Sidekiq.redis do |conn|
20
+ conn.publish "job_messages_#{id}", "while in #perform, status = #{conn.hget id, :status}"
21
+ end
22
+ end
23
+ end
24
+
25
+ class NoStatusConfirmationJob
26
+ include Sidekiq::Worker
27
+ def perform(id)
28
+ Sidekiq.redis do |conn|
29
+ conn.set "NoStatusConfirmationJob_#{id}", "done"
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ class FailingJob < StubJob
36
+ def perform
37
+ raise StandardError
38
+ end
39
+ end
40
+
41
+ class RetriedJob < StubJob
42
+ sidekiq_options 'retry' => 'true'
43
+ def perform()
44
+ Sidekiq.redis do |conn|
45
+ key = "RetriedJob_#{id}"
46
+ unless conn.exists key
47
+ conn.set key, 'tried'
48
+ raise StandardError
49
+ end
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-status
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Evgeniy Tsvigun
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sidekiq
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description:
47
+ email:
48
+ - utgarda@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - lib/sidekiq-status.rb
59
+ - lib/sidekiq-status/client_middleware.rb
60
+ - lib/sidekiq-status/server_middleware.rb
61
+ - lib/sidekiq-status/storage.rb
62
+ - lib/sidekiq-status/version.rb
63
+ - lib/sidekiq-status/worker.rb
64
+ - sidekiq-status.gemspec
65
+ - spec/lib/sidekiq-status/client_middleware_spec.rb
66
+ - spec/lib/sidekiq-status/server_middleware_spec.rb
67
+ - spec/lib/sidekiq-status/worker_spec.rb
68
+ - spec/lib/sidekiq-status_spec.rb
69
+ - spec/spec_helper.rb
70
+ - spec/support/test_jobs.rb
71
+ homepage: http://github.com/utgarda/sidekiq-status
72
+ licenses:
73
+ - MIT
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.23
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: An extension to the sidekiq message processing to track your jobs
96
+ test_files:
97
+ - spec/lib/sidekiq-status/client_middleware_spec.rb
98
+ - spec/lib/sidekiq-status/server_middleware_spec.rb
99
+ - spec/lib/sidekiq-status/worker_spec.rb
100
+ - spec/lib/sidekiq-status_spec.rb
101
+ - spec/spec_helper.rb
102
+ - spec/support/test_jobs.rb
103
+ has_rdoc: