resque-rate_limited_queue 0.0.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +157 -0
- data/Rakefile +2 -0
- data/lib/resque-rate_limited_queue/version.rb +3 -0
- data/lib/resque/plugins/rate_limited/apis/angellist_queue.rb +19 -0
- data/lib/resque/plugins/rate_limited/apis/base_api_queue.rb +16 -0
- data/lib/resque/plugins/rate_limited/apis/evernote_queue.rb +18 -0
- data/lib/resque/plugins/rate_limited/apis/twitter_queue.rb +19 -0
- data/lib/resque/plugins/rate_limited/rate_limited_queue.rb +80 -0
- data/lib/resque/plugins/rate_limited/rate_limited_un_pause.rb +37 -0
- data/lib/resque/rate_limited_queue.rb +9 -0
- data/resque-rate_limited_queue.gemspec +40 -0
- data/spec/apis/angellist_queue_spec.rb +46 -0
- data/spec/apis/evernote_queue_spec.rb +53 -0
- data/spec/apis/twitter_queue_spec.rb +47 -0
- data/spec/rate_limited_queue_spec.rb +177 -0
- data/spec/rate_limited_un_pause_spec.rb +38 -0
- data/spec/spec_helper.rb +18 -0
- metadata +233 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b71e99ba935500ac4f784badf3fb036ac4cc9d78
|
4
|
+
data.tar.gz: 93fd4bc5c271b4b2761b8df4e133684d22671bfa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ceb160532dbd82f4128b480ed324969a9368193ddecb1c3465a72c088adb798b9fe80ebb2c1ecd163f4cd8a93c308c0dfc482fcd2d4377a5b6360969da807f44
|
7
|
+
data.tar.gz: f71914524346ce7f3c8afd53be98f489d0a05ad29cc11f89130fff68084bdf6495f98b3305cda0089d9d24ad177746482e5158b3c3e1a27839839692525cf0f7
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 pavoni
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# Resque Rate Limited Queue
|
2
|
+
|
3
|
+
PLEASE NOTE THIS IS STILL EXPERIMENTAL - I EXPECT TO RELEASE A STABLE VERSION IN EARLY JAN 2015
|
4
|
+
|
5
|
+
A Resque plugin which makes handling jobs that use rate limited apis easier
|
6
|
+
|
7
|
+
If you have a series of jobs in a queue, this gem will pause the queue when one of the jobs hits a rate limit, and re-start the queue when the rate limit has expired.
|
8
|
+
|
9
|
+
There are two ways to use the gem.
|
10
|
+
|
11
|
+
If the api you are using has a dedicated queue included in the gem (currently Twitter, Angellist and Evernote) then you just need to make some very minor changes to how you queue jobs, and the gem will do the rest.
|
12
|
+
|
13
|
+
If you are using another API, then you need to write a little code that catches the rate limit signal.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'resque-rate-limited-queue'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install resque-rate-limited-queue
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
### Configuration
|
34
|
+
#### Redis
|
35
|
+
The gem uses [redis-mutex](https://github.com/kenn/redis-mutex ) which requires you to register the Redis server: (e.g. in `config/initializers/redis_mutex.rb` for Rails)
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
RedisClassy.redis = Redis.new
|
39
|
+
```
|
40
|
+
Note that Redis Mutex uses the `redis-classy` gem internally.
|
41
|
+
|
42
|
+
#### Un Pause
|
43
|
+
Queues can be unpaused in two ways.
|
44
|
+
|
45
|
+
The most elegant is using [resque-scheduler](https://github.com/resque/resque-scheduler), this works well as long as you aren't running on a platform like heroku which requires a dedicated worker to run the resque-scheduler.
|
46
|
+
|
47
|
+
To tell the gem to use resque-scheduler you need to include resque-scheduler in your Gemfile - and also let the gem know which queue to use to schedule the unpause job (make sure this isn't a queue that could get paused). Put this in an initializer.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
Resque::Plugins::RateLimitedQueue::UnPause.queue = :my_queue
|
51
|
+
```
|
52
|
+
|
53
|
+
Please see the section below on how to unpause on heroku as an alternative. If you don't install resque-scheduler AND configure the queue, then the gem will not schedule unpause jobs this way.
|
54
|
+
|
55
|
+
#### Workers
|
56
|
+
Queues are paused by renaming them, so a resque queue called 'twitter\_api' will be renamed 'twitter\_api\_paused' when it hits a rate limit. Of course this will only work if your resque workers are not also taking jobs from the 'twitter\_api\_paused' queue. So your worker commands need to look like:
|
57
|
+
|
58
|
+
Either
|
59
|
+
```ruby
|
60
|
+
bin/resque work --queues=high,low,twitter_api
|
61
|
+
```
|
62
|
+
or
|
63
|
+
```ruby
|
64
|
+
env QUEUES=high,low,twitter_api bundle exec rake jobs:work
|
65
|
+
```
|
66
|
+
|
67
|
+
NOT
|
68
|
+
```ruby
|
69
|
+
bin/resque work --queues=*
|
70
|
+
```
|
71
|
+
or NOT
|
72
|
+
```ruby
|
73
|
+
env QUEUES=* bundle exec rake jobs:work
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Unpausing on heroku
|
77
|
+
The built in schedler on heroku doesn't support dynamic scheduling from an API, so unless you want to provision an extra worker to run resque-scheduler - the best option is just to unpause all your queues on a regular basis. If they aren't paused this is a harmless no-op. If not enough time has elapsed the jobs will just hit the rate_limit and get paused again. We've found that a hourly 'rake unpause' job seems to work well. The rake task would need to call:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
Resque::Plugins::RateLimitedQueue.TwitterQueue.un_pause
|
81
|
+
Resque::Plugins::RateLimitedQueue.AngellistQueue.un_pause
|
82
|
+
MyQueue.un_pause
|
83
|
+
MyJob.un_pause
|
84
|
+
```
|
85
|
+
### A Pausable job using one of the build-in queues (Twitter, Angellist, Evernote)
|
86
|
+
If you're using the [twitter gem[ (https://github.com/sferik/twitter), this is really simple. Instead of queuing using Resque.enqueue, you just use Resque::Plugins::RateLimitedQueue:TwitterQueue.enqueue.
|
87
|
+
|
88
|
+
Make sure your code in perform doesn't catch the rate_limit exception.
|
89
|
+
|
90
|
+
The TwitterQueue will catch the exception and pause the queue (as well as re-scheduling the jobs and scheduling an un pause (if you are using resque-scheduler)). Any jobs you add while the queue is paused will be added to the paused queue
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class TwitterJob
|
94
|
+
class << self
|
95
|
+
def refresh(handle)
|
96
|
+
Resque::Plugins::RateLimitedQueue:TwitterQueue.enqueue(TwitterJob, handle)
|
97
|
+
end
|
98
|
+
|
99
|
+
def perform(handle)
|
100
|
+
do_something_with_the_twitter_gem(handle)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
### A single class of pausable job using a new api
|
107
|
+
If you only have one class of job you want to queue using the api, then you can use the PauseQueue module directly
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class MyApiJob
|
111
|
+
extend Resque::Plugins::RateLimitedQueue
|
112
|
+
@queue = :my_api
|
113
|
+
WAIT_TIME = 60*60
|
114
|
+
|
115
|
+
def self.perform(*params)
|
116
|
+
do_api_stuff
|
117
|
+
rescue MyApiRateLimit
|
118
|
+
pause_for(Time.now + WAIT_TIME, name)
|
119
|
+
rate_limited_requeue(self, *params)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.enqueue(*params)
|
123
|
+
rate_limited_enqueue(self, *params)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
````
|
127
|
+
|
128
|
+
### Multiple classes of pausable job using a new api
|
129
|
+
If you have more than one class of job you want to queue to the api, then you can need to add another Queue class. This isn't hard
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class MyApiQueue < Resque::Plugins::RateLimitedQueue::BaseApiQueue
|
133
|
+
@queue = :my_api
|
134
|
+
WAIT_TIME = 60*60
|
135
|
+
|
136
|
+
def self.perform(klass, *params)
|
137
|
+
super
|
138
|
+
rescue MyApiRateLimit
|
139
|
+
pause_for(Time.now + WAIT_TIME, name)
|
140
|
+
rate_limited_requeue(self, klass, *params)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
````
|
144
|
+
If you do this - please contribute - and I'll add to the gem.
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
1. Fork it ( https://github.com/[my-github-username]/resque_rate_limited_queue/fork )
|
149
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
150
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
151
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
152
|
+
5. Create a new Pull Request
|
153
|
+
|
154
|
+
## Final thoughts
|
155
|
+
Thanks to [Dominic](https://github.com/dominicsayers) for idea of renaming the redis key - and the sample code that does this.
|
156
|
+
|
157
|
+
This is my first gem - so please forgive any errors - and feedback very welcome
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'angellist_api'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimitedQueue
|
6
|
+
class AngellistQueue < BaseApiQueue
|
7
|
+
WAIT_TIME = 60
|
8
|
+
@queue = :angellist_api
|
9
|
+
|
10
|
+
def self.perform(klass, *params)
|
11
|
+
super
|
12
|
+
rescue AngellistApi::Error::TooManyRequests
|
13
|
+
pause_for(Time.now + (60 * 60))
|
14
|
+
rate_limited_requeue(self, klass, *params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module RateLimitedQueue
|
4
|
+
class BaseApiQueue
|
5
|
+
extend Resque::Plugins::RateLimitedQueue
|
6
|
+
def self.perform(klass, *params)
|
7
|
+
find_class(klass).perform(*params)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.enqueue(klass, *params)
|
11
|
+
rate_limited_enqueue(self, klass.to_s, *params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'evernote-thrift'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimitedQueue
|
6
|
+
class EvernoteQueue < BaseApiQueue
|
7
|
+
@queue = :evernote_api
|
8
|
+
|
9
|
+
def self.perform(klass, *params)
|
10
|
+
super
|
11
|
+
rescue Evernote::EDAM::Error::EDAMSystemException => e
|
12
|
+
pause_for(Time.now + 60 * e.rateLimitDuration.seconds)
|
13
|
+
rate_limited_requeue(self, klass, *params)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'twitter'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimitedQueue
|
6
|
+
class TwitterQueue < BaseApiQueue
|
7
|
+
@queue = :twitter_api
|
8
|
+
|
9
|
+
def self.perform(klass, *params)
|
10
|
+
super
|
11
|
+
rescue Twitter::Error::TooManyRequests,
|
12
|
+
Twitter::Error::EnhanceYourCalm => e
|
13
|
+
pause_for(Time.now + e.rate_limit.reset_in)
|
14
|
+
rate_limited_requeue(self, klass, *params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module RateLimitedQueue
|
4
|
+
RESQUE_PREFIX = 'queue:'
|
5
|
+
MUTEX = 'Resque::Plugins::RateLimitedQueue'
|
6
|
+
|
7
|
+
def around_perform_with_check_and_requeue(*params)
|
8
|
+
paused = false
|
9
|
+
with_lock do
|
10
|
+
paused = paused?
|
11
|
+
Resque.enqueue_to(paused_queue_name, self, *params) if paused
|
12
|
+
end
|
13
|
+
return if paused
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
|
17
|
+
def rate_limited_enqueue(klass, *params)
|
18
|
+
with_lock do
|
19
|
+
if paused?
|
20
|
+
Resque.enqueue_to(paused_queue_name, klass, *params)
|
21
|
+
else
|
22
|
+
Resque.enqueue_to(@queue, klass, *params)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rate_limited_requeue(klass, *params)
|
28
|
+
# split from above to make it easy to stub for testing
|
29
|
+
rate_limited_enqueue(klass, *params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pause_for(timestamp)
|
33
|
+
pause
|
34
|
+
UnPause.enqueue(timestamp, name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def un_pause
|
38
|
+
with_lock do
|
39
|
+
if paused?
|
40
|
+
begin
|
41
|
+
Resque.redis.renamenx(RESQUE_PREFIX + paused_queue_name, RESQUE_PREFIX + @queue.to_s)
|
42
|
+
rescue Redis::CommandError => e
|
43
|
+
raise unless e.message == 'ERR no such key'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pause
|
50
|
+
Resque.redis.renamenx(RESQUE_PREFIX + @queue.to_s, RESQUE_PREFIX + paused_queue_name)
|
51
|
+
rescue Redis::CommandError => e
|
52
|
+
raise unless e.message == 'ERR no such key'
|
53
|
+
end
|
54
|
+
|
55
|
+
def paused?
|
56
|
+
Resque.redis.exists(RESQUE_PREFIX + paused_queue_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def paused_queue_name
|
60
|
+
@queue.to_s + '_paused'
|
61
|
+
end
|
62
|
+
|
63
|
+
def with_lock
|
64
|
+
if Resque.inline
|
65
|
+
yield
|
66
|
+
else
|
67
|
+
RedisMutex.with_lock(MUTEX, block: 60, expire: 120) { yield }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_class(klass)
|
72
|
+
return klass if klass.is_a? Class
|
73
|
+
return Object.const_get(klass) unless klass.include?('::')
|
74
|
+
klass.split('::').reduce(Object) do |mod, class_name|
|
75
|
+
mod.const_get(class_name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module RateLimitedQueue
|
4
|
+
class UnPause
|
5
|
+
@queue = nil
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_writer(:queue)
|
9
|
+
|
10
|
+
def use?
|
11
|
+
Resque.respond_to?(:enqueue_at_with_queue) && @queue
|
12
|
+
end
|
13
|
+
|
14
|
+
def enqueue(timestamp, klass)
|
15
|
+
# If Resque scheduler is installed and queue is set - use it to queue a wake up job
|
16
|
+
return unless use?
|
17
|
+
Resque.enqueue_at_with_queue(
|
18
|
+
@queue,
|
19
|
+
timestamp,
|
20
|
+
Resque::Plugins::RateLimitedQueue::UnPause,
|
21
|
+
klass)
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform(klass)
|
25
|
+
class_from_string(klass.to_s).un_pause
|
26
|
+
end
|
27
|
+
|
28
|
+
def class_from_string(str)
|
29
|
+
str.split('::').reduce(Object) do |mod, class_name|
|
30
|
+
mod.const_get(class_name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'resque'
|
2
|
+
require 'redis-mutex'
|
3
|
+
require 'resque/version'
|
4
|
+
require 'resque/plugins/rate_limited/rate_limited_queue'
|
5
|
+
require 'resque/plugins/rate_limited/rate_limited_un_pause'
|
6
|
+
require 'resque/plugins/rate_limited/apis/base_api_queue'
|
7
|
+
require 'resque/plugins/rate_limited/apis/angellist_queue'
|
8
|
+
require 'resque/plugins/rate_limited/apis/evernote_queue'
|
9
|
+
require 'resque/plugins/rate_limited/apis/twitter_queue'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'resque-rate_limited_queue/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "resque-rate_limited_queue"
|
8
|
+
spec.version = RateLimitedQueue::VERSION
|
9
|
+
spec.authors = ["Greg Dowling"]
|
10
|
+
spec.email = ["mail@greddowling.com"]
|
11
|
+
spec.summary = %q{A Resque plugin to help manage jobs that use rate limited apis, pausing when you hit the limits and restarting later.}
|
12
|
+
spec.description = %q{A Resque plugin which allows you to create dedicated queues for jobs that use rate limited apis.
|
13
|
+
These queues will pause when one of the jobs hits a rate limit, and unpause after a suitable time period.
|
14
|
+
The rate_limited_queue can be used directly, and just requires catching the rate limit exception and pausing the
|
15
|
+
queue. There are also additional queues provided that already include the pause/rety logic for twitter, angelist
|
16
|
+
and evernote; these allow you to support rate limited apis with minimal changes.}
|
17
|
+
|
18
|
+
spec.homepage = ""
|
19
|
+
spec.license = "MIT"
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0")
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency('resque', '~> 1.9', '>= 1.9.10')
|
27
|
+
spec.add_dependency('redis-mutex','~> 4.0', '>= 4.0.0')
|
28
|
+
|
29
|
+
spec.add_dependency("angellist_api", '~> 1.0', '>= 1.0.7')
|
30
|
+
spec.add_dependency("evernote-thrift", '~> 1.25', '>= 1.25.1')
|
31
|
+
spec.add_dependency("twitter", '~> 5.11', '>= 5.11.0')
|
32
|
+
|
33
|
+
|
34
|
+
spec.add_development_dependency("bundler", "~> 1.7")
|
35
|
+
spec.add_development_dependency("rake", "~> 10.0")
|
36
|
+
spec.add_development_dependency("rspec", "~> 2.6")
|
37
|
+
spec.add_development_dependency("simplecov", '~> 0.9.1')
|
38
|
+
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque_rate_limited_queue'
|
3
|
+
|
4
|
+
class RateLimitedTestQueueAL
|
5
|
+
def self.perform(succeed)
|
6
|
+
raise(AngellistApi::Error::TooManyRequests, 'error') unless succeed
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Resque::Plugins::RateLimitedQueue::AngellistQueue do
|
11
|
+
describe 'enqueue' do
|
12
|
+
it 'enqueues to the correct queue with the correct parameters' do
|
13
|
+
Resque.should_receive(:enqueue_to).with(
|
14
|
+
:angellist_api,
|
15
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue,
|
16
|
+
RateLimitedTestQueueAL.to_s,
|
17
|
+
true)
|
18
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue
|
19
|
+
.enqueue(RateLimitedTestQueueAL, true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'perform' do
|
24
|
+
before do
|
25
|
+
Resque.inline = true
|
26
|
+
end
|
27
|
+
context 'with everything' do
|
28
|
+
it 'calls the class with the right parameters' do
|
29
|
+
RateLimitedTestQueueAL.should_receive(:perform).with('test_param')
|
30
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue
|
31
|
+
.enqueue(RateLimitedTestQueueAL, 'test_param')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with rate limit exception' do
|
36
|
+
before do
|
37
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue.stub(:rate_limited_requeue)
|
38
|
+
end
|
39
|
+
it 'pauses queue when request fails' do
|
40
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue.should_receive(:pause_for)
|
41
|
+
Resque::Plugins::RateLimitedQueue::AngellistQueue
|
42
|
+
.enqueue(RateLimitedTestQueueAL, false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque_rate_limited_queue'
|
3
|
+
|
4
|
+
class RateLimitDuration
|
5
|
+
def self.seconds
|
6
|
+
60
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class RateLimitedTestQueueEn
|
11
|
+
def self.perform(succeed)
|
12
|
+
raise(Evernote::EDAM::Error::EDAMSystemException, rateLimitDuration: RateLimitDuration) unless succeed
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Resque::Plugins::RateLimitedQueue::EvernoteQueue do
|
17
|
+
describe 'enqueue' do
|
18
|
+
it 'enqueues to the correct queue with the correct parameters' do
|
19
|
+
Resque.should_receive(:enqueue_to).with(
|
20
|
+
:evernote_api,
|
21
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue,
|
22
|
+
RateLimitedTestQueueEn.to_s,
|
23
|
+
true)
|
24
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue
|
25
|
+
.enqueue(RateLimitedTestQueueEn, true)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'perform' do
|
30
|
+
before do
|
31
|
+
Resque.inline = true
|
32
|
+
end
|
33
|
+
context 'with everything' do
|
34
|
+
it 'calls the class with the right parameters' do
|
35
|
+
RateLimitedTestQueueEn.should_receive(:perform).with('test_param')
|
36
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue
|
37
|
+
.enqueue(RateLimitedTestQueueEn, 'test_param')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with rate limit exception' do
|
42
|
+
before do
|
43
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue.stub(:rate_limited_requeue)
|
44
|
+
end
|
45
|
+
it 'pauses queue when request fails' do
|
46
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue.should_receive(:pause_for)
|
47
|
+
Resque::Plugins::RateLimitedQueue::EvernoteQueue
|
48
|
+
.enqueue(RateLimitedTestQueueEn, false)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque_rate_limited_queue'
|
3
|
+
|
4
|
+
class RateLimitedTestQueueTw
|
5
|
+
def self.perform(succeed)
|
6
|
+
raise(Twitter::Error::TooManyRequests
|
7
|
+
.new('', 'x-rate-limit-reset' => (Time.now + 60).to_i)) unless succeed
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Resque::Plugins::RateLimitedQueue::TwitterQueue do
|
12
|
+
describe 'enqueue' do
|
13
|
+
it 'enqueues to the correct queue with the correct parameters' do
|
14
|
+
Resque.should_receive(:enqueue_to).with(
|
15
|
+
:twitter_api,
|
16
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue,
|
17
|
+
RateLimitedTestQueueTw.to_s,
|
18
|
+
true)
|
19
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue
|
20
|
+
.enqueue(RateLimitedTestQueueTw, true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'perform' do
|
25
|
+
before do
|
26
|
+
Resque.inline = true
|
27
|
+
end
|
28
|
+
context 'with everything' do
|
29
|
+
it 'calls the class with the right parameters' do
|
30
|
+
RateLimitedTestQueueTw.should_receive(:perform).with('test_param')
|
31
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue
|
32
|
+
.enqueue(RateLimitedTestQueueTw, 'test_param')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with rate limit exception' do
|
37
|
+
before do
|
38
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue.stub(:rate_limited_requeue)
|
39
|
+
end
|
40
|
+
it 'pauses queue when request fails' do
|
41
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue.should_receive(:pause_for)
|
42
|
+
Resque::Plugins::RateLimitedQueue::TwitterQueue
|
43
|
+
.enqueue(RateLimitedTestQueueTw, false)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque_rate_limited_queue'
|
3
|
+
|
4
|
+
class RateLimitedTestQueue
|
5
|
+
extend Resque::Plugins::RateLimitedQueue
|
6
|
+
|
7
|
+
@queue = :test
|
8
|
+
|
9
|
+
def self.perform(succeed)
|
10
|
+
rate_limited_requeue(self, succeed) unless succeed
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.queue_name
|
14
|
+
@queue.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.queue
|
18
|
+
@queue
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Resque::Plugins::RateLimitedQueue do
|
23
|
+
it 'should be compliance with Resque::Plugin document' do
|
24
|
+
expect { Resque::Plugin.lint(Resque::Plugins::RateLimitedQueue) }.to_not raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
shared_examples_for 'queue' do |queue_suffix|
|
28
|
+
it 'should queue to the correct queue' do
|
29
|
+
queue_param = queue_suffix.empty? ? RateLimitedTestQueue.queue : "#{RateLimitedTestQueue.queue_name}#{queue_suffix}"
|
30
|
+
Resque.should_receive(:enqueue_to).with(queue_param, nil, nil)
|
31
|
+
RateLimitedTestQueue.rate_limited_enqueue(nil, nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when queue is not paused' do
|
36
|
+
before do
|
37
|
+
RateLimitedTestQueue.stub(:paused?).and_return(false)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'enqueue' do
|
41
|
+
include_examples 'queue', ''
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'paused?' do
|
45
|
+
it { RateLimitedTestQueue.paused?.should be false }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'perform' do
|
49
|
+
it 'should requeue the job on failure' do
|
50
|
+
Resque.should_receive(:enqueue_to)
|
51
|
+
RateLimitedTestQueue.perform(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should not requeue the job on success' do
|
55
|
+
Resque.should_not_receive(:enqueue_to)
|
56
|
+
RateLimitedTestQueue.perform(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'pause' do
|
62
|
+
it 'should rename the queue to paused' do
|
63
|
+
Resque.redis.should_receive(:renamenx).with("queue:#{RateLimitedTestQueue.queue_name}", "queue:#{RateLimitedTestQueue.queue_name}_paused")
|
64
|
+
RateLimitedTestQueue.pause
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'un_pause' do
|
69
|
+
it 'should not unpause the queue' do
|
70
|
+
Resque.redis.should_not_receive(:renamenx).with("queue:#{RateLimitedTestQueue.queue_name}", "queue:#{RateLimitedTestQueue.queue_name}_paused")
|
71
|
+
RateLimitedTestQueue.un_pause
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'pause_for' do
|
76
|
+
before do
|
77
|
+
Resque.redis.stub(:renamenx).and_return(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should pause the queue' do
|
81
|
+
RateLimitedTestQueue.should_receive(:pause)
|
82
|
+
RateLimitedTestQueue.pause_for(Time.now + (5 * 60 * 60))
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should schedule an unpause job' do
|
86
|
+
Resque::Plugins::RateLimitedQueue::UnPause.should_receive(:enqueue)
|
87
|
+
.with(nil, 'RateLimitedTestQueue')
|
88
|
+
RateLimitedTestQueue.pause_for(nil)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when queue is paused' do
|
94
|
+
before do
|
95
|
+
RateLimitedTestQueue.stub(:paused?).and_return(true)
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'enqueue' do
|
99
|
+
include_examples 'queue', '_paused'
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'paused?' do
|
103
|
+
it { RateLimitedTestQueue.paused?.should be true }
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'perform' do
|
107
|
+
it 'should not execute the block' do
|
108
|
+
Resque.should_receive(:enqueue_to).with("#{RateLimitedTestQueue.queue_name}_paused", RateLimitedTestQueue, true)
|
109
|
+
RateLimitedTestQueue.should_not_receive(:perform)
|
110
|
+
RateLimitedTestQueue.around_perform_with_check_and_requeue(true)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'un_pause' do
|
115
|
+
it 'should rename the queue to live' do
|
116
|
+
Resque.redis.should_receive(:renamenx).with("queue:#{RateLimitedTestQueue.queue_name}_paused", "queue:#{RateLimitedTestQueue.queue_name}")
|
117
|
+
RateLimitedTestQueue.un_pause
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'find_class' do
|
123
|
+
it 'works with symbol' do
|
124
|
+
RateLimitedTestQueue.find_class(RateLimitedTestQueue).should eq RateLimitedTestQueue
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'works with simple string' do
|
128
|
+
RateLimitedTestQueue.find_class('RateLimitedTestQueue').should eq RateLimitedTestQueue
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'works with complex string' do
|
132
|
+
RateLimitedTestQueue.find_class('Resque::Plugins::RateLimitedQueue').should eq Resque::Plugins::RateLimitedQueue
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with redis errors' do
|
137
|
+
before do
|
138
|
+
RateLimitedTestQueue.stub(:paused?).and_return(true)
|
139
|
+
end
|
140
|
+
context 'with not found error' do
|
141
|
+
before do
|
142
|
+
Resque.redis.stub(:renamenx).and_raise(Redis::CommandError.new('ERR no such key'))
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'pause' do
|
146
|
+
it 'should not throw exception' do
|
147
|
+
expect { RateLimitedTestQueue.pause }.to_not raise_error
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe 'un_pause' do
|
152
|
+
it 'should not throw exception' do
|
153
|
+
expect { RateLimitedTestQueue.un_pause }.to_not raise_error
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'with other errror' do
|
160
|
+
before do
|
161
|
+
Resque.redis.stub(:renamenx).and_raise(Redis::CommandError.new('ERR something else'))
|
162
|
+
end
|
163
|
+
|
164
|
+
describe 'pause' do
|
165
|
+
it 'should throw exception' do
|
166
|
+
expect { RateLimitedTestQueue.pause }.to raise_error(Redis::CommandError)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe 'un_pause' do
|
171
|
+
it 'should throw exception' do
|
172
|
+
expect { RateLimitedTestQueue.un_pause }.to raise_error(Redis::CommandError)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque_rate_limited_queue'
|
3
|
+
|
4
|
+
class RateLimitedTestQueue
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Resque::Plugins::RateLimitedQueue::UnPause do
|
8
|
+
describe 'perform' do
|
9
|
+
it 'unpauses the queue' do
|
10
|
+
RateLimitedTestQueue.should_receive(:un_pause)
|
11
|
+
Resque::Plugins::RateLimitedQueue::UnPause.perform(RateLimitedTestQueue)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'enqueue' do
|
16
|
+
before { Resque.stub(:respond_to?).and_return(true) }
|
17
|
+
context 'with no queue defined' do
|
18
|
+
it 'does not queue the job' do
|
19
|
+
Resque.should_not_receive(:enqueue_at_with_queue)
|
20
|
+
Resque::Plugins::RateLimitedQueue::UnPause.enqueue(Time.now, RateLimitedTestQueue)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with queue defined' do
|
25
|
+
before { Resque::Plugins::RateLimitedQueue::UnPause.queue = :queue_name }
|
26
|
+
it 'queues the job' do
|
27
|
+
Resque.should_receive(:enqueue_at_with_queue).with(
|
28
|
+
:queue_name,
|
29
|
+
nil,
|
30
|
+
Resque::Plugins::RateLimitedQueue::UnPause,
|
31
|
+
RateLimitedTestQueue)
|
32
|
+
|
33
|
+
Resque::Plugins::RateLimitedQueue::UnPause.enqueue(nil, RateLimitedTestQueue)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'redis-classy'
|
6
|
+
require 'redis-mutex'
|
7
|
+
|
8
|
+
require 'simplecov'
|
9
|
+
|
10
|
+
SimpleCov.start
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
# Use database 15 for testing so we don't accidentally step on your real data.
|
14
|
+
RedisClassy.redis = Redis.new(db: 15)
|
15
|
+
unless RedisClassy.keys.empty?
|
16
|
+
abort '[ERROR]: Redis database 15 not empty! If you are sure, run "rake flushdb" beforehand.'
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-rate_limited_queue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.30
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Greg Dowling
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-28 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.9'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.9.10
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.9'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.9.10
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: redis-mutex
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '4.0'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 4.0.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '4.0'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 4.0.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: angellist_api
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.0'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.0.7
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.0'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.0.7
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: evernote-thrift
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '1.25'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.25.1
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.25'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.25.1
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: twitter
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '5.11'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 5.11.0
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '5.11'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 5.11.0
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: bundler
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '1.7'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - "~>"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '1.7'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: rake
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - "~>"
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '10.0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - "~>"
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '10.0'
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: rspec
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - "~>"
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '2.6'
|
148
|
+
type: :development
|
149
|
+
prerelease: false
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - "~>"
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '2.6'
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: simplecov
|
157
|
+
requirement: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - "~>"
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: 0.9.1
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - "~>"
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 0.9.1
|
169
|
+
description: |-
|
170
|
+
A Resque plugin which allows you to create dedicated queues for jobs that use rate limited apis.
|
171
|
+
These queues will pause when one of the jobs hits a rate limit, and unpause after a suitable time period.
|
172
|
+
The rate_limited_queue can be used directly, and just requires catching the rate limit exception and pausing the
|
173
|
+
queue. There are also additional queues provided that already include the pause/rety logic for twitter, angelist
|
174
|
+
and evernote; these allow you to support rate limited apis with minimal changes.
|
175
|
+
email:
|
176
|
+
- mail@greddowling.com
|
177
|
+
executables: []
|
178
|
+
extensions: []
|
179
|
+
extra_rdoc_files: []
|
180
|
+
files:
|
181
|
+
- ".gitignore"
|
182
|
+
- ".rspec"
|
183
|
+
- Gemfile
|
184
|
+
- LICENSE.txt
|
185
|
+
- README.md
|
186
|
+
- Rakefile
|
187
|
+
- lib/resque-rate_limited_queue/version.rb
|
188
|
+
- lib/resque/plugins/rate_limited/apis/angellist_queue.rb
|
189
|
+
- lib/resque/plugins/rate_limited/apis/base_api_queue.rb
|
190
|
+
- lib/resque/plugins/rate_limited/apis/evernote_queue.rb
|
191
|
+
- lib/resque/plugins/rate_limited/apis/twitter_queue.rb
|
192
|
+
- lib/resque/plugins/rate_limited/rate_limited_queue.rb
|
193
|
+
- lib/resque/plugins/rate_limited/rate_limited_un_pause.rb
|
194
|
+
- lib/resque/rate_limited_queue.rb
|
195
|
+
- resque-rate_limited_queue.gemspec
|
196
|
+
- spec/apis/angellist_queue_spec.rb
|
197
|
+
- spec/apis/evernote_queue_spec.rb
|
198
|
+
- spec/apis/twitter_queue_spec.rb
|
199
|
+
- spec/rate_limited_queue_spec.rb
|
200
|
+
- spec/rate_limited_un_pause_spec.rb
|
201
|
+
- spec/spec_helper.rb
|
202
|
+
homepage: ''
|
203
|
+
licenses:
|
204
|
+
- MIT
|
205
|
+
metadata: {}
|
206
|
+
post_install_message:
|
207
|
+
rdoc_options: []
|
208
|
+
require_paths:
|
209
|
+
- lib
|
210
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
216
|
+
requirements:
|
217
|
+
- - ">="
|
218
|
+
- !ruby/object:Gem::Version
|
219
|
+
version: '0'
|
220
|
+
requirements: []
|
221
|
+
rubyforge_project:
|
222
|
+
rubygems_version: 2.4.2
|
223
|
+
signing_key:
|
224
|
+
specification_version: 4
|
225
|
+
summary: A Resque plugin to help manage jobs that use rate limited apis, pausing when
|
226
|
+
you hit the limits and restarting later.
|
227
|
+
test_files:
|
228
|
+
- spec/apis/angellist_queue_spec.rb
|
229
|
+
- spec/apis/evernote_queue_spec.rb
|
230
|
+
- spec/apis/twitter_queue_spec.rb
|
231
|
+
- spec/rate_limited_queue_spec.rb
|
232
|
+
- spec/rate_limited_un_pause_spec.rb
|
233
|
+
- spec/spec_helper.rb
|