em-throttled_queue 1.0.2

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.
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