resque-serializer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/resque-serializer/mutex.rb +46 -0
- data/lib/resque-serializer/serializers/both.rb +62 -0
- data/lib/resque-serializer/serializers/combined.rb +43 -0
- data/lib/resque-serializer/serializers/job.rb +43 -0
- data/lib/resque-serializer/serializers/queue.rb +41 -0
- data/lib/resque-serializer/version.rb +7 -0
- data/lib/resque-serializer.rb +32 -0
- data/resque-serializer.gemspec +30 -0
- data/spec/lib/resque-serializer/mutex_spec.rb +99 -0
- data/spec/lib/resque-serializer/serializers/both_spec.rb +127 -0
- data/spec/lib/resque-serializer/serializers/combined_spec.rb +101 -0
- data/spec/lib/resque-serializer/serializers/job_spec.rb +75 -0
- data/spec/lib/resque-serializer/serializers/queue_spec.rb +82 -0
- data/spec/spec_helper.rb +17 -0
- metadata +202 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea193acc18e7717c6a3f990c82ef5d31d9682219
|
4
|
+
data.tar.gz: 40d82f79180b84382efdddf99a7f4fa998127487
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 21271809497bc8c8ece3344fc7f9e351b9b9ea18653f2565e0518cee8ee349df96d9dd69fb728c34f3dc3be682da9527bb0685b1d56f493477884cac3f1516bc
|
7
|
+
data.tar.gz: cd46348ba7b8481816cae2fabd904c8fe06925fc8ccbf2462650aedf49e9819841d6244960e3c8d2433f4c246bff14e42dc67e10995ceb851b760176e07a8b8c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Ryan Ringler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Resque::Serializer
|
2
|
+
|
3
|
+
A Resque plugin which ensures for a given queue, that only one worker is executing a job at any given time.
|
4
|
+
|
5
|
+
It is slightly more flexible than [Resque::LonelyJob](https://github.com/wallace/resque-lonely_job).
|
6
|
+
|
7
|
+
This gem may be helpful to avoid database lock contention.
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'resque-serializer'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install resque-serializer
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### TLDR
|
30
|
+
```
|
31
|
+
| Lock Lifetime | :queue | :job | :both | :combined |
|
32
|
+
| -------------: | :----: | :---: | :----: | :-------: |
|
33
|
+
| before_enqueue | ✓ | | ✓ | ✓ |
|
34
|
+
| enqueue | | | | | | | |
|
35
|
+
| after_enqueue | | | | | | | |
|
36
|
+
| before_dequeue | | | ✓ | | ✓ | | |
|
37
|
+
| dequeue | | | | | | | | | |
|
38
|
+
| after_dequeue | ✗ | | | ✗ | | | |
|
39
|
+
| before_perform | | | | | | | |
|
40
|
+
| perform | | | | | | | |
|
41
|
+
| after_perform | | ✗ | ✗ | ✗ |
|
42
|
+
```
|
43
|
+
|
44
|
+
### Example #1 -- Serializing the queue
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'resque-serializer'
|
48
|
+
|
49
|
+
class SerializedJob
|
50
|
+
extend Resque::Plugins::Serializer
|
51
|
+
|
52
|
+
@queue = :default
|
53
|
+
|
54
|
+
serialize :queue
|
55
|
+
|
56
|
+
def self.perform
|
57
|
+
# work
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
Only one of job with identical arguments will be allowed to be queued at a time. As soon as the job is dequeued to begin executing, an identical job may be queued (and may begin executing.)
|
63
|
+
|
64
|
+
|
65
|
+
### Example #2 -- Serializing the job
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'resque-serializer'
|
69
|
+
|
70
|
+
class SerializedJob
|
71
|
+
extend Resque::Plugins::Serializer
|
72
|
+
|
73
|
+
@queue = :default
|
74
|
+
|
75
|
+
serialize :job
|
76
|
+
|
77
|
+
def self.perform
|
78
|
+
# work
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
Any number of these jobs may be queued, but only one job with identical arguments will be executed at a time. As soon as the executing job is completed, another queued job may be dequeued to execute.
|
84
|
+
|
85
|
+
|
86
|
+
### Example #3 -- Serializing both the queue & job (independently)
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
require 'resque-serializer'
|
90
|
+
|
91
|
+
class SerializedJob
|
92
|
+
extend Resque::Plugins::Serializer
|
93
|
+
|
94
|
+
@queue = :default
|
95
|
+
|
96
|
+
serialize :both
|
97
|
+
|
98
|
+
def self.perform
|
99
|
+
# work
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
A combination of the first two examples; both the queue and the execution of identical jobs is serialized (independently.) An additional job may be queued if no identical job exists in the queue. Similarly, an additional job may begin executing if no identical job is currently executing.
|
105
|
+
|
106
|
+
|
107
|
+
### Example #4 -- Serializing both the queue & job (combined)
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
require 'resque-serializer'
|
111
|
+
|
112
|
+
class SerializedJob
|
113
|
+
extend Resque::Plugins::Serializer
|
114
|
+
|
115
|
+
@queue = :default
|
116
|
+
|
117
|
+
serialize :combined
|
118
|
+
|
119
|
+
def self.perform
|
120
|
+
# work
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
Also a combination of the first two examples; both the queue and execution of identical jobs is serialized (together.) An additional job may not be queued if an identical job exists in the queue or execution.
|
126
|
+
|
127
|
+
|
128
|
+
## Contributing
|
129
|
+
|
130
|
+
1. Fork it
|
131
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
132
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
133
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
134
|
+
5. Create new Pull Request
|
135
|
+
|
136
|
+
|
137
|
+
## License
|
138
|
+
|
139
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
140
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'resque/serializer'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module Serializer
|
6
|
+
class Mutex
|
7
|
+
class LockFailed < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :key, :ttl
|
10
|
+
|
11
|
+
delegate :redis,
|
12
|
+
to: Resque
|
13
|
+
|
14
|
+
def initialize(key, ttl: 5.minutes)
|
15
|
+
@key = key
|
16
|
+
@ttl = ttl.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
def lock
|
20
|
+
!!redis.set(key, true, set_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def lock!
|
24
|
+
!!redis.set(key, true, set_options) || fail(LockFailed)
|
25
|
+
end
|
26
|
+
|
27
|
+
def locked?
|
28
|
+
!!redis.get(key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def unlock
|
32
|
+
!!redis.del(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def set_options
|
38
|
+
{
|
39
|
+
nx: true,
|
40
|
+
px: ttl * 1000 # msecs
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module Serializer
|
4
|
+
module Serializers
|
5
|
+
module Both
|
6
|
+
|
7
|
+
# before_enqueue: ✓
|
8
|
+
# enqueue: |
|
9
|
+
# after_enqueue: |
|
10
|
+
# before_dequeue: | ✓
|
11
|
+
# dequeue: | |
|
12
|
+
# after_dequeue: ✗ |
|
13
|
+
# before_perform: |
|
14
|
+
# perform: |
|
15
|
+
# after_perform: ✗
|
16
|
+
|
17
|
+
def before_enqueue_set_queue_lock(*args)
|
18
|
+
queue_mutex(args).lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def before_dequeue_set_job_lock(*args)
|
22
|
+
job_mutex(args).lock
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_dequeue_clear_queue_lock(*args)
|
26
|
+
queue_mutex(args).unlock
|
27
|
+
end
|
28
|
+
|
29
|
+
def around_perform_clear_job_lock(*args)
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
job_mutex(args).unlock
|
33
|
+
end
|
34
|
+
|
35
|
+
def queue_mutex(args)
|
36
|
+
Serializer::Mutex.new(queue_key(args))
|
37
|
+
end
|
38
|
+
|
39
|
+
def job_mutex(args)
|
40
|
+
Serializer::Mutex.new(job_key(args))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def queue_key(args)
|
46
|
+
klass = self.name.tableize.singularize
|
47
|
+
args = args.map(&:to_s).join(',')
|
48
|
+
|
49
|
+
"resque-serializer:queue:#{klass}:#{args}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def job_key(args)
|
53
|
+
klass = self.name.tableize.singularize
|
54
|
+
args = args.map(&:to_s).join(',')
|
55
|
+
|
56
|
+
"resque-serializer:job:#{klass}:#{args}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module Serializer
|
4
|
+
module Serializers
|
5
|
+
module Combined
|
6
|
+
|
7
|
+
# before_enqueue: ✓
|
8
|
+
# enqueue: |
|
9
|
+
# after_enqueue: |
|
10
|
+
# before_dequeue: |
|
11
|
+
# dequeue: |
|
12
|
+
# after_dequeue: |
|
13
|
+
# before_perform: |
|
14
|
+
# perform: |
|
15
|
+
# after_perform: ✗
|
16
|
+
|
17
|
+
def before_enqueue_set_lock(*args)
|
18
|
+
mutex(args).lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def around_perform_clear_lock(*args)
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
mutex(args).unlock
|
25
|
+
end
|
26
|
+
|
27
|
+
def mutex(args)
|
28
|
+
Serializer::Mutex.new(key(args))
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def key(args)
|
34
|
+
klass = self.name.tableize.singularize
|
35
|
+
args = args.map(&:to_s).join(',')
|
36
|
+
|
37
|
+
"resque-serializer:#{klass}:#{args}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module Serializer
|
4
|
+
module Serializers
|
5
|
+
module Job
|
6
|
+
|
7
|
+
# before_enqueue:
|
8
|
+
# enqueue:
|
9
|
+
# after_enqueue:
|
10
|
+
# before_dequeue: ✓
|
11
|
+
# dequeue: |
|
12
|
+
# after_dequeue: |
|
13
|
+
# before_perform: |
|
14
|
+
# perform: |
|
15
|
+
# after_perform: ✗
|
16
|
+
|
17
|
+
def before_dequeue_set_lock(*args)
|
18
|
+
mutex(args).lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def around_perform_clear_lock(*args)
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
mutex(args).unlock
|
25
|
+
end
|
26
|
+
|
27
|
+
def mutex(args)
|
28
|
+
Serializer::Mutex.new(key(args))
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def key(args)
|
34
|
+
klass = self.name.tableize.singularize
|
35
|
+
args = args.map(&:to_s).join(',')
|
36
|
+
|
37
|
+
"resque-serializer:#{klass}:#{args}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module Serializer
|
4
|
+
module Serializers
|
5
|
+
module Queue
|
6
|
+
|
7
|
+
# before_enqueue: ✓
|
8
|
+
# enqueue: |
|
9
|
+
# after_enqueue: |
|
10
|
+
# before_dequeue: |
|
11
|
+
# dequeue: |
|
12
|
+
# after_dequeue: ✗
|
13
|
+
# before_perform:
|
14
|
+
# perform:
|
15
|
+
# after_perform:
|
16
|
+
|
17
|
+
def before_enqueue_set_lock(*args)
|
18
|
+
mutex(args).lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_dequeue_clear_lock(*args)
|
22
|
+
mutex(args).unlock
|
23
|
+
end
|
24
|
+
|
25
|
+
def mutex(args)
|
26
|
+
Serializer::Mutex.new(key(args))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def key(args)
|
32
|
+
klass = self.name.tableize.singularize
|
33
|
+
args = args.map(&:to_s).join(',')
|
34
|
+
|
35
|
+
"resque-serializer:#{klass}:#{args}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'resque-serializer/version'
|
2
|
+
require 'resque-serializer/mutex'
|
3
|
+
require 'resque-serializer/serializers/both'
|
4
|
+
require 'resque-serializer/serializers/combined'
|
5
|
+
require 'resque-serializer/serializers/job'
|
6
|
+
require 'resque-serializer/serializers/queue'
|
7
|
+
|
8
|
+
module Resque
|
9
|
+
module Plugins
|
10
|
+
module Serializer
|
11
|
+
def serialize(resource)
|
12
|
+
case resource
|
13
|
+
when :job then extend(Serializers::Job)
|
14
|
+
when :queue then extend(Serializers::Queue)
|
15
|
+
when :both then extend(Serializers::Both)
|
16
|
+
when :combined then extend(Serializers::Combined)
|
17
|
+
else raise_invalid_resource
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def raise_invalid_resource
|
24
|
+
error_msg = begin
|
25
|
+
'The passed argument must be one of: [:job, :queue, :both, :combined]'
|
26
|
+
end
|
27
|
+
|
28
|
+
raise ArgumentError, error_msg
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/resque-serializer/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'resque-serializer'
|
6
|
+
gem.version = Resque::Plugins::Serializer::VERSION
|
7
|
+
gem.authors = ['Ryan Ringler']
|
8
|
+
gem.email = ['rringler@gmail.com']
|
9
|
+
|
10
|
+
gem.summary = 'Serializes Resque jobs'
|
11
|
+
gem.description = 'Ensures that only one Resque job with unique arguments is running at a time.'
|
12
|
+
gem.homepage = 'https://github.com/rringler/resque-serializer'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'activesupport'
|
21
|
+
gem.add_dependency 'resque'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'bundler', '~> 1.14'
|
24
|
+
gem.add_development_dependency 'mock_redis'
|
25
|
+
gem.add_development_dependency 'pry-byebug'
|
26
|
+
gem.add_development_dependency 'pry-stack_explorer'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
|
+
gem.add_development_dependency 'resque_spec'
|
29
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
30
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Resque::Plugins::Serializer::Mutex do
|
4
|
+
let(:redis) { Resque.redis }
|
5
|
+
|
6
|
+
describe 'instance methods' do
|
7
|
+
describe '#lock' do
|
8
|
+
let(:key) { 'key' }
|
9
|
+
let(:options) { {} }
|
10
|
+
let(:mutex) { described_class.new(key, options) }
|
11
|
+
|
12
|
+
subject(:lock) { mutex.lock }
|
13
|
+
|
14
|
+
it 'calls redis#set with the correct arguments' do
|
15
|
+
default_ttl = 5.minutes
|
16
|
+
|
17
|
+
expect(redis).to receive(:set).with(
|
18
|
+
key,
|
19
|
+
true,
|
20
|
+
nx: true,
|
21
|
+
px: default_ttl.to_i * 1000
|
22
|
+
).and_return(true)
|
23
|
+
|
24
|
+
lock
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when the lock gets set' do
|
28
|
+
before { allow(redis).to receive(:set).and_return(true) }
|
29
|
+
|
30
|
+
it 'returns true' do
|
31
|
+
expect(lock).to eq(true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when the lock does not get set' do
|
36
|
+
before { allow(redis).to receive(:set).and_return(false) }
|
37
|
+
|
38
|
+
it 'returns false' do
|
39
|
+
expect(lock).to eq(false)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#lock!' do
|
45
|
+
let(:key) { 'key' }
|
46
|
+
let(:options) { {} }
|
47
|
+
let(:mutex) { described_class.new(key, options) }
|
48
|
+
|
49
|
+
subject(:lock!) { mutex.lock! }
|
50
|
+
|
51
|
+
it 'calls redis#set with the correct arguments' do
|
52
|
+
default_ttl = 5.minutes
|
53
|
+
|
54
|
+
expect(redis).to receive(:set).with(
|
55
|
+
key,
|
56
|
+
true,
|
57
|
+
nx: true,
|
58
|
+
px: default_ttl.to_i * 1000
|
59
|
+
).and_return(true)
|
60
|
+
|
61
|
+
lock!
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when the lock gets set' do
|
65
|
+
before { allow(redis).to receive(:set).and_return(true) }
|
66
|
+
|
67
|
+
it 'returns true' do
|
68
|
+
expect(lock!).to eq(true)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when the lock does not get set' do
|
73
|
+
let(:error) { described_class::LockFailed }
|
74
|
+
|
75
|
+
before { allow(redis).to receive(:set).and_raise(error) }
|
76
|
+
|
77
|
+
it 'raises a Mutex::LockFailed error' do
|
78
|
+
expect { lock! }.to raise_error(error)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#unlock' do
|
84
|
+
let(:key) { 'key' }
|
85
|
+
let(:mutex) { described_class.new(key) }
|
86
|
+
|
87
|
+
subject(:unlock) { mutex.unlock }
|
88
|
+
|
89
|
+
it 'calls redis#del with its key' do
|
90
|
+
expect(redis).to receive(:del).with(key)
|
91
|
+
unlock
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns true' do
|
95
|
+
expect(unlock).to eq(true)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class JobSerializedByBoth
|
4
|
+
extend Resque::Plugins::Serializer
|
5
|
+
|
6
|
+
@queue = :default
|
7
|
+
|
8
|
+
serialize :both
|
9
|
+
|
10
|
+
# before_enqueue: ✓
|
11
|
+
# enqueue: |
|
12
|
+
# after_enqueue: |
|
13
|
+
# before_dequeue: | ✓
|
14
|
+
# dequeue: | |
|
15
|
+
# after_dequeue: ✗ |
|
16
|
+
# before_perform: |
|
17
|
+
# perform: |
|
18
|
+
# after_perform: ✗
|
19
|
+
|
20
|
+
def self.perform(*args); end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.describe JobSerializedByBoth do
|
24
|
+
let(:args) { %w(arg1 arg2) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
ResqueSpec.reset!
|
28
|
+
Resque.redis.redis.flushall
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'before enqueuing the job' do
|
32
|
+
let(:mutex) { described_class.queue_mutex(args) }
|
33
|
+
|
34
|
+
subject(:enqueue_job) { Resque.enqueue(described_class, *args) }
|
35
|
+
|
36
|
+
context 'when a lock for the job exists' do
|
37
|
+
before { mutex.lock! }
|
38
|
+
|
39
|
+
it 'does not enqueue the job' do
|
40
|
+
expect { enqueue_job }.to_not change {
|
41
|
+
ResqueSpec.queue_for(described_class).size
|
42
|
+
}.from(0)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not unlock the mutex' do
|
46
|
+
expect { enqueue_job }.to_not change {
|
47
|
+
mutex.locked?
|
48
|
+
}.from(true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when a lock for the job does not exist' do
|
53
|
+
it 'enqueues the job' do
|
54
|
+
expect { enqueue_job }.to change {
|
55
|
+
ResqueSpec.queue_for(described_class).size
|
56
|
+
}.from(0).to(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'locks the mutex' do
|
60
|
+
expect { enqueue_job }.to change {
|
61
|
+
mutex.locked?
|
62
|
+
}.from(false).to(true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'after dequeuing the job' do
|
68
|
+
let(:mutex) { described_class.queue_mutex(args) }
|
69
|
+
|
70
|
+
subject(:dequeue_job) { Resque.dequeue(described_class, *args) }
|
71
|
+
|
72
|
+
before { mutex.lock! }
|
73
|
+
|
74
|
+
it 'unlocks the mutex' do
|
75
|
+
expect { dequeue_job }.to change {
|
76
|
+
mutex.locked?
|
77
|
+
}.from(true).to(false)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'before dequeuing the job' do
|
82
|
+
let(:mutex) { described_class.job_mutex(args) }
|
83
|
+
|
84
|
+
subject(:dequeue_job) { Resque.dequeue(described_class, *args) }
|
85
|
+
|
86
|
+
it 'locks the mutex' do
|
87
|
+
expect { dequeue_job }.to change {
|
88
|
+
mutex.locked?
|
89
|
+
}.from(false).to(true)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'after performing the job' do
|
94
|
+
let(:mutex) { described_class.job_mutex(args) }
|
95
|
+
|
96
|
+
before do
|
97
|
+
Resque.enqueue(described_class, *args)
|
98
|
+
mutex.lock!
|
99
|
+
end
|
100
|
+
|
101
|
+
subject(:perform_job) { ResqueSpec.perform_next(:default) }
|
102
|
+
|
103
|
+
context 'if the job completes successfully' do
|
104
|
+
it 'releases the lock after execution' do
|
105
|
+
expect { perform_job }.to change {
|
106
|
+
mutex.locked?
|
107
|
+
}.from(true).to(false)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'if the job raises an exception' do
|
112
|
+
let(:error) { StandardError }
|
113
|
+
|
114
|
+
before do
|
115
|
+
allow(described_class).to receive(:perform).and_raise(error)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'still releases the lock after execution' do
|
119
|
+
expect(mutex.locked?).to eq(true)
|
120
|
+
|
121
|
+
expect { perform_job }.to raise_error(error)
|
122
|
+
|
123
|
+
expect(mutex.locked?).to eq(false)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class JobSerializedByCombined
|
4
|
+
extend Resque::Plugins::Serializer
|
5
|
+
|
6
|
+
@queue = :default
|
7
|
+
|
8
|
+
serialize :combined
|
9
|
+
|
10
|
+
# before_enqueue: ✓
|
11
|
+
# enqueue: |
|
12
|
+
# after_enqueue: |
|
13
|
+
# before_dequeue: |
|
14
|
+
# dequeue: |
|
15
|
+
# after_dequeue: |
|
16
|
+
# before_perform: |
|
17
|
+
# perform: |
|
18
|
+
# after_perform: ✗
|
19
|
+
|
20
|
+
def self.perform(*args); end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.describe JobSerializedByCombined do
|
24
|
+
let(:args) { %w(arg1 arg2) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
ResqueSpec.reset!
|
28
|
+
Resque.redis.redis.flushall
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'before enqueuing the job' do
|
32
|
+
let(:mutex) { described_class.mutex(args) }
|
33
|
+
|
34
|
+
subject(:enqueue_job) { Resque.enqueue(described_class, *args) }
|
35
|
+
|
36
|
+
context 'when a lock for the job exists' do
|
37
|
+
before { mutex.lock! }
|
38
|
+
|
39
|
+
it 'does not enqueue the job' do
|
40
|
+
expect { enqueue_job }.to_not change {
|
41
|
+
ResqueSpec.queue_for(described_class).size
|
42
|
+
}.from(0)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not change the mutex' do
|
46
|
+
expect { enqueue_job }.to_not change {
|
47
|
+
mutex.locked?
|
48
|
+
}.from(true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when a lock for the job does not exist' do
|
53
|
+
before { mutex.unlock }
|
54
|
+
|
55
|
+
it 'enqueues the job' do
|
56
|
+
expect { enqueue_job }.to change {
|
57
|
+
ResqueSpec.queue_for(described_class).size
|
58
|
+
}.from(0).to(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'locks the mutex' do
|
62
|
+
expect { enqueue_job }.to change {
|
63
|
+
mutex.locked?
|
64
|
+
}.from(false).to(true)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'after performing the job' do
|
70
|
+
let(:mutex) { described_class.mutex(args) }
|
71
|
+
|
72
|
+
before do
|
73
|
+
# Note: this locks the mutex on the :before_enqueue_* hook
|
74
|
+
Resque.enqueue(described_class, *args)
|
75
|
+
end
|
76
|
+
|
77
|
+
subject(:perform_job) { ResqueSpec.perform_next(:default) }
|
78
|
+
|
79
|
+
context 'when the job completes successfully' do
|
80
|
+
it 'releases the lock after execution' do
|
81
|
+
expect { perform_job }.to change {
|
82
|
+
mutex.locked?
|
83
|
+
}.from(true).to(false)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when the job raises an exception' do
|
88
|
+
let(:error) { StandardError }
|
89
|
+
|
90
|
+
before { allow(described_class).to receive(:perform).and_raise(error) }
|
91
|
+
|
92
|
+
it 'still releases the lock after execution' do
|
93
|
+
expect(mutex.locked?).to eq(true)
|
94
|
+
|
95
|
+
expect { perform_job }.to raise_error(error)
|
96
|
+
|
97
|
+
expect(mutex.locked?).to eq(false)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class JobSerializedByJob
|
4
|
+
extend Resque::Plugins::Serializer
|
5
|
+
|
6
|
+
@queue = :default
|
7
|
+
|
8
|
+
serialize :job
|
9
|
+
|
10
|
+
# before_enqueue:
|
11
|
+
# enqueue:
|
12
|
+
# after_enqueue:
|
13
|
+
# before_dequeue: ✓
|
14
|
+
# dequeue: |
|
15
|
+
# after_dequeue: |
|
16
|
+
# before_perform: |
|
17
|
+
# perform: |
|
18
|
+
# after_perform: ✗
|
19
|
+
|
20
|
+
def self.perform(*args); end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.describe JobSerializedByJob do
|
24
|
+
let(:args) { %w(arg1 arg2) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
ResqueSpec.reset!
|
28
|
+
Resque.redis.redis.flushall
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'before dequeuing the job' do
|
32
|
+
let(:mutex) { described_class.mutex(args) }
|
33
|
+
|
34
|
+
subject(:dequeue_job) { Resque.dequeue(described_class, *args) }
|
35
|
+
|
36
|
+
it 'locks the mutex' do
|
37
|
+
expect { dequeue_job }.to change {
|
38
|
+
mutex.locked?
|
39
|
+
}.from(false).to(true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'after performing the job' do
|
44
|
+
let(:mutex) { described_class.mutex(args) }
|
45
|
+
|
46
|
+
before do
|
47
|
+
Resque.enqueue(described_class, *args)
|
48
|
+
mutex.lock!
|
49
|
+
end
|
50
|
+
|
51
|
+
subject(:perform_job) { ResqueSpec.perform_next(:default) }
|
52
|
+
|
53
|
+
context 'when the job completes successfully' do
|
54
|
+
it 'releases the lock after execution' do
|
55
|
+
expect { perform_job }.to change {
|
56
|
+
mutex.locked?
|
57
|
+
}.from(true).to(false)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when the job raises an exception' do
|
62
|
+
let(:error) { StandardError }
|
63
|
+
|
64
|
+
before { allow(described_class).to receive(:perform).and_raise(error) }
|
65
|
+
|
66
|
+
it 'still releases the lock after execution' do
|
67
|
+
expect(mutex.locked?).to eq(true)
|
68
|
+
|
69
|
+
expect { perform_job }.to raise_error(error)
|
70
|
+
|
71
|
+
expect(mutex.locked?).to eq(false)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class JobSerializedByQueue
|
4
|
+
extend Resque::Plugins::Serializer
|
5
|
+
|
6
|
+
@queue = :default
|
7
|
+
|
8
|
+
serialize :queue
|
9
|
+
|
10
|
+
# before_enqueue: ✓
|
11
|
+
# enqueue: |
|
12
|
+
# after_enqueue: |
|
13
|
+
# before_dequeue: |
|
14
|
+
# dequeue: |
|
15
|
+
# after_dequeue: ✗
|
16
|
+
# before_perform:
|
17
|
+
# perform:
|
18
|
+
# after_perform:
|
19
|
+
|
20
|
+
def self.perform(*args); end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.describe JobSerializedByQueue do
|
24
|
+
let(:args) { %w(arg1 arg2) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
ResqueSpec.reset!
|
28
|
+
Resque.redis.redis.flushall
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'before enqueuing the job' do
|
32
|
+
let(:mutex) { described_class.mutex(args) }
|
33
|
+
|
34
|
+
subject(:enqueue_job) { Resque.enqueue(described_class, *args) }
|
35
|
+
|
36
|
+
context 'when a lock for the job exists' do
|
37
|
+
before { mutex.lock! }
|
38
|
+
|
39
|
+
it 'does not enqueue the job' do
|
40
|
+
expect { enqueue_job }.to_not change {
|
41
|
+
ResqueSpec.queue_for(described_class).size
|
42
|
+
}.from(0)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not change the mutex' do
|
46
|
+
expect { enqueue_job }.to_not change {
|
47
|
+
mutex.locked?
|
48
|
+
}.from(true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when a lock for the job does not exist' do
|
53
|
+
before { mutex.unlock }
|
54
|
+
|
55
|
+
it 'enqueues the job' do
|
56
|
+
expect { enqueue_job }.to change {
|
57
|
+
ResqueSpec.queue_for(described_class).size
|
58
|
+
}.from(0).to(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'locks the mutex' do
|
62
|
+
expect { enqueue_job }.to change {
|
63
|
+
mutex.locked?
|
64
|
+
}.from(false).to(true)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'after dequeuing the job' do
|
70
|
+
let(:mutex) { described_class.mutex(args) }
|
71
|
+
|
72
|
+
subject(:dequeue_job) { Resque.dequeue(described_class, *args) }
|
73
|
+
|
74
|
+
before { mutex.lock! }
|
75
|
+
|
76
|
+
it 'unlocks the mutex' do
|
77
|
+
expect { dequeue_job }.to change {
|
78
|
+
mutex.locked?
|
79
|
+
}.from(true).to(false)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'resque-serializer'
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'mock_redis'
|
5
|
+
require 'resque'
|
6
|
+
require 'resque_spec'
|
7
|
+
require 'pry-byebug'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.expect_with(:rspec) do |c|
|
11
|
+
c.syntax = :expect
|
12
|
+
end
|
13
|
+
|
14
|
+
config.before(:suite) do
|
15
|
+
Resque.redis = MockRedis.new
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-serializer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Ringler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: resque
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mock_redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-stack_explorer
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: resque_spec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
description: Ensures that only one Resque job with unique arguments is running at
|
140
|
+
a time.
|
141
|
+
email:
|
142
|
+
- rringler@gmail.com
|
143
|
+
executables:
|
144
|
+
- console
|
145
|
+
- setup
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- ".gitignore"
|
150
|
+
- ".rspec"
|
151
|
+
- ".travis.yml"
|
152
|
+
- Gemfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- bin/console
|
157
|
+
- bin/setup
|
158
|
+
- lib/resque-serializer.rb
|
159
|
+
- lib/resque-serializer/mutex.rb
|
160
|
+
- lib/resque-serializer/serializers/both.rb
|
161
|
+
- lib/resque-serializer/serializers/combined.rb
|
162
|
+
- lib/resque-serializer/serializers/job.rb
|
163
|
+
- lib/resque-serializer/serializers/queue.rb
|
164
|
+
- lib/resque-serializer/version.rb
|
165
|
+
- resque-serializer.gemspec
|
166
|
+
- spec/lib/resque-serializer/mutex_spec.rb
|
167
|
+
- spec/lib/resque-serializer/serializers/both_spec.rb
|
168
|
+
- spec/lib/resque-serializer/serializers/combined_spec.rb
|
169
|
+
- spec/lib/resque-serializer/serializers/job_spec.rb
|
170
|
+
- spec/lib/resque-serializer/serializers/queue_spec.rb
|
171
|
+
- spec/spec_helper.rb
|
172
|
+
homepage: https://github.com/rringler/resque-serializer
|
173
|
+
licenses:
|
174
|
+
- MIT
|
175
|
+
metadata: {}
|
176
|
+
post_install_message:
|
177
|
+
rdoc_options: []
|
178
|
+
require_paths:
|
179
|
+
- lib
|
180
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
requirements: []
|
191
|
+
rubyforge_project:
|
192
|
+
rubygems_version: 2.4.5.1
|
193
|
+
signing_key:
|
194
|
+
specification_version: 4
|
195
|
+
summary: Serializes Resque jobs
|
196
|
+
test_files:
|
197
|
+
- spec/lib/resque-serializer/mutex_spec.rb
|
198
|
+
- spec/lib/resque-serializer/serializers/both_spec.rb
|
199
|
+
- spec/lib/resque-serializer/serializers/combined_spec.rb
|
200
|
+
- spec/lib/resque-serializer/serializers/job_spec.rb
|
201
|
+
- spec/lib/resque-serializer/serializers/queue_spec.rb
|
202
|
+
- spec/spec_helper.rb
|