em-throttled_queue 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --title "EM-Throttled Queue"
2
+ --markup markdown
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gem 'eventmachine'
4
+
5
+ group :development do
6
+ gem 'jeweler', '~> 1.5'
7
+ gem 'rake'
8
+ gem 'yard'
9
+ gem 'rdiscount'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ eventmachine (0.12.10)
5
+ git (1.2.5)
6
+ jeweler (1.5.2)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+ rdiscount (1.6.8)
12
+ yard (0.6.4)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ eventmachine
19
+ jeweler (~> 1.5)
20
+ rake
21
+ rdiscount
22
+ yard
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Kim Burgestrand
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,42 @@
1
+ # EM-Throttled_Queue is a throttled queue (surprise, surprise!)
2
+ [ThrottledQueue](http://rdoc.info/github/Burgestrand/em-throttled_queue/master/EventMachine/ThrottledQueue) is just like an [EM::Queue](http://rdoc.info/github/eventmachine/eventmachine/master/EventMachine/Queue), but will pop items off itself at a pace specified by you!
3
+
4
+ Version v1.0.0 and v1.0.1 has an unintentional flaw (result of coding while tired) and should not be used. v1.0.2 is coming as soon as issue #2 is resolved. They have been yanked from rubygems.
5
+
6
+ Example
7
+ -------
8
+
9
+ # Example code that will pop off 2 items in total within a period of
10
+ # one second. The other items are not popped because of throttle.
11
+ EM::run do
12
+ # Create a queue that will pop off maximum of 2 items per second,
13
+ # with a refill-limit of 1 second.
14
+ queue = EM::ThrottledQueue.new(2)
15
+
16
+ queue.push 1 # you can push one item at a time
17
+ queue.push 2, 3, 4, 5, 6 # or several at once
18
+
19
+ 5.times { queue.pop(&EM::Callback(Object, :puts)) }
20
+
21
+ EM::add_timer(1) { EM::stop }
22
+ end
23
+
24
+ # Output:
25
+ # 1
26
+ # 2
27
+
28
+ What problem does EM-Throttled_Queue solve?
29
+ -------------------------------------------
30
+ At [radiofy](http://radiofy.se/) we consume a lot of external services. As we are rebuilding our internal structures we will be using EventMachine for the consumption of said services. Some of them have an applied rate-limit that will punish you if you don’t restrain how many queries you execute.
31
+
32
+ As a result, we need to call our blocks of code within the allowed rate-limit. For example, the [Spotify Metadata API](http://developer.spotify.com/en/metadata-api/overview/) allow a maximum of 10 requests per second, and if exceeded will force you to wait 10 seconds (essentially losing 90 reqs/10 seconds). EM::ThrottledQueue will allow us to easily limit how many calls we make per second to their API.
33
+
34
+ How do I contribute?
35
+ --------------------
36
+ Fork, add tests (important!), add your code and send a pull request. If you wish to report an issue, please use the GitHub issue tracker. I can also be contacted by mail (visible on [my GitHub user page](http://github.com/Burgestrand)).
37
+
38
+ As far as development dependencies goes they are all specified in the Gemfile. Once you have checked out the code, a mere `bundle install` should fetch them all for you! \o/
39
+
40
+ Why don’t you support 1.8.7?
41
+ ----------------------------
42
+ There currently is no reason for me to do so. If you believe there is, file an issue.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'jeweler'
4
+ require './lib/em/throttled_queue/version'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "em-throttled_queue"
7
+ gem.homepage = "http://github.com/Burgestrand/em-throttled_queue"
8
+
9
+ gem.license = "X11 License"
10
+
11
+ gem.summary = "A rate-limited Queue for EventMachine"
12
+ gem.authors = ["Kim Burgestrand"]
13
+ gem.email = "kim@burgestrand.se"
14
+
15
+ gem.version = EventMachine::ThrottledQueue::VERSION
16
+ gem.required_ruby_version = '~> 1.9'
17
+ end
18
+ Jeweler::RubygemsDotOrgTasks.new
19
+
20
+ require 'yard'
21
+ require 'yard/rake/yardoc_task'
22
+ YARD::Rake::YardocTask.new
23
+
24
+ require 'rake/testtask'
25
+ Rake::TestTask.new(:spec) do |t|
26
+ t.pattern = 'spec/*.rb'
27
+ end
28
+
29
+ task :release => ['gemspec:release', 'git:release', 'gemcutter:release']
30
+ task :default => :spec
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{em-throttled_queue}
8
+ s.version = "1.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kim Burgestrand"]
12
+ s.date = %q{2011-02-13}
13
+ s.email = %q{kim@burgestrand.se}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".yardopts",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "em-throttled_queue.gemspec",
26
+ "lib/em/throttled_queue.rb",
27
+ "lib/em/throttled_queue/version.rb",
28
+ "lib/eventmachine/throttled_queue.rb",
29
+ "spec/throttled_queue.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/Burgestrand/em-throttled_queue}
32
+ s.licenses = ["X11 License"]
33
+ s.require_paths = ["lib"]
34
+ s.required_ruby_version = Gem::Requirement.new("~> 1.9")
35
+ s.rubygems_version = %q{1.5.0}
36
+ s.summary = %q{A rate-limited Queue for EventMachine}
37
+ s.test_files = [
38
+ "spec/throttled_queue.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
46
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5"])
47
+ s.add_development_dependency(%q<rake>, [">= 0"])
48
+ s.add_development_dependency(%q<yard>, [">= 0"])
49
+ s.add_development_dependency(%q<rdiscount>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<eventmachine>, [">= 0"])
52
+ s.add_dependency(%q<jeweler>, ["~> 1.5"])
53
+ s.add_dependency(%q<rake>, [">= 0"])
54
+ s.add_dependency(%q<yard>, [">= 0"])
55
+ s.add_dependency(%q<rdiscount>, [">= 0"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<eventmachine>, [">= 0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.5"])
60
+ s.add_dependency(%q<rake>, [">= 0"])
61
+ s.add_dependency(%q<yard>, [">= 0"])
62
+ s.add_dependency(%q<rdiscount>, [">= 0"])
63
+ end
64
+ end
65
+
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+ require 'eventmachine'
3
+ require 'em/throttled_queue/version'
4
+
5
+ # @see https://github.com/eventmachine/eventmachine
6
+ module EventMachine
7
+ # An EM::Queue with a rate-limit applied to it. This is useful if you
8
+ # wish to consume items at a limited pace.
9
+ #
10
+ # @todo Add a maximum size-limit on amount of items in queue.
11
+ class ThrottledQueue < Queue
12
+ # Create a new rate-limited queue.
13
+ #
14
+ # @example
15
+ #
16
+ # EM::ThrottledQueue.new(10)
17
+ # # => allows maximum 10 deqs every second
18
+ #
19
+ # @param [Fixnum] maximum of dequeues every second
20
+ def initialize(limit)
21
+ @credits = @limit = limit.to_i
22
+ @ticker = EM::add_periodic_timer(1) do
23
+ @credits = @limit
24
+ scheduled_dequeue
25
+ end
26
+
27
+ super()
28
+ end
29
+
30
+ # Pop an item off the queue as soon as conditions allow, passing it
31
+ # to the given block.
32
+ #
33
+ # @note The given block is executed within the reactor thread.
34
+ # @yield [item] callback to execute when item is popped off
35
+ # @yieldparam item an item from the queue
36
+ # @return (see #interact)
37
+ # @see http://rdoc.info/github/eventmachine/eventmachine/master/EventMachine.Callback
38
+ def pop(&block)
39
+ interact { @popq.push(block) }
40
+ end
41
+
42
+ # Push items onto the queue (from within the reactor thread) asap.
43
+ #
44
+ # @note This call is thread-safe.
45
+ # @param [item, …] items items to be pushed onto the queue
46
+ # @return (see #interact)
47
+ def push(*items)
48
+ interact { @items.push(*items) }
49
+ end
50
+
51
+ private
52
+ # Helper method to schedule queue interaction and then _dequeue.
53
+ #
54
+ # @yield block to call within EM::schedule
55
+ # @return [ThrottledQueue]
56
+ def interact(&block)
57
+ EM::schedule do
58
+ block.call
59
+ # next_tick to avoid locking up caller in a dequeuing loop
60
+ EM::next_tick method(:scheduled_dequeue)
61
+ end
62
+ self
63
+ end
64
+
65
+ # Dequeue as many items as ossible.
66
+ #
67
+ # @return [ThrottledQueue]
68
+ def scheduled_dequeue
69
+ until @credits < 1 || @items.empty? || @popq.empty?
70
+ @credits -= 1
71
+ @popq.shift.call @items.shift
72
+ end
73
+ self
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,7 @@
1
+ module EventMachine
2
+ class ThrottledQueue < Queue
3
+ # Gem version, following Semantic Versioning since v1.0.2
4
+ # @see http://semver.org/
5
+ VERSION = [1, 0, 2].join('.')
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require 'em/throttled_queue'
@@ -0,0 +1,75 @@
1
+ require 'em/throttled_queue'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+
5
+ class << MiniTest::Spec
6
+ def it_enhanced(*args, &block)
7
+ it_original(*args) do
8
+ EM::run(&block)
9
+ end
10
+ end
11
+
12
+ alias_method :it_original, :it
13
+ alias_method :it, :it_enhanced
14
+ end
15
+
16
+ describe EM::ThrottledQueue do
17
+ it "should pop items in FIFO order" do
18
+ queue = EM::ThrottledQueue.new(1)
19
+ pushed_items = [1, 2, 3, 4]
20
+ popped_items = []
21
+ queue.push(*pushed_items)
22
+
23
+ 1.upto(4) do |i|
24
+ queue.pop do |j|
25
+ popped_items << j
26
+
27
+ if i == 4
28
+ popped_items.must_equal pushed_items
29
+ EM::stop
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ it "should pop items within the rate limit" do
36
+ ticks = 0
37
+ deqs = 0
38
+
39
+ queue = EM::ThrottledQueue.new(10) # 10 deqs/s
40
+ queue.push(*(1..1000).to_a)
41
+ queue.size.must_equal 1000
42
+
43
+ EM::add_timer(0.5) do
44
+ deqs.must_equal 10
45
+ (ticks > 100).must_equal true # need good margin
46
+ EM::stop
47
+ end
48
+
49
+ ticker = proc do |me|
50
+ ticks += 1
51
+ queue.pop { deqs += 1 }
52
+ EM::next_tick { me.call(me) }
53
+ end
54
+
55
+ ticker.call(ticker)
56
+ end
57
+
58
+ it "should not build up credits over time" do
59
+ deqs = 0
60
+
61
+ queue = EM::ThrottledQueue.new(10) # 10 deqs/s
62
+ queue.push(*(1..1000).to_a)
63
+
64
+ EM::add_timer(3) do
65
+ # Oh this is bad… test tied to implementation details!
66
+ queue.instance_variable_get("@credits").must_equal 10
67
+ EM::stop
68
+ end
69
+ end
70
+
71
+ it "should load the version" do
72
+ defined?(EM::ThrottledQueue::VERSION).wont_be_nil
73
+ EM::stop
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-throttled_queue
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Kim Burgestrand
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-13 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: eventmachine
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: jeweler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: "1.5"
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rake
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: yard
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rdiscount
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ description:
72
+ email: kim@burgestrand.se
73
+ executables: []
74
+
75
+ extensions: []
76
+
77
+ extra_rdoc_files:
78
+ - LICENSE
79
+ - README.markdown
80
+ files:
81
+ - .yardopts
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - LICENSE
85
+ - README.markdown
86
+ - Rakefile
87
+ - em-throttled_queue.gemspec
88
+ - lib/em/throttled_queue.rb
89
+ - lib/em/throttled_queue/version.rb
90
+ - lib/eventmachine/throttled_queue.rb
91
+ - spec/throttled_queue.rb
92
+ has_rdoc: true
93
+ homepage: http://github.com/Burgestrand/em-throttled_queue
94
+ licenses:
95
+ - X11 License
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ~>
105
+ - !ruby/object:Gem::Version
106
+ version: "1.9"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ requirements: []
114
+
115
+ rubyforge_project:
116
+ rubygems_version: 1.5.0
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: A rate-limited Queue for EventMachine
120
+ test_files:
121
+ - spec/throttled_queue.rb