sendq 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sendq.rb +124 -0
- data/test/test_sendq.rb +74 -0
- metadata +54 -0
data/lib/sendq.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
#
|
4
|
+
# SendQ - Easy access to time-constrained send queues.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# output = []
|
9
|
+
# # process a maximum of 5 within 3 seconds
|
10
|
+
# s = SendQ.new(5, 3) { |x| output.push(x) }
|
11
|
+
# (0..9).each { |x| s.add(x) }
|
12
|
+
# s.run
|
13
|
+
# output # => [0,1,2,3,4]
|
14
|
+
# s.queue # => [5,6,7,8,9]
|
15
|
+
# s.run # nothing changes, we're still waiting to run
|
16
|
+
# sleep 3 # wait for the constraint to expire
|
17
|
+
# s.run
|
18
|
+
# output # => [0,1,2,3,4,5,6,7,8,9]
|
19
|
+
# s.queue # => []
|
20
|
+
#
|
21
|
+
# Note: The calculation is very simple and can probably be gamed. It simply
|
22
|
+
# tests if the last run, subtracted from the current time, is greater than the
|
23
|
+
# interval.
|
24
|
+
#
|
25
|
+
# The clock is by default the unix epoch and therefore is restricted to a
|
26
|
+
# minimum granularity of 1 second. If you wish to change this or otherwise
|
27
|
+
# manipulate the constraint, see SendQ.clock.
|
28
|
+
#
|
29
|
+
# Simple accessors (all can be modified):
|
30
|
+
# * queue: this is the internal queue that will be exhausted on runs. See SendQ#add.
|
31
|
+
# * filled: this is the number filled in the current interval. guaranteed to never be greater than max_per_interval
|
32
|
+
# * action: this is the block you supplied to the constructor.
|
33
|
+
# * max_per_interval: the maximum number of queue elements to exhaust in the timeframe of the interval.
|
34
|
+
#
|
35
|
+
#--
|
36
|
+
#
|
37
|
+
# Please see the COPYING file in the source distribution for copyright information.
|
38
|
+
#
|
39
|
+
#++
|
40
|
+
|
41
|
+
class SendQ
|
42
|
+
|
43
|
+
VERSION = "0.0.1"
|
44
|
+
|
45
|
+
attr_accessor :queue
|
46
|
+
attr_accessor :filled
|
47
|
+
attr_accessor :action
|
48
|
+
attr_accessor :max_per_interval
|
49
|
+
|
50
|
+
#
|
51
|
+
# Constructor.
|
52
|
+
#
|
53
|
+
# * max_per_interval (required): the maximum amount of queue elements to process in a given interval.
|
54
|
+
# * interval (optional, default 1 second): if this much time has passed, reset how many can be filled in the next run to max_per_interval. See SendQ.clock for more information on how to manipulate the clock.
|
55
|
+
# * action (required): A block that describes what you want to do with each element in the queue. This block must take one argument - the element of the queue to process.
|
56
|
+
#
|
57
|
+
def initialize(max_per_interval, interval = 1, &action)
|
58
|
+
raise ArgumentError, "Requires an action block to construct" unless action
|
59
|
+
|
60
|
+
@interval = interval
|
61
|
+
@last_run = 0
|
62
|
+
@max_per_interval = max_per_interval
|
63
|
+
@queue = []
|
64
|
+
@filled = 0
|
65
|
+
@action = action
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Add an item to the queue.
|
70
|
+
#
|
71
|
+
# Like any normal queue, items are processed in FIFO order.
|
72
|
+
#
|
73
|
+
def add(item)
|
74
|
+
@queue.push(item)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Evaluate how many items to process in the queue and then process them.
|
79
|
+
# Update the clock with its current value.
|
80
|
+
#
|
81
|
+
def run
|
82
|
+
fill = [how_many, @queue.size].min
|
83
|
+
@filled += fill
|
84
|
+
fill.times do @action.call(@queue.shift) end
|
85
|
+
@last_run = clock
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
#
|
91
|
+
# Get this SendQs clock.
|
92
|
+
#
|
93
|
+
# This defaults to Time.now.to_i. You can of course redefine it. Example:
|
94
|
+
#
|
95
|
+
# sendq = SendQ.new { ...}
|
96
|
+
# def sendq.clock Time.now.to_f
|
97
|
+
#
|
98
|
+
# For fractional seconds.
|
99
|
+
#
|
100
|
+
# Note:: All of the internal clock management is done via simple
|
101
|
+
# mathematical operators and comparison operators. Therefore, your clock can
|
102
|
+
# realistically be anything that implemements these operators.
|
103
|
+
#
|
104
|
+
def clock
|
105
|
+
Time.now.to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Return how many items in the queue to process this run.
|
110
|
+
#
|
111
|
+
# Side Effect: will reset the filled count if we have determined the
|
112
|
+
# interval for the existing filled count has expired.
|
113
|
+
#
|
114
|
+
def how_many
|
115
|
+
if (clock - @last_run) >= @interval then
|
116
|
+
@filled = 0
|
117
|
+
@max_per_interval
|
118
|
+
elsif @filled < @max_per_interval
|
119
|
+
@max_per_interval - @filled
|
120
|
+
else
|
121
|
+
0
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/test/test_sendq.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# Please see the COPYING file in the source distribution for copyright information.
|
3
|
+
#
|
4
|
+
require 'test/unit'
|
5
|
+
require 'sendq'
|
6
|
+
|
7
|
+
class TestSendQ < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@output = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_construct
|
13
|
+
assert_raise(ArgumentError) { SendQ.new }
|
14
|
+
assert_raise(ArgumentError) { SendQ.new(10) }
|
15
|
+
assert_raise(ArgumentError) { SendQ.new(10, 1, 12) }
|
16
|
+
assert_nothing_raised { SendQ.new(10) { puts "Foo" } }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_fill_queue
|
20
|
+
s = SendQ.new(1) { }
|
21
|
+
(0..10).each { |x| s.add(x) }
|
22
|
+
assert_equal(s.queue, (0..10).to_a)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_run_basic
|
26
|
+
s = SendQ.new(5) { |x| @output.push(x) }
|
27
|
+
(0..10).each { |x| s.add(x) }
|
28
|
+
s.run
|
29
|
+
assert_equal(@output, (0..4).to_a)
|
30
|
+
assert_equal(s.queue, (5..10).to_a)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_run_multiple
|
34
|
+
# XXX obviously, this test will have some failures if it takes more
|
35
|
+
# than 3 seconds to get to the sleep statement
|
36
|
+
s = SendQ.new(5, 3) { |x| @output.push(x) }
|
37
|
+
(0..10).each { |x| s.add(x) }
|
38
|
+
s.run
|
39
|
+
assert_equal(@output, (0..4).to_a)
|
40
|
+
assert_equal(s.queue, (5..10).to_a)
|
41
|
+
|
42
|
+
assert_equal(s.max_per_interval, s.filled)
|
43
|
+
|
44
|
+
s.run
|
45
|
+
|
46
|
+
# should be exactly the same
|
47
|
+
assert_equal(@output, (0..4).to_a)
|
48
|
+
assert_equal(s.queue, (5..10).to_a)
|
49
|
+
|
50
|
+
sleep 3
|
51
|
+
|
52
|
+
s.run
|
53
|
+
|
54
|
+
assert_equal(@output, (0..9).to_a)
|
55
|
+
assert_equal(s.queue, [10])
|
56
|
+
|
57
|
+
sleep 3
|
58
|
+
|
59
|
+
s.run
|
60
|
+
|
61
|
+
assert_equal(@output, (0..10).to_a)
|
62
|
+
assert_equal(s.queue, [])
|
63
|
+
|
64
|
+
assert_equal(s.filled, 1)
|
65
|
+
|
66
|
+
(11..14).each { |x| s.add(x) }
|
67
|
+
|
68
|
+
s.run
|
69
|
+
|
70
|
+
assert_equal(@output, (0..14).to_a)
|
71
|
+
assert_equal(s.queue, [])
|
72
|
+
assert_equal(s.max_per_interval, s.filled)
|
73
|
+
end
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sendq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Erik Hollensbe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-04 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: erik@hollensbe.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/sendq.rb
|
26
|
+
- test/test_sendq.rb
|
27
|
+
has_rdoc: true
|
28
|
+
homepage:
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: "0"
|
39
|
+
version:
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
rubyforge_project: sendq
|
49
|
+
rubygems_version: 1.1.1
|
50
|
+
signing_key:
|
51
|
+
specification_version: 2
|
52
|
+
summary: A library for queues that can only release so much information over a given timespan
|
53
|
+
test_files: []
|
54
|
+
|