resque-job-tracking 0.0.1
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 +18 -0
- data/Gemfile +10 -0
- data/LICENSE +19 -0
- data/README +15 -0
- data/Rakefile +2 -0
- data/lib/resque/plugins/job_tracking/meta_ext.rb +23 -0
- data/lib/resque/plugins/job_tracking/version.rb +7 -0
- data/lib/resque/plugins/job_tracking.rb +78 -0
- data/resque-job-tracking.gemspec +22 -0
- data/spec/basic_spec.rb +48 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/worker_support.rb +50 -0
- data/spec/tracking_spec.rb +156 -0
- metadata +126 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Engine Yard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
A resque plugin for tracking jobs and their state (pending, running, failed) based on some originating entity.
|
2
|
+
This plugin relies heavily on resque-meta to store meta data on resque jobs.
|
3
|
+
|
4
|
+
Our use case is:
|
5
|
+
|
6
|
+
Account is a model in our database. Accounts do lots of things which trigger background jobs. We want to be able to see for a given account:
|
7
|
+
1. What jobs are waiting to be run.
|
8
|
+
2. What jobs are currently running.
|
9
|
+
3. What jobs ran and had some exception in the past 24 hours.
|
10
|
+
|
11
|
+
You define how jobs are tracked by defining the "track" method on your job class (see the tests for an example)
|
12
|
+
|
13
|
+
You define how long meta data is kept around by defining expire_meta_in, expire_normal_meta_in, and/or expire_failures_meta_in on your job class (see tests for an example)
|
14
|
+
|
15
|
+
You can access the list of pending, running, and failed jobs using the Resque::Plugins::JobTracking methods: pending_jobs, running_jobs, failed_jobs (again, see tests)
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#TODO: send a PULL request with this change!!!
|
2
|
+
Resque::Plugins::Meta::Metadata.class_eval do
|
3
|
+
|
4
|
+
def initialize(data_hash)
|
5
|
+
data_hash['enqueued_at'] ||= to_time_format_str(Time.now)
|
6
|
+
@data = data_hash
|
7
|
+
@meta_id = data_hash['meta_id'].dup
|
8
|
+
@enqueued_at = from_time_format_str('enqueued_at')
|
9
|
+
@job_class = data_hash['job_class']
|
10
|
+
if @job_class.is_a?(String)
|
11
|
+
@job_class = Resque.constantize(data_hash['job_class'])
|
12
|
+
else
|
13
|
+
data_hash['job_class'] = @job_class.to_s
|
14
|
+
end
|
15
|
+
@expire_in = data_hash["expire_in"] || @job_class.expire_meta_in || 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def expire_in=(val)
|
19
|
+
@expire_in = val
|
20
|
+
data["expire_in"] = val
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'resque/plugins/meta'
|
2
|
+
require 'resque/plugins/job_tracking/meta_ext'
|
3
|
+
|
4
|
+
module Resque
|
5
|
+
module Plugins
|
6
|
+
module JobTracking
|
7
|
+
def self.extended(mod)
|
8
|
+
mod.extend(Resque::Plugins::Meta)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.pending_jobs(identifier)
|
12
|
+
Resque.redis.smembers("#{identifier}:pending") || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.running_jobs(identifier)
|
16
|
+
Resque.redis.smembers("#{identifier}:running") || []
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.failed_jobs(identifier)
|
20
|
+
Resque.redis.smembers("#{identifier}:failed") || []
|
21
|
+
end
|
22
|
+
|
23
|
+
def expire_normal_meta_in
|
24
|
+
expire_meta_in
|
25
|
+
end
|
26
|
+
|
27
|
+
def expire_failures_meta_in
|
28
|
+
expire_meta_in + (24 * 60 * 60)
|
29
|
+
end
|
30
|
+
|
31
|
+
def before_enqueue_job_tracking(meta_id, *jobargs)
|
32
|
+
if self.respond_to?(:track)
|
33
|
+
identifiers = track(*jobargs)
|
34
|
+
identifiers.each do |ident|
|
35
|
+
Resque.redis.sadd("#{ident}:pending", meta_id)
|
36
|
+
end
|
37
|
+
meta = get_meta(meta_id)
|
38
|
+
meta["job_args"] = jobargs
|
39
|
+
meta.save
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def around_perform_job_tracking(meta_id, *jobargs)
|
44
|
+
if self.respond_to?(:track)
|
45
|
+
identifiers = track(*jobargs)
|
46
|
+
identifiers.each do |ident|
|
47
|
+
Resque.redis.srem("#{ident}:pending", meta_id)
|
48
|
+
Resque.redis.sadd("#{ident}:running", meta_id)
|
49
|
+
end
|
50
|
+
begin
|
51
|
+
to_return = yield
|
52
|
+
meta = get_meta(meta_id)
|
53
|
+
meta.expire_in = expire_normal_meta_in
|
54
|
+
meta.save
|
55
|
+
to_return
|
56
|
+
rescue => e
|
57
|
+
identifiers.each do |ident|
|
58
|
+
Resque.redis.sadd("#{ident}:failed", meta_id)
|
59
|
+
end
|
60
|
+
meta = get_meta(meta_id)
|
61
|
+
meta.expire_in = expire_failures_meta_in
|
62
|
+
meta['exception_message'] = e.message
|
63
|
+
meta['exception_backtrace'] = e.backtrace
|
64
|
+
meta.save
|
65
|
+
raise e
|
66
|
+
ensure
|
67
|
+
identifiers.each do |ident|
|
68
|
+
Resque.redis.srem("#{ident}:running", meta_id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
yield
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/resque/plugins/job_tracking/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jacob Burkhart"]
|
6
|
+
gem.email = ["jacob@engineyard.com"]
|
7
|
+
gem.description = %q{A resque plugin for tracking jobs and their state (pending, running, failed) based on some originating entity}
|
8
|
+
gem.summary = %q{A resque plugin for tracking jobs and their state (pending, running, failed) based on some originating entity}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "resque-job-tracking"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Resque::Plugins::JobTracking::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'resque', '>= 1.8.0'
|
19
|
+
gem.add_dependency 'resque-meta', '>= 1.0.0'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rspec'
|
22
|
+
end
|
data/spec/basic_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'resque'
|
2
|
+
require 'resque/plugins/job_tracking'
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
class WhatHappened
|
7
|
+
require 'tempfile'
|
8
|
+
def self.reset!
|
9
|
+
@what_happened = Tempfile.new("what_happened")
|
10
|
+
end
|
11
|
+
def self.what_happened
|
12
|
+
File.read(@what_happened.path)
|
13
|
+
end
|
14
|
+
def self.record(*event)
|
15
|
+
@what_happened.write(event.to_s)
|
16
|
+
@what_happened.flush
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BasicJob
|
21
|
+
extend Resque::Plugins::JobTracking
|
22
|
+
@queue = :test
|
23
|
+
|
24
|
+
def self.perform(*args)
|
25
|
+
begin
|
26
|
+
WhatHappened.record(self, args)
|
27
|
+
rescue => e
|
28
|
+
puts e.inspect
|
29
|
+
puts e.backtrace.join("\n")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
describe "the basics" do
|
36
|
+
before do
|
37
|
+
WhatHappened.reset!
|
38
|
+
Resque.redis.flushall
|
39
|
+
end
|
40
|
+
|
41
|
+
it "works" do
|
42
|
+
meta = BasicJob.enqueue('foo', 'bar')
|
43
|
+
worker = Resque::Worker.new(:test)
|
44
|
+
worker.work(0)
|
45
|
+
meta = BasicJob.get_meta(meta.meta_id)
|
46
|
+
WhatHappened.what_happened.should == "BasicJob#{meta.meta_id}foobar"
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.expand_path('../support/*.rb', __FILE__)].each{|f| require f}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module WorkerSupport
|
2
|
+
|
3
|
+
def work(worker_count = 5)
|
4
|
+
@workers = []
|
5
|
+
worker_count.times do
|
6
|
+
@workers << Process.fork do
|
7
|
+
begin
|
8
|
+
Resque.redis.client.reconnect
|
9
|
+
Resque::Worker.new(:test).work(1)
|
10
|
+
rescue => e
|
11
|
+
puts e.inspect
|
12
|
+
puts e.backtrace.join("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def finished?
|
19
|
+
Resque.redis.keys("meta*").each do |key|
|
20
|
+
meta = Resque::Plugins::Meta.get_meta(key.split(":").last)
|
21
|
+
if meta.finished?
|
22
|
+
# puts "finished #{meta['job_class']}"
|
23
|
+
else
|
24
|
+
return false
|
25
|
+
# puts "still running #{meta['job_class']}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_until_finished
|
32
|
+
while(!finished?)
|
33
|
+
sleep(0.5)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def work_until_finished
|
38
|
+
work
|
39
|
+
wait_until_finished
|
40
|
+
end
|
41
|
+
|
42
|
+
def cleanup
|
43
|
+
if @workers
|
44
|
+
@workers.each do |p|
|
45
|
+
Process.kill(9, p)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'resque'
|
2
|
+
require 'resque/plugins/job_tracking'
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
class WhatHappened
|
7
|
+
require 'tempfile'
|
8
|
+
def self.reset!
|
9
|
+
@what_happened = Tempfile.new("what_happened")
|
10
|
+
end
|
11
|
+
def self.what_happened
|
12
|
+
File.read(@what_happened.path)
|
13
|
+
end
|
14
|
+
def self.record(*event)
|
15
|
+
@what_happened.write(event.to_s)
|
16
|
+
@what_happened.flush
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BaseJobWithPerform
|
21
|
+
extend Resque::Plugins::JobTracking
|
22
|
+
def self.queue
|
23
|
+
:test
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.expire_meta_in
|
27
|
+
1
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.perform(meta_id, *args)
|
31
|
+
self.new.perform(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'cubbyhole/base'
|
37
|
+
class Account < Cubbyhole::Base
|
38
|
+
|
39
|
+
def pending_jobs
|
40
|
+
Resque::Plugins::JobTracking.pending_jobs(job_tracking_identifier)
|
41
|
+
end
|
42
|
+
|
43
|
+
def running_jobs
|
44
|
+
Resque::Plugins::JobTracking.running_jobs(job_tracking_identifier)
|
45
|
+
end
|
46
|
+
|
47
|
+
def failed_jobs
|
48
|
+
Resque::Plugins::JobTracking.failed_jobs(job_tracking_identifier)
|
49
|
+
end
|
50
|
+
|
51
|
+
def job_tracking_identifier
|
52
|
+
"account#{self.id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
class TypicalProblemJob < BaseJobWithPerform
|
59
|
+
|
60
|
+
def self.track(account_id, something)
|
61
|
+
[Account.get(account_id).job_tracking_identifier]
|
62
|
+
end
|
63
|
+
|
64
|
+
def perform(account_id, something)
|
65
|
+
if something == 'fail_immediate'
|
66
|
+
raise "failing immediate"
|
67
|
+
end
|
68
|
+
sleep(2)
|
69
|
+
if something == 'fail_please'
|
70
|
+
raise "i fail now"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
describe TypicalProblemJob do
|
78
|
+
include WorkerSupport
|
79
|
+
|
80
|
+
before do
|
81
|
+
WhatHappened.reset!
|
82
|
+
Resque.redis.flushall
|
83
|
+
end
|
84
|
+
after do
|
85
|
+
cleanup
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should keep meta data for failed jobs" do
|
89
|
+
account = Account.create
|
90
|
+
TypicalProblemJob.enqueue(account.id, 'fail_please')
|
91
|
+
account.pending_jobs.size.should eq 1
|
92
|
+
account.running_jobs.size.should eq 0
|
93
|
+
account.failed_jobs.size.should eq 0
|
94
|
+
meta_id = account.pending_jobs.first
|
95
|
+
TypicalProblemJob.get_meta(meta_id).should_not be_nil
|
96
|
+
work(1)
|
97
|
+
sleep(1)
|
98
|
+
account.pending_jobs.size.should eq 0
|
99
|
+
account.running_jobs.size.should eq 1
|
100
|
+
account.failed_jobs.size.should eq 0
|
101
|
+
account.running_jobs.first.should eq meta_id
|
102
|
+
TypicalProblemJob.get_meta(meta_id).should_not be_nil
|
103
|
+
wait_until_finished
|
104
|
+
account.pending_jobs.size.should eq 0
|
105
|
+
account.running_jobs.size.should eq 0
|
106
|
+
account.failed_jobs.size.should eq 1
|
107
|
+
account.failed_jobs.first.should eq meta_id
|
108
|
+
meta = TypicalProblemJob.get_meta(meta_id)
|
109
|
+
meta.should_not be_nil
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should lose meta data for non-failing jobs" do
|
113
|
+
account = Account.create
|
114
|
+
TypicalProblemJob.enqueue(account.id, 'pass_please')
|
115
|
+
account.pending_jobs.size.should eq 1
|
116
|
+
account.running_jobs.size.should eq 0
|
117
|
+
account.failed_jobs.size.should eq 0
|
118
|
+
meta_id = account.pending_jobs.first
|
119
|
+
TypicalProblemJob.get_meta(meta_id).should_not be_nil
|
120
|
+
work(1)
|
121
|
+
sleep(1)
|
122
|
+
account.pending_jobs.size.should eq 0
|
123
|
+
account.running_jobs.size.should eq 1
|
124
|
+
account.failed_jobs.size.should eq 0
|
125
|
+
account.running_jobs.first.should eq meta_id
|
126
|
+
TypicalProblemJob.get_meta(meta_id).should_not be_nil
|
127
|
+
wait_until_finished
|
128
|
+
account.pending_jobs.size.should eq 0
|
129
|
+
account.running_jobs.size.should eq 0
|
130
|
+
account.failed_jobs.size.should eq 0
|
131
|
+
sleep(2)
|
132
|
+
TypicalProblemJob.get_meta(meta_id).should be_nil
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should store the exception in meta data" do
|
136
|
+
account = Account.create
|
137
|
+
TypicalProblemJob.enqueue(account.id, 'fail_immediate')
|
138
|
+
work_until_finished
|
139
|
+
account.failed_jobs.size.should eq 1
|
140
|
+
meta_id = account.failed_jobs.first
|
141
|
+
meta_data = TypicalProblemJob.get_meta(meta_id)
|
142
|
+
meta_data['exception_message'].should eq "failing immediate"
|
143
|
+
meta_data['exception_backtrace'].should_not be_nil
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should store the job class and args in meta data" do
|
147
|
+
account = Account.create
|
148
|
+
TypicalProblemJob.enqueue(account.id, 'dontcare')
|
149
|
+
account.pending_jobs.size.should eq 1
|
150
|
+
meta_id = account.pending_jobs.first
|
151
|
+
meta_data = TypicalProblemJob.get_meta(meta_id)
|
152
|
+
meta_data['job_class'].should eq "TypicalProblemJob"
|
153
|
+
meta_data['job_args'].should eq [account.id, 'dontcare']
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-job-tracking
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jacob Burkhart
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-10-13 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: resque
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 55
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 8
|
32
|
+
- 0
|
33
|
+
version: 1.8.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: resque-meta
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 23
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 0
|
48
|
+
- 0
|
49
|
+
version: 1.0.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
description: A resque plugin for tracking jobs and their state (pending, running, failed) based on some originating entity
|
67
|
+
email:
|
68
|
+
- jacob@engineyard.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files: []
|
74
|
+
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE
|
79
|
+
- README
|
80
|
+
- Rakefile
|
81
|
+
- lib/resque/plugins/job_tracking.rb
|
82
|
+
- lib/resque/plugins/job_tracking/meta_ext.rb
|
83
|
+
- lib/resque/plugins/job_tracking/version.rb
|
84
|
+
- resque-job-tracking.gemspec
|
85
|
+
- spec/basic_spec.rb
|
86
|
+
- spec/spec_helper.rb
|
87
|
+
- spec/support/worker_support.rb
|
88
|
+
- spec/tracking_spec.rb
|
89
|
+
homepage: ""
|
90
|
+
licenses: []
|
91
|
+
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
requirements: []
|
116
|
+
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 1.8.10
|
119
|
+
signing_key:
|
120
|
+
specification_version: 3
|
121
|
+
summary: A resque plugin for tracking jobs and their state (pending, running, failed) based on some originating entity
|
122
|
+
test_files:
|
123
|
+
- spec/basic_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
- spec/support/worker_support.rb
|
126
|
+
- spec/tracking_spec.rb
|