resque-unique_in_queue 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +91 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +18 -0
- data/LICENSE +24 -0
- data/README.md +158 -0
- data/Rakefile +14 -0
- data/lib/resque-unique_in_queue.rb +76 -0
- data/lib/resque/plugins/unique_in_queue.rb +72 -0
- data/lib/resque/unique_in_queue/configuration.rb +49 -0
- data/lib/resque/unique_in_queue/queue.rb +90 -0
- data/lib/resque/unique_in_queue/resque_ext/job.rb +64 -0
- data/lib/resque/unique_in_queue/resque_ext/resque.rb +41 -0
- data/lib/resque/unique_in_queue/version.rb +7 -0
- data/resque-unique_in_queue.gemspec +34 -0
- metadata +202 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 32f50a9af11ff498a110d4a93549fbb653c2d471252bde302e50c8b7d30eb5f2
|
|
4
|
+
data.tar.gz: 5913f22ba7c2c23666833c86200d181431b2e4f2c9827e363bee62d8dbca18a2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f2905f2489519bff3cd7424827fb67d6ef24bfaba5e164d0c539ebb986c2174d4d8fd38a4e5433447bc171a1313de44a82c9129662e0bfcb0c64ada21fb5868d
|
|
7
|
+
data.tar.gz: 8bda5be739bb307679330cf658d5e8865dedc6295af8d7ec657ac6982d5d4ac1ff394cd943070240d8d8c79c5504d3d7b1431645820c338e8fb67c8666c50633
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2018-11-07 04:05:15 -0800 using RuboCop version 0.60.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# Configuration parameters: Include.
|
|
11
|
+
# Include: **/*.gemspec
|
|
12
|
+
Gemspec/DuplicatedAssignment:
|
|
13
|
+
Exclude:
|
|
14
|
+
- 'resque-unique_in_queue.gemspec'
|
|
15
|
+
|
|
16
|
+
# Offense count: 1
|
|
17
|
+
# Configuration parameters: Include.
|
|
18
|
+
# Include: **/*.gemspec
|
|
19
|
+
Gemspec/RequiredRubyVersion:
|
|
20
|
+
Exclude:
|
|
21
|
+
- 'resque-unique_in_queue.gemspec'
|
|
22
|
+
|
|
23
|
+
# Offense count: 1
|
|
24
|
+
# Cop supports --auto-correct.
|
|
25
|
+
# Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity.
|
|
26
|
+
# SupportedStylesAlignWith: start_of_line, def
|
|
27
|
+
Layout/DefEndAlignment:
|
|
28
|
+
Exclude:
|
|
29
|
+
- 'lib/resque/unique_in_queue/queue.rb'
|
|
30
|
+
|
|
31
|
+
# Offense count: 1
|
|
32
|
+
Lint/HandleExceptions:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'test/test_helper.rb'
|
|
35
|
+
|
|
36
|
+
# Offense count: 2
|
|
37
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
|
38
|
+
Metrics/MethodLength:
|
|
39
|
+
Max: 11
|
|
40
|
+
|
|
41
|
+
# Offense count: 1
|
|
42
|
+
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
|
|
43
|
+
# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
|
|
44
|
+
Naming/FileName:
|
|
45
|
+
Exclude:
|
|
46
|
+
- 'lib/resque-unique_in_queue.rb'
|
|
47
|
+
|
|
48
|
+
# Offense count: 1
|
|
49
|
+
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
|
|
50
|
+
# NamePrefix: is_, has_, have_
|
|
51
|
+
# NamePrefixBlacklist: is_, has_, have_
|
|
52
|
+
# NameWhitelist: is_a?
|
|
53
|
+
# MethodDefinitionMacros: define_method, define_singleton_method
|
|
54
|
+
Naming/PredicateName:
|
|
55
|
+
Exclude:
|
|
56
|
+
- 'spec/**/*'
|
|
57
|
+
- 'lib/resque/unique_in_queue/queue.rb'
|
|
58
|
+
|
|
59
|
+
# Offense count: 4
|
|
60
|
+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
61
|
+
# AllowedNames: io, id, to, by, on, in, at, ip, db
|
|
62
|
+
Naming/UncommunicativeMethodParamName:
|
|
63
|
+
Exclude:
|
|
64
|
+
- 'test/fake_jobs.rb'
|
|
65
|
+
|
|
66
|
+
# Offense count: 6
|
|
67
|
+
Style/Documentation:
|
|
68
|
+
Exclude:
|
|
69
|
+
- 'spec/**/*'
|
|
70
|
+
- 'test/**/*'
|
|
71
|
+
- 'lib/resque-unique_in_queue.rb'
|
|
72
|
+
- 'lib/resque/plugins/unique_in_queue.rb'
|
|
73
|
+
- 'lib/resque/unique_in_queue/configuration.rb'
|
|
74
|
+
- 'lib/resque/unique_in_queue/queue.rb'
|
|
75
|
+
- 'lib/resque/unique_in_queue/resque_ext/job.rb'
|
|
76
|
+
- 'lib/resque/unique_in_queue/resque_ext/resque.rb'
|
|
77
|
+
|
|
78
|
+
# Offense count: 1
|
|
79
|
+
# Cop supports --auto-correct.
|
|
80
|
+
# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
|
|
81
|
+
# SupportedStyles: predicate, comparison
|
|
82
|
+
Style/NumericPredicate:
|
|
83
|
+
Exclude:
|
|
84
|
+
- 'spec/**/*'
|
|
85
|
+
- 'lib/resque/unique_in_queue/queue.rb'
|
|
86
|
+
|
|
87
|
+
# Offense count: 26
|
|
88
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
|
89
|
+
# URISchemes: http, https
|
|
90
|
+
Metrics/LineLength:
|
|
91
|
+
Max: 116
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby-2.5.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
6
|
+
|
|
7
|
+
group :test do
|
|
8
|
+
unless ENV['TRAVIS']
|
|
9
|
+
gem 'byebug', '~> 10', platform: :mri, require: false
|
|
10
|
+
gem 'pry', '~> 0', platform: :mri, require: false
|
|
11
|
+
gem 'pry-byebug', '~> 3', platform: :mri, require: false
|
|
12
|
+
end
|
|
13
|
+
gem 'rubocop', '~> 0.60.0'
|
|
14
|
+
gem 'simplecov', '~> 0', require: false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Specify your gem's dependencies in resque-unique_in_queue.gemspec
|
|
18
|
+
gemspec
|
data/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2010 Moviepilot GmbH http://moviepilot.com
|
|
2
|
+
Copyright (c) 2013 Neighborland, Inc.
|
|
3
|
+
Copyright (c) 2017 - 2018 Peter H. Boling
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
8
|
+
a copy of this software and associated documentation files (the
|
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
13
|
+
the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be
|
|
16
|
+
included in all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Resque::UniqueInQueue
|
|
2
|
+
|
|
3
|
+
| Project | Resque::UniqueInQueue |
|
|
4
|
+
|------------------------ | ----------------------- |
|
|
5
|
+
| gem name | [resque-unique_in_queue](https://rubygems.org/gems/resque-unique_in_queue) |
|
|
6
|
+
| license | [](https://opensource.org/licenses/MIT) |
|
|
7
|
+
| download rank | [](https://github.com/pboling/resque-unique_in_queue) |
|
|
8
|
+
| version | [](https://rubygems.org/gems/resque-unique_in_queue) |
|
|
9
|
+
| dependencies | [](https://depfu.com/github/pboling/resque-unique_in_queue?project_id=2729) |
|
|
10
|
+
| continuous integration | [](https://travis-ci.org/pboling/resque-unique_in_queue) |
|
|
11
|
+
| test coverage | [](https://codeclimate.com/github/pboling/resque-unique_in_queue/test_coverage) |
|
|
12
|
+
| maintainability | [](https://codeclimate.com/github/pboling/resque-unique_in_queue/maintainability) |
|
|
13
|
+
| code triage | [](https://www.codetriage.com/pboling/resque-unique_in_queue) |
|
|
14
|
+
| homepage | [on Github.com][homepage], [on Railsbling.com][blogpage] |
|
|
15
|
+
| documentation | [on RDoc.info][documentation] |
|
|
16
|
+
| Spread ~♡ⓛⓞⓥⓔ♡~ | [🌍 🌎 🌏](https://about.me/peter.boling), [🍚](https://www.crowdrise.com/helprefugeeswithhopefortomorrowliberia/fundraiser/peterboling), [➕](https://plus.google.com/+PeterBoling/posts), [👼](https://angel.co/peter-boling), [🐛](https://www.topcoder.com/members/pboling/), [:shipit:](http://coderwall.com/pboling), [](http://twitter.com/galtzo) |
|
|
17
|
+
|
|
18
|
+
Resque::UniqueInQueue is a resque plugin to add unique jobs to resque.
|
|
19
|
+
|
|
20
|
+
It is a re-write of [resque-loner](https://github.com/jayniz/resque-loner).
|
|
21
|
+
|
|
22
|
+
It requires resque 1.25 and works with ruby 2.0 and later.
|
|
23
|
+
|
|
24
|
+
It removes the dependency on `Resque::Helpers`, which is deprecated for resque 2.0.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Add the gem to your Gemfile:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem 'resque-unique_in_queue'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
class UpdateCat
|
|
38
|
+
include Resque::Plugins::UniqueJob
|
|
39
|
+
@queue = :cats
|
|
40
|
+
|
|
41
|
+
def self.perform(cat_id)
|
|
42
|
+
# do something
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you attempt to queue a unique job multiple times, it is ignored:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Resque.enqueue UpdateCat, 1
|
|
51
|
+
=> true
|
|
52
|
+
Resque.enqueue UpdateCat, 1
|
|
53
|
+
=> nil
|
|
54
|
+
Resque.enqueue UpdateCat, 1
|
|
55
|
+
=> nil
|
|
56
|
+
Resque.size :cats
|
|
57
|
+
=> 1
|
|
58
|
+
Resque.enqueued? UpdateCat, 1
|
|
59
|
+
=> true
|
|
60
|
+
Resque.enqueued_in? :dogs, UpdateCat, 1
|
|
61
|
+
=> false
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Options
|
|
65
|
+
|
|
66
|
+
#### `lock_after_execution_period`
|
|
67
|
+
|
|
68
|
+
By default, lock_after_execution_period is 0 and `enqueued?` becomes false as soon as the job
|
|
69
|
+
is being worked on.
|
|
70
|
+
|
|
71
|
+
The `lock_after_execution_period` setting can be used to delay when the unique job key is deleted
|
|
72
|
+
(i.e. when `enqueued?` becomes `false`). For example, if you have a long-running unique job that
|
|
73
|
+
takes around 10 seconds, and you don't want to requeue another job until you are sure it is done,
|
|
74
|
+
you could set `lock_after_execution_period = 20`. Or if you never want to run a long running
|
|
75
|
+
job more than once per minute, set `lock_after_execution_period = 60`.
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
class UpdateCat
|
|
79
|
+
include Resque::Plugins::UniqueJob
|
|
80
|
+
@queue = :cats
|
|
81
|
+
@lock_after_execution_period = 20
|
|
82
|
+
|
|
83
|
+
def self.perform(cat_id)
|
|
84
|
+
# do something
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Oops, I have stale Queue Time uniqueness keys...
|
|
90
|
+
|
|
91
|
+
Preventing jobs with matching signatures from being queued, and they never get
|
|
92
|
+
dequeued because there is no actual corresponding job to dequeue.
|
|
93
|
+
|
|
94
|
+
*How to deal?*
|
|
95
|
+
|
|
96
|
+
Option: Rampage
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
# Delete *all* queued jobs in the queue, and
|
|
100
|
+
# delete *all* unqueness keys for the queue.
|
|
101
|
+
Redis.remove_queue('queue_name')
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Option: Butterfly
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
# Delete *no* queued jobs at all, and
|
|
108
|
+
# delete *all* unqueness keys for the queue (might then allow duplicates).
|
|
109
|
+
Resque::UniqueInQueue::Queue.cleanup('queue_name')
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Contributing
|
|
113
|
+
|
|
114
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/pboling/resque-unique_in_queue. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
115
|
+
|
|
116
|
+
1. Fork it
|
|
117
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
118
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
119
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
120
|
+
5. Create new Pull Request
|
|
121
|
+
|
|
122
|
+
## Code of Conduct
|
|
123
|
+
|
|
124
|
+
Everyone interacting in the Resque::Plugins::UniqueInQueue project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/pboling/resque-unique_in_queue/blob/master/CODE_OF_CONDUCT.md).
|
|
125
|
+
|
|
126
|
+
## Versioning
|
|
127
|
+
|
|
128
|
+
This library aims to adhere to [Semantic Versioning 2.0.0][semver].
|
|
129
|
+
Violations of this scheme should be reported as bugs. Specifically,
|
|
130
|
+
if a minor or patch version is released that breaks backward
|
|
131
|
+
compatibility, a new version should be immediately released that
|
|
132
|
+
restores compatibility. Breaking changes to the public API will
|
|
133
|
+
only be introduced with new major versions.
|
|
134
|
+
|
|
135
|
+
As a result of this policy, you can (and should) specify a
|
|
136
|
+
dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision.
|
|
137
|
+
|
|
138
|
+
For example:
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
spec.add_dependency 'resque-unique_in_queue', '~> 1.0'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
* Copyright (c) 2012 Jonathan R. Wallace
|
|
147
|
+
* Copyright (c) 2017 - 2018 [Peter H. Boling][peterboling] of [Rails Bling][railsbling]
|
|
148
|
+
|
|
149
|
+
[](https://opensource.org/licenses/MIT)
|
|
150
|
+
|
|
151
|
+
[license]: LICENSE
|
|
152
|
+
[semver]: http://semver.org/
|
|
153
|
+
[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
|
|
154
|
+
[railsbling]: http://www.railsbling.com
|
|
155
|
+
[peterboling]: http://www.peterboling.com
|
|
156
|
+
[documentation]: http://rdoc.info/github/pboling/resque-unique_in_queue/frames
|
|
157
|
+
[homepage]: https://github.com/pboling/resque-unique_in_queue/
|
|
158
|
+
[blogpage]: http://www.railsbling.com/tags/resque-unique_in_queue/
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/gem_tasks'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
|
|
7
|
+
desc 'Run tests'
|
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
|
9
|
+
t.libs << 'test'
|
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
|
11
|
+
t.verbose = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
task default: :test
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'resque/unique_in_queue/version'
|
|
4
|
+
|
|
5
|
+
# Ruby Std Lib
|
|
6
|
+
require 'digest/md5'
|
|
7
|
+
|
|
8
|
+
# External Gems
|
|
9
|
+
require 'colorized_string'
|
|
10
|
+
require 'resque'
|
|
11
|
+
|
|
12
|
+
# This Gem
|
|
13
|
+
require 'resque/plugins/unique_in_queue'
|
|
14
|
+
require 'resque/unique_in_queue/resque_ext/job'
|
|
15
|
+
require 'resque/unique_in_queue/resque_ext/resque'
|
|
16
|
+
require 'resque/unique_in_queue/queue'
|
|
17
|
+
require 'resque/unique_in_queue/configuration'
|
|
18
|
+
|
|
19
|
+
# See lib/resque/plugins/unique_in_queue.rb for the actual plugin
|
|
20
|
+
#
|
|
21
|
+
# This is not that ^. Rather, it is an API used by the plugin or as tools by a
|
|
22
|
+
# developer. These methods are not intended to be included/extended into
|
|
23
|
+
# Resque, Resque::Job, or Resque::Queue.
|
|
24
|
+
module Resque
|
|
25
|
+
module UniqueInQueue
|
|
26
|
+
PLUGIN_TAG = (ColorizedString['[R-UIQ] '].blue).freeze
|
|
27
|
+
|
|
28
|
+
def in_queue_unique_log(message, config_proxy = nil)
|
|
29
|
+
config_proxy ||= uniqueness_configuration
|
|
30
|
+
config_proxy.unique_logger&.send(config_proxy.unique_log_level, message) if config_proxy.unique_logger
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def in_queue_unique_debug(message, config_proxy = nil)
|
|
34
|
+
config_proxy ||= uniqueness_configuration
|
|
35
|
+
config_proxy.unique_logger&.debug("#{PLUGIN_TAG}#{message}") if config_proxy.debug_mode
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# There are times when the class will need access to the configuration object,
|
|
39
|
+
# such as to override it per instance method
|
|
40
|
+
def uniq_config
|
|
41
|
+
@uniqueness_configuration
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# For per-class config with a block
|
|
45
|
+
def uniqueness_configure
|
|
46
|
+
@uniqueness_configuration ||= Configuration.new
|
|
47
|
+
yield(@uniqueness_configuration)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#### CONFIG ####
|
|
51
|
+
class << self
|
|
52
|
+
attr_accessor :uniqueness_configuration
|
|
53
|
+
end
|
|
54
|
+
def uniqueness_config_reset(config = Configuration.new)
|
|
55
|
+
@uniqueness_configuration = config
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def uniqueness_log_level
|
|
59
|
+
@uniqueness_configuration.log_level
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def uniqueness_log_level=(log_level)
|
|
63
|
+
@uniqueness_configuration.log_level = log_level
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
self.uniqueness_configuration = Configuration.new # setup defaults
|
|
67
|
+
|
|
68
|
+
module_function(:in_queue_unique_log,
|
|
69
|
+
:in_queue_unique_debug,
|
|
70
|
+
:uniq_config,
|
|
71
|
+
:uniqueness_configure,
|
|
72
|
+
:uniqueness_config_reset,
|
|
73
|
+
:uniqueness_log_level,
|
|
74
|
+
:uniqueness_log_level=)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Resque
|
|
2
|
+
module Plugins
|
|
3
|
+
# If you want your job to support uniqueness at enqueue-time, simply include
|
|
4
|
+
# this module into your job class.
|
|
5
|
+
#
|
|
6
|
+
# class EnqueueAlone
|
|
7
|
+
# @queue = :enqueue_alone
|
|
8
|
+
# include Resque::Plugins::UniqueInQueue
|
|
9
|
+
#
|
|
10
|
+
# def self.perform(arg1, arg2)
|
|
11
|
+
# alone_stuff
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
module UniqueInQueue
|
|
16
|
+
def self.included(base)
|
|
17
|
+
base.extend ClassMethods
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
def unique_in_queue_redis_key(queue, item)
|
|
22
|
+
"#{unique_in_queue_key_base}:queue:#{queue}:job:#{Resque::UniqueInQueue::Queue.const_for(item).redis_key(item)}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Payload is what Resque stored for this job along with the job's class name:
|
|
26
|
+
# a hash containing string keys 'class' and 'args'
|
|
27
|
+
def redis_key(payload)
|
|
28
|
+
payload = Resque.decode(Resque.encode(payload))
|
|
29
|
+
job = payload['class']
|
|
30
|
+
args = payload['args']
|
|
31
|
+
args.map! do |arg|
|
|
32
|
+
arg.is_a?(Hash) ? arg.sort : arg
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Digest::MD5.hexdigest Resque.encode(class: job, args: args)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The default ttl of a locking key is -1 (forever).
|
|
39
|
+
# To expire the lock after a certain amount of time, set a ttl (in seconds).
|
|
40
|
+
# For example:
|
|
41
|
+
#
|
|
42
|
+
# class FooJob
|
|
43
|
+
# include Resque::Plugins::UniqueJob
|
|
44
|
+
# @ttl = 40
|
|
45
|
+
# end
|
|
46
|
+
def ttl
|
|
47
|
+
@ttl ||= Resque::UniqueInQueue.uniq_config&.ttl
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The default ttl of a persisting key is 0, i.e. immediately deleted.
|
|
51
|
+
# Set lock_after_execution_period to block the execution
|
|
52
|
+
# of the job for a certain amount of time (in seconds).
|
|
53
|
+
# For example:
|
|
54
|
+
#
|
|
55
|
+
# class FooJob
|
|
56
|
+
# include Resque::Plugins::UniqueJob
|
|
57
|
+
# @lock_after_execution_period = 40
|
|
58
|
+
# end
|
|
59
|
+
def lock_after_execution_period
|
|
60
|
+
@lock_after_execution_period ||= Resque::UniqueInQueue.uniq_config&.lock_after_execution_period
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Can't be overridden per each class because it wouldn't make sense.
|
|
64
|
+
# It wouldn't be able to determine or enforce uniqueness across queues,
|
|
65
|
+
# and general cleanup of stray keys would be nearly impossible.
|
|
66
|
+
def unique_in_queue_key_base
|
|
67
|
+
Resque::UniqueInQueue.uniq_config&.unique_in_queue_key_base
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
module Resque
|
|
3
|
+
module UniqueInQueue
|
|
4
|
+
class Configuration
|
|
5
|
+
DEFAULT_IN_QUEUE_KEY_BASE = 'r-uiq'.freeze
|
|
6
|
+
DEFAULT_LOCK_AFTER_EXECUTION_PERIOD = 0
|
|
7
|
+
DEFAULT_TTL = -1
|
|
8
|
+
|
|
9
|
+
attr_accessor :logger,
|
|
10
|
+
:log_level,
|
|
11
|
+
:unique_in_queue_key_base,
|
|
12
|
+
:lock_after_execution_period,
|
|
13
|
+
:ttl,
|
|
14
|
+
:debug_mode
|
|
15
|
+
def initialize(**options)
|
|
16
|
+
@logger = options.key?(:logger) ? options[:logger] : Logger.new(STDOUT)
|
|
17
|
+
@log_level = options.key?(:log_level) ? options[:log_level] : :debug
|
|
18
|
+
|
|
19
|
+
# Can't be set per job:
|
|
20
|
+
@unique_in_queue_key_base = options.key?(:unique_in_queue_key_base) ? options[:unique_in_queue_key_base] : DEFAULT_IN_QUEUE_KEY_BASE
|
|
21
|
+
|
|
22
|
+
# Can be set per each job:
|
|
23
|
+
@lock_after_execution_period = options.key?(:lock_after_execution_period) ? options[:lock_after_execution_period] : DEFAULT_LOCK_AFTER_EXECUTION_PERIOD
|
|
24
|
+
@ttl = options.key?(:ttl) ? options[:ttl] : DEFAULT_TTL
|
|
25
|
+
env_debug = ENV['RESQUE_DEBUG']
|
|
26
|
+
@debug_mode = options.key?(:debug_mode) ? options[:debug_mode] : env_debug == 'true' || (env_debug.is_a?(String) && env_debug.match?(/in_queue/))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def unique_logger
|
|
30
|
+
logger
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unique_log_level
|
|
34
|
+
log_level
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def log(msg)
|
|
38
|
+
Resque::UniqueInQueue.in_queue_unique_log(msg, self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_hash
|
|
42
|
+
{
|
|
43
|
+
logger: logger,
|
|
44
|
+
log_level: log_level
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Resque
|
|
2
|
+
module UniqueInQueue
|
|
3
|
+
module Queue
|
|
4
|
+
def queued?(queue, item)
|
|
5
|
+
return false unless is_unique?(item)
|
|
6
|
+
|
|
7
|
+
redis.get(unique_key(queue, item)) == '1'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def mark_queued(queue, item)
|
|
11
|
+
return unless is_unique?(item)
|
|
12
|
+
|
|
13
|
+
key = unique_key(queue, item)
|
|
14
|
+
redis.set(key, 1)
|
|
15
|
+
ttl = item_ttl(item)
|
|
16
|
+
redis.expire(key, ttl) if ttl >= 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def mark_unqueued(queue, job)
|
|
20
|
+
item = job.is_a?(Resque::Job) ? job.payload : job
|
|
21
|
+
return unless is_unique?(item)
|
|
22
|
+
|
|
23
|
+
ttl = lock_after_execution_period(item)
|
|
24
|
+
if ttl == 0
|
|
25
|
+
redis.del(unique_key(queue, item))
|
|
26
|
+
else
|
|
27
|
+
redis.expire(unique_key(queue, item), ttl)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def unique_key(queue, item)
|
|
32
|
+
const_for(item).unique_in_queue_redis_key(queue, item)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def is_unique?(item)
|
|
36
|
+
const_for(item).included_modules.include?(::Resque::Plugins::UniqueInQueue)
|
|
37
|
+
rescue NameError
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def item_ttl(item)
|
|
42
|
+
const_for(item).ttl
|
|
43
|
+
rescue NameError
|
|
44
|
+
-1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lock_after_execution_period(item)
|
|
48
|
+
const_for(item).lock_after_execution_period
|
|
49
|
+
rescue NameError
|
|
50
|
+
0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def destroy(queue, klass, *args)
|
|
54
|
+
klass = klass.to_s
|
|
55
|
+
redis_queue = "queue:#{queue}"
|
|
56
|
+
|
|
57
|
+
redis.lrange(redis_queue, 0, -1).each do |string|
|
|
58
|
+
json = Resque.decode(string)
|
|
59
|
+
next unless json['class'] == klass
|
|
60
|
+
next if args.any? && json['args'] != args
|
|
61
|
+
|
|
62
|
+
Resque::UniqueInQueue::Queue.mark_unqueued(queue, json)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def cleanup(queue)
|
|
67
|
+
keys = redis.keys("#{Resque::UniqueInQueue.uniq_config&.unique_in_queue_key_base}:queue:#{queue}:job:*")
|
|
68
|
+
redis.del(*keys) if keys.any?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def redis
|
|
74
|
+
Resque.redis
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def item_class(item)
|
|
78
|
+
item[:class] || item['class']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def const_for(item)
|
|
82
|
+
Resque.constantize(item_class(item))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
module_function :queued?, :mark_queued, :mark_unqueued, :unique_key
|
|
86
|
+
module_function :is_unique?, :item_ttl, :lock_after_execution_period
|
|
87
|
+
module_function :destroy, :cleanup, :redis, :item_class, :const_for
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Resque
|
|
2
|
+
class Job
|
|
3
|
+
class << self
|
|
4
|
+
# Mark an item as queued
|
|
5
|
+
def create_unique_in_queue(queue, klass, *args)
|
|
6
|
+
item = { class: klass.to_s, args: args }
|
|
7
|
+
if Resque.inline? || !Resque::UniqueInQueue::Queue.is_unique?(item)
|
|
8
|
+
return create_without_unique_in_queue(queue, klass, *args)
|
|
9
|
+
end
|
|
10
|
+
return 'EXISTED' if Resque::UniqueInQueue::Queue.queued?(queue, item)
|
|
11
|
+
|
|
12
|
+
create_return_value = false
|
|
13
|
+
# redis transaction block
|
|
14
|
+
Resque.redis.multi do
|
|
15
|
+
create_return_value = create_without_unique_in_queue(queue, klass, *args)
|
|
16
|
+
Resque::UniqueInQueue::Queue.mark_queued(queue, item)
|
|
17
|
+
end
|
|
18
|
+
create_return_value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Mark an item as unqueued
|
|
22
|
+
def reserve_unique_in_queue(queue)
|
|
23
|
+
item = reserve_without_unique_in_queue(queue)
|
|
24
|
+
Resque::UniqueInQueue::Queue.mark_unqueued(queue, item) if item && !Resque.inline?
|
|
25
|
+
item
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Mark destroyed jobs as unqueued
|
|
29
|
+
def destroy_unique_in_queue(queue, klass, *args)
|
|
30
|
+
Resque::UniqueInQueue::Queue.destroy(queue, klass, *args) unless Resque.inline?
|
|
31
|
+
destroy_without_unique_in_queue(queue, klass, *args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
alias create_without_unique_in_queue create
|
|
35
|
+
alias create create_unique_in_queue
|
|
36
|
+
alias reserve_without_unique_in_queue reserve
|
|
37
|
+
alias reserve reserve_unique_in_queue
|
|
38
|
+
alias destroy_without_unique_in_queue destroy
|
|
39
|
+
alias destroy destroy_unique_in_queue
|
|
40
|
+
|
|
41
|
+
if defined?(Resque::Plugins::PriorityEnqueue::Resque)
|
|
42
|
+
# Hack to support resque-priority_enqueue: https://github.com/coupa/resque-priority_enqueue
|
|
43
|
+
def priority_create_unique_in_queue(queue, klass, *args)
|
|
44
|
+
item = { class: klass.to_s, args: args }
|
|
45
|
+
if Resque.inline? || !Resque::UniqueInQueue::Queue.is_unique?(item)
|
|
46
|
+
return priority_create_without_unique_in_queue(queue, klass, *args)
|
|
47
|
+
end
|
|
48
|
+
return 'EXISTED' if Resque::UniqueInQueue::Queue.queued?(queue, item)
|
|
49
|
+
|
|
50
|
+
priority_create_return_value = false
|
|
51
|
+
# redis transaction block
|
|
52
|
+
Resque.redis.multi do
|
|
53
|
+
priority_create_return_value = priority_create_without_unique_in_queue(queue, klass, *args)
|
|
54
|
+
Resque::UniqueInQueue::Queue.mark_queued(queue, item)
|
|
55
|
+
end
|
|
56
|
+
priority_create_return_value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
alias priority_create_without_unique_in_queue priority_create
|
|
60
|
+
alias priority_create priority_create_unique_in_queue
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Resque
|
|
2
|
+
class << self
|
|
3
|
+
# Override
|
|
4
|
+
# https://github.com/resque/resque/blob/master/lib/resque.rb
|
|
5
|
+
def enqueue_to(queue, klass, *args)
|
|
6
|
+
# Perform before_enqueue hooks. Don't perform enqueue if any hook returns false
|
|
7
|
+
before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
|
|
8
|
+
klass.send(hook, *args)
|
|
9
|
+
end
|
|
10
|
+
return nil if before_hooks.any? { |result| result == false }
|
|
11
|
+
|
|
12
|
+
result = Job.create(queue, klass, *args)
|
|
13
|
+
return nil if result == 'EXISTED'
|
|
14
|
+
|
|
15
|
+
Plugin.after_enqueue_hooks(klass).each do |hook|
|
|
16
|
+
klass.send(hook, *args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def enqueued?(klass, *args)
|
|
23
|
+
enqueued_in?(queue_from_class(klass), klass, *args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def enqueued_in?(queue, klass, *args)
|
|
27
|
+
item = { class: klass.to_s, args: args }
|
|
28
|
+
return nil unless Resque::UniqueInQueue::Queue.is_unique?(item)
|
|
29
|
+
|
|
30
|
+
Resque::UniqueInQueue::Queue.queued?(queue, item)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def remove_queue_with_unique_in_queue_cleanup(queue)
|
|
34
|
+
remove_queue_without_unique_in_queue_cleanup(queue)
|
|
35
|
+
Resque::UniqueInQueue::Queue.cleanup(queue)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias remove_queue_without_unique_in_queue_cleanup remove_queue
|
|
39
|
+
alias remove_queue remove_queue_with_unique_in_queue_cleanup
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path('lib/resque/unique_in_queue/version', __dir__)
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'resque-unique_in_queue'
|
|
7
|
+
spec.version = Resque::UniqueInQueue::VERSION
|
|
8
|
+
spec.authors = ['Peter H. Boling', 'Tee Parham']
|
|
9
|
+
spec.email = %w[peter.boling@gmail.com]
|
|
10
|
+
spec.license = 'MIT'
|
|
11
|
+
|
|
12
|
+
spec.summary = 'A resque plugin that ensures job uniqueness at enqueue time.'
|
|
13
|
+
spec.summary = 'A resque plugin that ensures job uniqueness at enqueue time.'
|
|
14
|
+
spec.homepage = 'https://github.com/pboling/resque-unique_in_queue'
|
|
15
|
+
spec.required_ruby_version = '>= 2.3.0'
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
|
19
|
+
end
|
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
|
+
spec.require_paths = ['lib']
|
|
22
|
+
|
|
23
|
+
spec.add_runtime_dependency 'colorize', '~> 0.8'
|
|
24
|
+
spec.add_runtime_dependency 'resque', '>= 1.2'
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
27
|
+
spec.add_development_dependency 'byebug', '~> 10.0'
|
|
28
|
+
spec.add_development_dependency 'fakeredis', '~> 0.7'
|
|
29
|
+
spec.add_development_dependency 'minitest', '~> 5.11'
|
|
30
|
+
spec.add_development_dependency 'pry', '~> 0.11'
|
|
31
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.6'
|
|
32
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
|
33
|
+
spec.add_development_dependency 'rubocop', '~> 0.60'
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: resque-unique_in_queue
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Peter H. Boling
|
|
8
|
+
- Tee Parham
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2018-11-08 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: colorize
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0.8'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0.8'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: resque
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '1.2'
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '1.2'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: bundler
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - "~>"
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '1.16'
|
|
49
|
+
type: :development
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '1.16'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: byebug
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '10.0'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '10.0'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: fakeredis
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - "~>"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0.7'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - "~>"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0.7'
|
|
84
|
+
- !ruby/object:Gem::Dependency
|
|
85
|
+
name: minitest
|
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - "~>"
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '5.11'
|
|
91
|
+
type: :development
|
|
92
|
+
prerelease: false
|
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - "~>"
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '5.11'
|
|
98
|
+
- !ruby/object:Gem::Dependency
|
|
99
|
+
name: pry
|
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - "~>"
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0.11'
|
|
105
|
+
type: :development
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - "~>"
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0.11'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: pry-byebug
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - "~>"
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '3.6'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - "~>"
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '3.6'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: rake
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - "~>"
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '12.3'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - "~>"
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '12.3'
|
|
140
|
+
- !ruby/object:Gem::Dependency
|
|
141
|
+
name: rubocop
|
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - "~>"
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0.60'
|
|
147
|
+
type: :development
|
|
148
|
+
prerelease: false
|
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
150
|
+
requirements:
|
|
151
|
+
- - "~>"
|
|
152
|
+
- !ruby/object:Gem::Version
|
|
153
|
+
version: '0.60'
|
|
154
|
+
description:
|
|
155
|
+
email:
|
|
156
|
+
- peter.boling@gmail.com
|
|
157
|
+
executables: []
|
|
158
|
+
extensions: []
|
|
159
|
+
extra_rdoc_files: []
|
|
160
|
+
files:
|
|
161
|
+
- ".gitignore"
|
|
162
|
+
- ".rubocop.yml"
|
|
163
|
+
- ".rubocop_todo.yml"
|
|
164
|
+
- ".ruby-version"
|
|
165
|
+
- ".travis.yml"
|
|
166
|
+
- Gemfile
|
|
167
|
+
- LICENSE
|
|
168
|
+
- README.md
|
|
169
|
+
- Rakefile
|
|
170
|
+
- lib/resque-unique_in_queue.rb
|
|
171
|
+
- lib/resque/plugins/unique_in_queue.rb
|
|
172
|
+
- lib/resque/unique_in_queue/configuration.rb
|
|
173
|
+
- lib/resque/unique_in_queue/queue.rb
|
|
174
|
+
- lib/resque/unique_in_queue/resque_ext/job.rb
|
|
175
|
+
- lib/resque/unique_in_queue/resque_ext/resque.rb
|
|
176
|
+
- lib/resque/unique_in_queue/version.rb
|
|
177
|
+
- resque-unique_in_queue.gemspec
|
|
178
|
+
homepage: https://github.com/pboling/resque-unique_in_queue
|
|
179
|
+
licenses:
|
|
180
|
+
- MIT
|
|
181
|
+
metadata: {}
|
|
182
|
+
post_install_message:
|
|
183
|
+
rdoc_options: []
|
|
184
|
+
require_paths:
|
|
185
|
+
- lib
|
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
|
+
requirements:
|
|
188
|
+
- - ">="
|
|
189
|
+
- !ruby/object:Gem::Version
|
|
190
|
+
version: 2.3.0
|
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
|
+
requirements:
|
|
193
|
+
- - ">="
|
|
194
|
+
- !ruby/object:Gem::Version
|
|
195
|
+
version: '0'
|
|
196
|
+
requirements: []
|
|
197
|
+
rubyforge_project:
|
|
198
|
+
rubygems_version: 2.7.7
|
|
199
|
+
signing_key:
|
|
200
|
+
specification_version: 4
|
|
201
|
+
summary: A resque plugin that ensures job uniqueness at enqueue time.
|
|
202
|
+
test_files: []
|