resque-throttler 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6cc4d7ea16ce3914cd16f8c6bdc42bb43d391d3d
4
+ data.tar.gz: 1315df0567f36ee68e14b4daf16f756552c871bf
5
+ SHA512:
6
+ metadata.gz: 03d5c6177b891b30cab5b4ebe9b5e31de08db32790aa2c9c132fcac835c55a8734a843b31720f915536142d73e6ed620e027fa08138d3e428188df7f7677c0e1
7
+ data.tar.gz: c1b0db2f23672b7e7566c64199dca70364ffc48342ed1922984c1739993a77940e79062e416c581f0ecc9f24d4913287045baea7f26f384097f3ab408751fc61
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sunstone.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ resque-throttler (0.1.0)
5
+ resque (~> 1.25)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.1.4)
11
+ i18n (~> 0.6, >= 0.6.9)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 1.1)
16
+ ansi (1.4.3)
17
+ builder (3.2.2)
18
+ i18n (0.6.11)
19
+ json (1.8.1)
20
+ metaclass (0.0.4)
21
+ minitest (5.4.0)
22
+ minitest-reporters (1.0.4)
23
+ ansi
24
+ builder
25
+ minitest (>= 5.0)
26
+ ruby-progressbar
27
+ mocha (1.1.0)
28
+ metaclass (~> 0.0.1)
29
+ mono_logger (1.1.0)
30
+ multi_json (1.10.1)
31
+ rack (1.5.2)
32
+ rack-protection (1.5.3)
33
+ rack
34
+ rake (10.3.2)
35
+ redis (3.0.7)
36
+ redis-namespace (1.5.0)
37
+ redis (~> 3.0, >= 3.0.4)
38
+ resque (1.25.2)
39
+ mono_logger (~> 1.0)
40
+ multi_json (~> 1.0)
41
+ redis-namespace (~> 1.3)
42
+ sinatra (>= 0.9.2)
43
+ vegas (~> 0.1.2)
44
+ ruby-progressbar (1.5.1)
45
+ sinatra (1.4.5)
46
+ rack (~> 1.4)
47
+ rack-protection (~> 1.4)
48
+ tilt (~> 1.3, >= 1.3.4)
49
+ thread_safe (0.3.4)
50
+ tilt (1.4.1)
51
+ tzinfo (1.2.1)
52
+ thread_safe (~> 0.1)
53
+ vegas (0.1.11)
54
+ rack (>= 1.0.0)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ activesupport
61
+ bundler
62
+ minitest
63
+ minitest-reporters
64
+ mocha
65
+ rake
66
+ resque-throttler!
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Jon Bracy
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ Resque Throttler
2
+ ================
3
+
4
+ Resque Throttler allows you to throttle the rate at which jobs are performed
5
+ on a specific queue.
6
+
7
+
8
+ Installation
9
+ ============
10
+
11
+ ```ruby
12
+ require 'resque/throttler'
13
+ ```
14
+
15
+ Or in a Gemfile:
16
+
17
+ ```ruby
18
+ require 'resque-throttler', :require => 'resque/throttler'
19
+ ```
20
+
21
+ Usage
22
+ =====
23
+
24
+ ```ruby
25
+ require 'resque'
26
+ require 'resque/throttler'
27
+
28
+ # Rate limit at 10 jobs from `my_queue` per minute
29
+ Resque.rate_limit(:my_queue, :at => 10, :per => 60)
30
+ ```
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'bundler/setup'
2
+ require "bundler/gem_tasks"
3
+ Bundler.require(:development)
4
+ require 'rake/testtask'
5
+ require 'rdoc/task'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'lib' << 'test'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ #t.warning = true
11
+ #t.verbose = true
12
+ end
13
+
14
+ Rake::RDocTask.new do |rd|
15
+ rd.main = 'README.md'
16
+ rd.title = 'Sunstone Documentation'
17
+ rd.rdoc_dir = 'doc'
18
+
19
+ rd.options << '-f' << 'sdoc'
20
+ rd.options << '-T' << '42floors'
21
+ rd.options << '-g' # Generate github links
22
+
23
+ rd.rdoc_files.include('README.rdoc')
24
+ rd.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ desc "Run tests"
28
+ task :default => :test
29
+
30
+ namespace :pages do
31
+ #TODO: https://github.com/defunkt/sdoc-helpers/blob/master/lib/sdoc_helpers/pages.rb
32
+ end
@@ -0,0 +1,86 @@
1
+ require 'resque'
2
+ require 'securerandom'
3
+
4
+ module Resque::Plugins
5
+ module Throttler
6
+ extend self
7
+
8
+ def self.extended(other)
9
+ other.instance_variable_set(:@rate_limits, {})
10
+ end
11
+
12
+ def pop(queue)
13
+ if queue_at_or_over_rate_limit?(queue)
14
+ gc_rate_limit_data_for_queue(queue)
15
+ nil
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ def rate_limit(queue, options={})
22
+ if options.keys.sort != [:at, :per]
23
+ raise ArgumentError.new("Mising either :at or :per in options")
24
+ end
25
+
26
+ @rate_limits[queue.to_s] = options
27
+ end
28
+
29
+ def rate_limit_for(queue)
30
+ @rate_limits[queue.to_s]
31
+ end
32
+
33
+ def queue_rate_limited?(queue)
34
+ @rate_limits[queue.to_s]
35
+ end
36
+
37
+ def queue_at_or_over_rate_limit?(queue)
38
+ if queue_rate_limited?(queue)
39
+ redis.scard("throttler:#{queue}_uuids") >= rate_limit_for(queue)[:at]
40
+ else
41
+ false
42
+ end
43
+ end
44
+
45
+ def gc_rate_limit_data_for_queue(queue)
46
+ return unless queue_rate_limited?(queue)
47
+
48
+ limit = rate_limit_for(queue)
49
+ queue_key = "throttler:#{queue}_uuids"
50
+ uuids = redis.smembers(queue_key)
51
+
52
+ uuids.each do |uuid|
53
+ job_ended_at = redis.hmget("throttler:jobs:#{uuid}", "ended_at")[0]
54
+ if job_ended_at && Time.at(job_ended_at.to_i) < Time.now - limit[:per]
55
+ redis.srem(queue_key, uuid)
56
+ redis.del("throttler:jobs:#{uuid}")
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ Resque.extend(Resque::Plugins::Throttler)
65
+
66
+ class Resque::Job
67
+
68
+ def perform_with_throttler
69
+ if Resque.queue_rate_limited?(self.queue)
70
+ uuid = SecureRandom.uuid
71
+ begin
72
+ # TODO this needs to be wrapped in a transcation
73
+ redis.hmset("throttler:jobs:#{uuid}", "started_at", Time.now.to_i)
74
+ redis.sadd("throttler:#{queue}_uuids", uuid)
75
+ perform_without_throttler
76
+ ensure
77
+ redis.hmset("throttler:jobs:#{uuid}", "ended_at", Time.now.to_i)
78
+ end
79
+ else
80
+ perform_without_throttler
81
+ end
82
+ end
83
+ alias_method :perform_without_throttler, :perform
84
+ alias_method :perform, :perform_with_throttler
85
+
86
+ end
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "resque-throttler"
3
+ s.version = '0.1.1'
4
+ s.licenses = ['MIT']
5
+ s.authors = ["Jon Bracy"]
6
+ s.email = ["jonbracy@gmail.com"]
7
+ s.homepage = "https://github.com/malomalo/resque-throttler"
8
+ s.summary = %q{Rate limit Resque Jobs}
9
+ s.description = %q{Rate limit how many times a job can be run from a queue}
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.extensions = []
15
+ s.require_paths = ["lib"]
16
+ #s.extra_rdoc_files = ["LICENSE", "README.md"]
17
+
18
+ # Developoment
19
+ s.add_development_dependency 'rake'
20
+ #s.add_development_dependency 'rdoc'
21
+ #s.add_development_dependency 'sdoc'
22
+ s.add_development_dependency 'bundler'
23
+ s.add_development_dependency 'activesupport'
24
+ s.add_development_dependency 'minitest'
25
+ s.add_development_dependency 'minitest-reporters'
26
+ s.add_development_dependency 'mocha'
27
+ #s.add_development_dependency 'sdoc-templates-42floors'
28
+
29
+ # Runtime
30
+ s.add_runtime_dependency 'resque', '~> 1.25'
31
+ end
@@ -0,0 +1,77 @@
1
+ require 'test_helper'
2
+
3
+ class MyJob
4
+ def self.perform
5
+ end
6
+ end
7
+
8
+ class MyErrorJob
9
+ def self.perform
10
+ raise ArgumentError
11
+ end
12
+ end
13
+
14
+ class Resque::JobTest < Minitest::Test
15
+
16
+ def setup
17
+ Resque.instance_variable_set(:@rate_limits, {})
18
+ end
19
+
20
+ test "Resque::Job::perform on unthrottled job" do
21
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
22
+
23
+ job = Resque::Job.new(:other_queue, {
24
+ 'class' => 'MyJob',
25
+ 'args' => []
26
+ })
27
+
28
+ travel_to Time.now do
29
+ SecureRandom.expects(:uuid).returns("jobuuid").never
30
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "started_at", Time.now.to_i).never
31
+ Resque.redis.expects(:sadd).with("throttler:myqueue_uuids", "jobuuid").never
32
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "ended_at", Time.now.to_i).never
33
+
34
+ job.perform
35
+ end
36
+ end
37
+
38
+ test "Resque::Job::perform on throttled job" do
39
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
40
+
41
+ job = Resque::Job.new(:myqueue, {
42
+ 'class' => 'MyJob',
43
+ 'args' => []
44
+ })
45
+
46
+ travel_to Time.now do
47
+ SecureRandom.expects(:uuid).returns("jobuuid")
48
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "started_at", Time.now.to_i).once
49
+ Resque.redis.expects(:sadd).with("throttler:myqueue_uuids", "jobuuid").once
50
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "ended_at", Time.now.to_i).once
51
+
52
+ job.perform
53
+ end
54
+ end
55
+
56
+ test "Resque::Job::perform on throttled job with job that throws error" do
57
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
58
+
59
+ job = Resque::Job.new('myqueue', {
60
+ 'class' => 'MyErrorJob',
61
+ 'args' => []
62
+ })
63
+
64
+ travel_to Time.now do
65
+ SecureRandom.expects(:uuid).returns("jobuuid")
66
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "started_at", Time.now.to_i).once
67
+ Resque.redis.expects(:sadd).with("throttler:myqueue_uuids", "jobuuid").once
68
+ Resque.redis.expects(:hmset).with("throttler:jobs:jobuuid", "ended_at", Time.now.to_i).once
69
+
70
+ assert_raises(ArgumentError) {
71
+ job.perform
72
+ }
73
+ end
74
+ end
75
+
76
+ end
77
+
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+
3
+ class ResqueTest < Minitest::Test
4
+
5
+ def setup
6
+ Resque.instance_variable_set(:@rate_limits, {})
7
+ end
8
+
9
+ test "Resque::rate_limit" do
10
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
11
+
12
+ assert_equal Resque.instance_variable_get(:@rate_limits), {
13
+ 'myqueue' => {:at => 10, :per => 1}
14
+ }
15
+ end
16
+
17
+ test "Resque::queue_rate_limited?" do
18
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
19
+
20
+ assert Resque.queue_rate_limited?(:myqueue)
21
+ assert Resque.queue_rate_limited?("myqueue")
22
+ end
23
+
24
+ test "Resque::queue_at_or_over_rate_limit?" do
25
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
26
+
27
+ Resque.redis.expects(:scard).with("throttler:myqueue_uuids").returns(5).twice
28
+ assert !Resque.queue_at_or_over_rate_limit?(:myqueue)
29
+ assert !Resque.queue_at_or_over_rate_limit?("myqueue")
30
+
31
+ Resque.redis.expects(:scard).with("throttler:myqueue_uuids").returns(10).twice
32
+ assert Resque.queue_at_or_over_rate_limit?(:myqueue)
33
+ assert Resque.queue_at_or_over_rate_limit?("myqueue")
34
+ end
35
+
36
+ test "Resque::pop pops on unthrottled queues" do
37
+ Resque.redis.expects(:lpop).returns(nil)
38
+
39
+ Resque.pop('myqueue')
40
+ end
41
+
42
+ test "Resque::pop skips over queues that are at or over their limit" do
43
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
44
+ Resque.expects(:queue_at_or_over_rate_limit?).with("myqueue").returns(true)
45
+ Resque.redis.expects(:lpop).never
46
+
47
+ Resque.pop('myqueue')
48
+ end
49
+
50
+
51
+ test "Resque::pop gc's the limit data after skipping over a throttled queue" do
52
+ Resque.rate_limit(:myqueue, :at => 10, :per => 1)
53
+ Resque.expects(:queue_at_or_over_rate_limit?).with("myqueue").returns(true)
54
+ Resque.expects(:gc_rate_limit_data_for_queue).with("myqueue").once
55
+
56
+ Resque.pop('myqueue')
57
+ end
58
+
59
+ test "Resque::gc_rate_limit_data_for_queue" do
60
+ Resque.rate_limit(:myqueue, :at => 10, :per => 5)
61
+ Resque.redis.expects(:smembers).with("throttler:myqueue_uuids").returns(["1","2","3"]).once
62
+ Resque.redis.expects(:srem).with("throttler:myqueue_uuids", "1").once
63
+ Resque.redis.expects(:del).with("throttler:jobs:1").once
64
+
65
+ travel_to Time.now do
66
+ Resque.redis.expects(:hmget).with("throttler:jobs:1", "ended_at").returns([(Time.now - 10).to_i])
67
+ Resque.redis.expects(:hmget).with("throttler:jobs:2", "ended_at").returns([(Time.now - 3).to_i])
68
+ Resque.redis.expects(:hmget).with("throttler:jobs:3", "ended_at").returns([nil])
69
+
70
+ Resque.gc_rate_limit_data_for_queue('myqueue')
71
+ end
72
+ end
73
+
74
+ test "Resque::gc_rate_limit_data_for_queue for unthrottled queue" do
75
+ Resque.gc_rate_limit_data_for_queue('myqueue')
76
+ end
77
+
78
+ end
@@ -0,0 +1,54 @@
1
+ # To make testing/debugging easier, test within this source tree versus an
2
+ # installed gem
3
+ dir = File.dirname(__FILE__)
4
+ root = File.expand_path(File.join(dir, '..'))
5
+ lib = File.expand_path(File.join(root, 'lib'))
6
+
7
+ $LOAD_PATH << lib
8
+
9
+ require 'resque'
10
+ require 'resque/throttler'
11
+ require "minitest/autorun"
12
+ require 'minitest/unit'
13
+ require 'minitest/reporters'
14
+ require "mocha"
15
+ require "mocha/mini_test"
16
+ require 'active_support/testing/time_helpers'
17
+
18
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
19
+
20
+ # File 'lib/active_support/testing/declarative.rb', somewhere in rails....
21
+ class Minitest::Test
22
+
23
+ include ActiveSupport::Testing::TimeHelpers
24
+
25
+ def self.test(name, &block)
26
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
27
+ defined = instance_method(test_name) rescue false
28
+ raise "#{test_name} is already defined in #{self}" if defined
29
+ if block_given?
30
+ define_method(test_name, &block)
31
+ else
32
+ define_method(test_name) do
33
+ flunk "No implementation provided for #{name}"
34
+ end
35
+ end
36
+ end
37
+
38
+ # test/unit backwards compatibility methods
39
+ alias :assert_raise :assert_raises
40
+ alias :assert_not_empty :refute_empty
41
+ alias :assert_not_equal :refute_equal
42
+ alias :assert_not_in_delta :refute_in_delta
43
+ alias :assert_not_in_epsilon :refute_in_epsilon
44
+ alias :assert_not_includes :refute_includes
45
+ alias :assert_not_instance_of :refute_instance_of
46
+ alias :assert_not_kind_of :refute_kind_of
47
+ alias :assert_no_match :refute_match
48
+ alias :assert_not_nil :refute_nil
49
+ alias :assert_not_operator :refute_operator
50
+ alias :assert_not_predicate :refute_predicate
51
+ alias :assert_not_respond_to :refute_respond_to
52
+ alias :assert_not_same :refute_same
53
+
54
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-throttler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Jon Bracy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
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: minitest-reporters
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: mocha
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: resque
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.25'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.25'
111
+ description: Rate limit how many times a job can be run from a queue
112
+ email:
113
+ - jonbracy@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - Gemfile
120
+ - Gemfile.lock
121
+ - LICENSE
122
+ - README.md
123
+ - Rakefile
124
+ - lib/resque/throttler.rb
125
+ - resque-throttler.gemspec
126
+ - test/resque/job_test.rb
127
+ - test/resque_test.rb
128
+ - test/test_helper.rb
129
+ homepage: https://github.com/malomalo/resque-throttler
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.2.2
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Rate limit Resque Jobs
153
+ test_files:
154
+ - test/resque/job_test.rb
155
+ - test/resque_test.rb
156
+ - test/test_helper.rb
157
+ has_rdoc: