resque_solo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1439a366dbc27ba7ab5fda8a4f2d3cd8249b2f2b
4
+ data.tar.gz: a4fbc3cfd7eb599ebdaab59c99be4bbdc7413c7b
5
+ SHA512:
6
+ metadata.gz: df1a343cf086b25b8b35d1f8d4498f481977d2c3bf123cd7238ae942706d74c466b37e0fd435cac47eae9c5c71f48f9e4d57f3b5548fe55b18dd22df9e36ed6a
7
+ data.tar.gz: 34d522a75a2e5dee71423271280b114e1a9d1c104643e4e4b5b36e1415ae47bf07b015f39f25406f5ca6922d10de293b99fc8e8e9992bfcaf8d6775dafaeb5eb
@@ -0,0 +1,6 @@
1
+ .bundle
2
+ .config
3
+ Gemfile.lock
4
+ coverage
5
+ pkg
6
+ tmp
@@ -0,0 +1 @@
1
+ ruby-2.0.0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'test-unit'
5
+ gem 'mocha'
6
+ gem 'shoulda-context'
7
+ gem 'pry'
8
+ gem 'pry-byebug'
9
+ gem 'fakeredis'
10
+ end
11
+
12
+ gemspec
@@ -0,0 +1,34 @@
1
+ Copyright (c) 2010 Moviepilot GmbH http://moviepilot.com
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,
14
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
17
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+
21
+
22
+ Copyright (c) 2013 Tee Parham
23
+
24
+ MIT License
25
+
26
+ Permission is hereby granted, free of charge, to any person obtaining a copy
27
+ of this software and associated documentation files (the "Software"), to deal
28
+ in the Software without restriction, including without limitation the rights
29
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30
+ copies of the Software, and to permit persons to whom the Software is
31
+ furnished to do so, subject to the following conditions:
32
+
33
+ The above copyright notice and this permission notice shall be included in
34
+ all copies or substantial portions of the Software.
@@ -0,0 +1,45 @@
1
+ # ResqueSolo
2
+
3
+ ResqueSolo is a resque plugin to add unique jobs to resque.
4
+
5
+ It is a re-write of [resque-loner](https://github.com/jayniz/resque-loner).
6
+
7
+ It requires ruby 2.0 and resque 1.25.
8
+
9
+ It removes the dependency on `Resque::Helpers`, which is deprecated for resque 2.0.
10
+
11
+ ## Install
12
+
13
+ Add the gem to your Gemfile:
14
+
15
+ gem 'resque_solo'
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ class UpdateCat
21
+ include Resque::Plugins::UniqueJob
22
+ @queue = :cats
23
+
24
+ def self.perform(cat_id)
25
+ # do something
26
+ end
27
+ end
28
+ ```
29
+
30
+ If you attempt to queue a unique job multiple times, it is ignored:
31
+
32
+ ```
33
+ Resque.enqueue UpdateCat, 1
34
+ => "OK"
35
+ Resque.enqueue UpdateCat, 1
36
+ => "EXISTED"
37
+ Resque.enqueue UpdateCat, 1
38
+ => "EXISTED"
39
+ Resque.size :cats
40
+ => 1
41
+ Resque.enqueued? UpdateCat, 1
42
+ => true
43
+ Resque.enqueued_in? :dogs, UpdateCat, 1
44
+ => false
45
+ ```
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ desc 'Run tests'
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.pattern = 'test/**/*_test.rb'
9
+ t.verbose = false
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,40 @@
1
+ module Resque
2
+ class Job
3
+ class << self
4
+ # Mark an item as queued after Resque::Job.create has called Resque.push
5
+ def create_solo(queue, klass, *args)
6
+ return create_without_solo(queue, klass, *args) if Resque.inline?
7
+ item = {class: klass.to_s, args: args}
8
+ return "EXISTED" if ResqueSolo::Queue.queued?(queue, item)
9
+ # multi returns array of keys
10
+ create_return_value = false
11
+ Resque.redis.multi do
12
+ create_return_value = create_without_solo(queue, klass, *args)
13
+ ResqueSolo::Queue.mark_queued(queue, item)
14
+ end
15
+ create_return_value
16
+ end
17
+
18
+ # Mark an item as unqueued
19
+ def reserve_solo(queue)
20
+ item = reserve_without_solo(queue)
21
+ ResqueSolo::Queue.mark_unqueued(queue, item) if item && !Resque.inline?
22
+ item
23
+ end
24
+
25
+ # Mark all destroyed jobs as unqueued.
26
+ # The original method only returns the amount of jobs destroyed, but not the jobs themselves.
27
+ def destroy_solo(queue, klass, *args)
28
+ ResqueSolo::Queue.destroy(queue, klass, *args) unless Resque.inline?
29
+ destroy_without_solo(queue, klass, *args)
30
+ end
31
+
32
+ alias_method :create_without_solo, :create
33
+ alias_method :create, :create_solo
34
+ alias_method :reserve_without_solo, :reserve
35
+ alias_method :reserve, :reserve_solo
36
+ alias_method :destroy_without_solo, :destroy
37
+ alias_method :destroy, :destroy_solo
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ module Resque
2
+ class << self
3
+ def enqueued?(klass, *args)
4
+ enqueued_in?(queue_from_class(klass), klass, *args )
5
+ end
6
+
7
+ def enqueued_in?(queue, klass, *args)
8
+ item = {class: klass.to_s, args: args}
9
+ return nil unless ResqueSolo::Queue.is_unique?(item)
10
+ ResqueSolo::Queue.queued?(queue, item)
11
+ end
12
+
13
+ def remove_queue_with_cleanup(queue)
14
+ remove_queue_without_cleanup(queue)
15
+ ResqueSolo::Queue.cleanup(queue)
16
+ end
17
+
18
+ alias_method :remove_queue_without_cleanup, :remove_queue
19
+ alias_method :remove_queue, :remove_queue_with_cleanup
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ require 'resque'
2
+ require 'resque_ext/job'
3
+ require 'resque_ext/resque'
4
+ require 'resque_solo/version'
5
+ require 'resque_solo/queue'
6
+ require 'resque_solo/unique_job'
@@ -0,0 +1,82 @@
1
+ module ResqueSolo
2
+ class Queue
3
+ class << self
4
+ def queued?(queue, item)
5
+ return false unless is_unique?(item)
6
+ redis.get(unique_key(queue, item)) == "1"
7
+ end
8
+
9
+ def mark_queued(queue, item)
10
+ return unless is_unique?(item)
11
+ key = unique_key(queue, item)
12
+ redis.set(key, 1)
13
+ ttl = item_ttl(item)
14
+ redis.expire(key, ttl) if ttl >= 0
15
+ end
16
+
17
+ def mark_unqueued(queue, job)
18
+ item = job.is_a?(Resque::Job) ? job.payload : job
19
+ return unless is_unique?(item)
20
+ ttl = lock_after_execution_period(item)
21
+ if ttl == 0
22
+ redis.del(unique_key(queue, item))
23
+ else
24
+ redis.expire(unique_key(queue, item), ttl)
25
+ end
26
+ end
27
+
28
+ def unique_key(queue, item)
29
+ "solo:queue:#{queue}:job:#{const_for(item).redis_key(item)}"
30
+ end
31
+
32
+ def is_unique?(item)
33
+ const_for(item).included_modules.include?(::Resque::Plugins::UniqueJob)
34
+ rescue NameError
35
+ false
36
+ end
37
+
38
+ def item_ttl(item)
39
+ const_for(item).ttl
40
+ rescue NameError
41
+ -1
42
+ end
43
+
44
+ def lock_after_execution_period(item)
45
+ const_for(item).lock_after_execution_period
46
+ rescue NameError
47
+ 0
48
+ end
49
+
50
+ def destroy(queue, klass, *args)
51
+ klass = klass.to_s
52
+ redis_queue = "queue:#{queue}"
53
+
54
+ redis.lrange(redis_queue, 0, -1).each do |string|
55
+ json = JSON.parse(string)
56
+ next unless json['class'] == klass
57
+ next unless json['args'] == args if args.any?
58
+ ResqueSolo::Queue.mark_unqueued(queue, json)
59
+ end
60
+ end
61
+
62
+ def cleanup(queue)
63
+ keys = redis.keys("solo:queue:#{queue}:job:*")
64
+ redis.del(*keys) if keys.any?
65
+ end
66
+
67
+ private
68
+
69
+ def redis
70
+ Resque.redis
71
+ end
72
+
73
+ def item_class(item)
74
+ item[:class] || item['class']
75
+ end
76
+
77
+ def const_for(item)
78
+ const_get item_class(item)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,53 @@
1
+ require 'digest/md5'
2
+
3
+ module Resque
4
+ module Plugins
5
+ module UniqueJob
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Payload is what Resque stored for this job along with the job's class name:
12
+ # a hash containing :class and :args
13
+ def redis_key(payload)
14
+ payload = JSON.parse(JSON.generate(payload)) # todo - .stringify_keys
15
+ job = payload["class"]
16
+ args = payload["args"]
17
+ args.map! do |arg|
18
+ arg.is_a?(Hash) ? arg.sort : arg
19
+ end
20
+
21
+ Digest::MD5.hexdigest JSON.generate(class: job, args: args)
22
+ end
23
+
24
+ # The default ttl of a locking key is -1 (forever).
25
+ # To expire the lock after a certain amount of time, set a ttl (in seconds).
26
+ # For example:
27
+ #
28
+ # class FooJob
29
+ # include Resque::Plugins::UniqueJob
30
+ # @ttl = 40
31
+ # end
32
+ # end
33
+ def ttl
34
+ @ttl || -1
35
+ end
36
+
37
+ # The default ttl of a persisting key is 0, i.e. immediately deleted.
38
+ # Set lock_after_execution_period to block the execution
39
+ # of the job for a certain amount of time (in seconds).
40
+ # For example:
41
+ #
42
+ # class FooJob
43
+ # include Resque::Plugins::UniqueJob
44
+ # @lock_after_execution_period = 40
45
+ # end
46
+ # end
47
+ def lock_after_execution_period
48
+ @lock_after_execution_period || 0
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module ResqueSolo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'resque_solo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "resque_solo"
8
+ spec.version = ResqueSolo::VERSION
9
+ spec.authors = ["Tee Parham"]
10
+ spec.email = %w(tee@neighborland.com)
11
+ spec.description = %q{Resque plugin to add unique jobs}
12
+ spec.summary = %q{Resque plugin to add unique jobs}
13
+ spec.homepage = "https://github.com/teeparham/resque_solo"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = []
18
+ spec.test_files = spec.files.grep(%r{^(test|features)/})
19
+ spec.require_paths = %w(lib)
20
+
21
+ spec.required_ruby_version = ">= 2.0.0"
22
+
23
+ spec.add_dependency "resque", "~> 1.25.1"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ end
@@ -0,0 +1,38 @@
1
+ class FakeJob
2
+ @queue = :normal
3
+ end
4
+
5
+ class FakeUniqueJob
6
+ include Resque::Plugins::UniqueJob
7
+ @queue = :unique
8
+
9
+ def self.perform(_)
10
+ end
11
+ end
12
+
13
+ class FailingUniqueJob
14
+ include Resque::Plugins::UniqueJob
15
+ @queue = :unique
16
+
17
+ def self.perform(_)
18
+ raise "Fail"
19
+ end
20
+ end
21
+
22
+ class UniqueJobWithTtl
23
+ include Resque::Plugins::UniqueJob
24
+ @queue = :unique_with_ttl
25
+ @ttl = 300
26
+
27
+ def self.perform(*_)
28
+ end
29
+ end
30
+
31
+ class UniqueJobWithLock
32
+ include Resque::Plugins::UniqueJob
33
+ @queue = :unique_with_lock
34
+ @lock_after_execution_period = 150
35
+
36
+ def self.perform(*_)
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ require 'test_helper'
2
+
3
+ class JobTest < Test::Unit::TestCase
4
+ setup do
5
+ Resque.redis.flushall
6
+ end
7
+
8
+ should "enqueue identical jobs once" do
9
+ Resque.enqueue FakeUniqueJob, "x"
10
+ Resque.enqueue FakeUniqueJob, "x"
11
+ assert_equal 1, Resque.size(:unique)
12
+ end
13
+
14
+ should "allow the same jobs to be executed one after the other" do
15
+ Resque.enqueue FakeUniqueJob, "foo"
16
+ Resque.enqueue FakeUniqueJob, "foo"
17
+ assert_equal 1, Resque.size(:unique)
18
+ Resque.reserve(:unique)
19
+ assert_equal 0, Resque.size(:unique)
20
+ Resque.enqueue FakeUniqueJob, "foo"
21
+ Resque.enqueue FakeUniqueJob, "foo"
22
+ assert_equal 1, Resque.size(:unique)
23
+ end
24
+
25
+ should "consider equivalent hashes regardless of key order" do
26
+ Resque.enqueue FakeUniqueJob, bar: 1, foo: 2
27
+ Resque.enqueue FakeUniqueJob, foo: 2, bar: 1
28
+ assert_equal 1, Resque.size(:unique)
29
+ end
30
+
31
+ should "treat string and symbol keys equally" do
32
+ Resque.enqueue FakeUniqueJob, bar: 1, foo: 1
33
+ Resque.enqueue FakeUniqueJob, bar: 1, "foo" => 1
34
+ assert_equal 1, Resque.size(:unique)
35
+ end
36
+
37
+ should "mark jobs as unqueued, when Job.destroy is killing them" do
38
+ Resque.enqueue FakeUniqueJob, "foo"
39
+ Resque.enqueue FakeUniqueJob, "foo"
40
+ assert_equal 1, Resque.size(:unique)
41
+ Resque::Job.destroy(:unique, FakeUniqueJob)
42
+ assert_equal 0, Resque.size(:unique)
43
+ Resque.enqueue FakeUniqueJob, "foo"
44
+ Resque.enqueue FakeUniqueJob, "foo"
45
+ assert_equal 1, Resque.size(:unique)
46
+ end
47
+
48
+ should "mark jobs as unqueued when they raise an exception" do
49
+ 2.times { Resque.enqueue( FailingUniqueJob, "foo" ) }
50
+ assert_equal 1, Resque.size(:unique)
51
+ worker = Resque::Worker.new(:unique)
52
+ worker.work 0
53
+ assert_equal 0, Resque.size(:unique)
54
+ 2.times { Resque.enqueue( FailingUniqueJob, "foo" ) }
55
+ assert_equal 1, Resque.size(:unique)
56
+ end
57
+
58
+ should "report if a unique job is enqueued" do
59
+ Resque.enqueue FakeUniqueJob, "foo"
60
+ assert Resque.enqueued?(FakeUniqueJob, "foo")
61
+ refute Resque.enqueued?(FakeUniqueJob, "bar")
62
+ end
63
+
64
+ should "report if a job is enqueued" do
65
+ default_queue = FakeUniqueJob.instance_variable_get(:@queue)
66
+ FakeUniqueJob.instance_variable_set(:@queue, :other)
67
+ Resque.enqueue FakeUniqueJob, "foo"
68
+ assert Resque.enqueued_in?(:other, FakeUniqueJob, "foo")
69
+ FakeUniqueJob.instance_variable_set(:@queue, default_queue)
70
+ refute Resque.enqueued?(FakeUniqueJob, "foo")
71
+ end
72
+
73
+ should "cleanup when a queue is destroyed" do
74
+ Resque.enqueue FakeUniqueJob, "foo"
75
+ Resque.enqueue FailingUniqueJob, "foo"
76
+ Resque.remove_queue(:unique)
77
+ Resque.enqueue(FakeUniqueJob, "foo")
78
+ assert_equal 1, Resque.size(:unique)
79
+ end
80
+
81
+ should "honor ttl in the redis key" do
82
+ Resque.enqueue UniqueJobWithTtl
83
+ assert Resque.enqueued?(UniqueJobWithTtl)
84
+ keys = Resque.redis.keys "solo:queue:unique_with_ttl:job:*"
85
+ assert_equal 1, keys.length
86
+ assert_in_delta UniqueJobWithTtl.ttl, Resque.redis.ttl(keys.first), 2
87
+ end
88
+
89
+ should "not allow the same job to be enqueued after execution if lock_after_execution_period is set" do
90
+ Resque.enqueue UniqueJobWithLock, "foo"
91
+ Resque.enqueue UniqueJobWithLock, "foo"
92
+ assert_equal 1, Resque.size(:unique_with_lock)
93
+ Resque.reserve(:unique_with_lock)
94
+ assert_equal 0, Resque.size(:unique_with_lock)
95
+ Resque.enqueue UniqueJobWithLock, "foo"
96
+ assert_equal 0, Resque.size(:unique_with_lock)
97
+ end
98
+
99
+ should "honor lock_after_execution_period in the redis key" do
100
+ Resque.enqueue UniqueJobWithLock
101
+ Resque.reserve(:unique_with_lock)
102
+ keys = Resque.redis.keys "solo:queue:unique_with_lock:job:*"
103
+ assert_equal 1, keys.length
104
+ assert_in_delta UniqueJobWithLock.lock_after_execution_period, Resque.redis.ttl(keys.first), 2
105
+ end
106
+
107
+ end
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ class QueueTest < Test::Unit::TestCase
4
+
5
+ end
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ class ResqueTest < Test::Unit::TestCase
4
+ setup do
5
+ Resque.redis.flushall
6
+ end
7
+
8
+ should "enqueue normal jobs" do
9
+ Resque.enqueue FakeJob, "x"
10
+ Resque.enqueue FakeJob, "x"
11
+ assert_equal 2, Resque.size(:normal)
12
+ end
13
+
14
+ should "not be able to report if a non-unique job was enqueued" do
15
+ assert_nil Resque.enqueued?(FakeJob)
16
+ end
17
+
18
+ should "not raise when deleting an empty queue" do
19
+ assert_nothing_raised do
20
+ Resque.remove_queue(:unique)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+ require 'shoulda-context'
3
+ require 'mocha/setup'
4
+ require 'resque_solo'
5
+ require 'pry'
6
+ require 'pry-byebug'
7
+ require 'fake_jobs'
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ class UniqueJobTest < Test::Unit::TestCase
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque_solo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tee Parham
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: resque
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.25.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.25.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Resque plugin to add unique jobs
56
+ email:
57
+ - tee@neighborland.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .ruby-version
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/resque_ext/job.rb
69
+ - lib/resque_ext/resque.rb
70
+ - lib/resque_solo.rb
71
+ - lib/resque_solo/queue.rb
72
+ - lib/resque_solo/unique_job.rb
73
+ - lib/resque_solo/version.rb
74
+ - resque_solo.gemspec
75
+ - test/fake_jobs.rb
76
+ - test/job_test.rb
77
+ - test/queue_test.rb
78
+ - test/resque_test.rb
79
+ - test/test_helper.rb
80
+ - test/unique_job_test.rb
81
+ homepage: https://github.com/teeparham/resque_solo
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 2.0.0
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.1.11
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Resque plugin to add unique jobs
105
+ test_files:
106
+ - test/fake_jobs.rb
107
+ - test/job_test.rb
108
+ - test/queue_test.rb
109
+ - test/resque_test.rb
110
+ - test/test_helper.rb
111
+ - test/unique_job_test.rb