sendq 0.0.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.
- 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
|
+
|