resque_solo 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.
@@ -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