later 0.1.3 → 0.2.0
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/README.md +31 -14
- data/Rakefile +1 -1
- data/later.gemspec +2 -2
- data/lib/later.rb +79 -42
- data/lib/later/version.rb +1 -1
- data/test/helper.rb +0 -14
- data/test/specs/later-schedule.rb +74 -0
- data/test/specs/later.rb +15 -0
- metadata +8 -6
- data/test/lib/later.rb +0 -103
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Later
|
|
2
2
|
|
|
3
|
-
[**Later**](erol.github.com/later) is a
|
|
3
|
+
[**Later**](erol.github.com/later) is a lean Redis-backed event scheduling library for Ruby.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -8,29 +8,30 @@ Later allows you to set unique events on a schedule and run them in the future:
|
|
|
8
8
|
|
|
9
9
|
require 'later'
|
|
10
10
|
|
|
11
|
-
Later[:
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
schedule = Later[:schedule]
|
|
12
|
+
schedule.set 'event-1', Time.now + 60
|
|
13
|
+
schedule.set 'event-2', Time.now + 120
|
|
14
|
+
schedule.set 'event-3', Time.now + 180
|
|
14
15
|
|
|
15
16
|
Rescheduling an event is simple:
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
schedule.set 'event-1', Time.now + 240
|
|
18
19
|
|
|
19
|
-
And an event can
|
|
20
|
+
And an event can unset with equal ease:
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
schedule.unset 'event-1'
|
|
22
23
|
|
|
23
24
|
You can manage multiple schedules using different keys:
|
|
24
25
|
|
|
25
|
-
Later[:reservations]
|
|
26
|
-
Later[:appointments]
|
|
26
|
+
reservations = Later[:reservations]
|
|
27
|
+
appointments = Later[:appointments]
|
|
27
28
|
|
|
28
29
|
The schedules are stored on the default Redis instance. If you need a schedule which must reside on a different Redis instance, you can pass a [Nest](github.com/soveran/nest) object when referencing a schedule set.
|
|
29
30
|
|
|
30
31
|
redis = Redis.new host: host, port: port
|
|
31
|
-
key = Nest.new
|
|
32
|
+
key = Nest.new 'Reservations', redis
|
|
32
33
|
|
|
33
|
-
Later[key]
|
|
34
|
+
reservations = Later[key]
|
|
34
35
|
|
|
35
36
|
### Workers
|
|
36
37
|
|
|
@@ -38,18 +39,34 @@ Workers are Ruby processes that run forever. They allow you to process event sch
|
|
|
38
39
|
|
|
39
40
|
require 'later'
|
|
40
41
|
|
|
41
|
-
Later[:
|
|
42
|
+
Later[:schedule].each do |event|
|
|
42
43
|
# Do something with the event.
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
# This line is never reached.
|
|
46
47
|
|
|
48
|
+
#### Timeouts, Blocking & Polling
|
|
49
|
+
|
|
50
|
+
`Later::Schedule#each` accepts an optional `timeout` parameter, which has a default value of `1`. Passing an `Integer` will use Redis' blocking mechanism
|
|
51
|
+
to process the schedule and is therefore more efficient. Passing a `Float` will poll Redis using the given timeout, and should
|
|
52
|
+
only be used for events which need to be triggered with millisecond precision.
|
|
53
|
+
|
|
54
|
+
See [BLPOP](http://redis.io/commands/blpop) and [BRPOPLPUSH](http://redis.io/commands/brpoplpush) for more information.
|
|
55
|
+
|
|
56
|
+
The below schedule will be polled every 0.1 seconds:
|
|
57
|
+
|
|
58
|
+
Later[:schedule].each(0.1) do |event|
|
|
59
|
+
# Do something with the event.
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#### Stopping
|
|
63
|
+
|
|
47
64
|
If for some reason, a worker has to stop itself from running:
|
|
48
65
|
|
|
49
|
-
Later[:
|
|
66
|
+
Later[:schedule].each do |event|
|
|
50
67
|
# Do something with the event.
|
|
51
68
|
|
|
52
|
-
Later[:
|
|
69
|
+
Later[:schedule].stop! if stop?
|
|
53
70
|
end
|
|
54
71
|
|
|
55
72
|
# This line is reached when stop? is true and Later[:reservations].stop! is called.
|
data/Rakefile
CHANGED
data/later.gemspec
CHANGED
|
@@ -4,8 +4,8 @@ require File.expand_path('../lib/later/version', __FILE__)
|
|
|
4
4
|
Gem::Specification.new do |gem|
|
|
5
5
|
gem.authors = ['Erol Fornoles']
|
|
6
6
|
gem.email = ['erol.fornoles@gmail.com']
|
|
7
|
-
gem.description = %q{
|
|
8
|
-
gem.summary = %q{
|
|
7
|
+
gem.description = %q{Lean Redis-backed event scheduling library for Ruby}
|
|
8
|
+
gem.summary = %q{Lean Redis-backed event scheduling library for Ruby}
|
|
9
9
|
gem.homepage = 'http://erol.github.com/later'
|
|
10
10
|
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
data/lib/later.rb
CHANGED
|
@@ -18,101 +18,111 @@ module Later
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Returns the Nest key of this schedule.
|
|
22
|
+
#
|
|
23
|
+
# Later[:reservations].key #=> Later:reservations
|
|
22
24
|
#
|
|
23
|
-
# Later[:reservations].key #=> Later::reservations
|
|
24
|
-
|
|
25
25
|
def key
|
|
26
26
|
@key
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
# Returns the Nest key of this schedule's exception list.
|
|
30
|
+
#
|
|
31
|
+
# Later[:reservations].exceptions #=> Later:reservations:exceptions
|
|
30
32
|
#
|
|
31
|
-
# Later[:reservations].exceptions #=> Later::reservations::exceptions
|
|
32
|
-
|
|
33
33
|
def exceptions
|
|
34
34
|
@exceptions ||= key[:exceptions]
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Returns the time of a scheduled unique event.
|
|
38
38
|
#
|
|
39
39
|
# Later[:reservations].set 'event-1', Time.parse('2012-09-28 11:36:17 +0800')
|
|
40
40
|
# Later[:reservations]['event-1'] #=> 2012-09-28 11:36:17 +0800
|
|
41
|
-
|
|
41
|
+
#
|
|
42
42
|
def [](event)
|
|
43
43
|
Time.at key[:schedule].zscore(event) rescue nil
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
#
|
|
46
|
+
# Returns the number of scheduled unique events.
|
|
47
47
|
#
|
|
48
48
|
# Later[:reservations].set 'event-1', Time.now + 60
|
|
49
49
|
# Later[:reservations].set 'event-2', Time.now + 120
|
|
50
50
|
# Later[:reservations].set 'event-3', Time.now + 180
|
|
51
51
|
# Later[:reservations].count #=> 3
|
|
52
|
-
|
|
52
|
+
#
|
|
53
53
|
def count
|
|
54
54
|
key[:schedule].zcard
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
57
|
+
# Returns `true` if there are no scheduled unique events. Returns `false` otherwise.
|
|
59
58
|
# Later[:reservations].set 'event-1', Time.now + 60
|
|
59
|
+
# Later[:reservations].empty? #=> false
|
|
60
|
+
# Later[:reservations].unset 'event-1'
|
|
61
|
+
# Later[:reservations].empty? #=> true
|
|
62
|
+
#
|
|
63
|
+
def empty?
|
|
64
|
+
count.zero?
|
|
65
|
+
end
|
|
60
66
|
|
|
67
|
+
# Sets a unique event to this schedule.
|
|
68
|
+
#
|
|
69
|
+
# Later[:reservations].set 'event-1', Time.now + 60
|
|
70
|
+
#
|
|
61
71
|
def set(event, time)
|
|
62
|
-
key[:schedule].zadd time.
|
|
72
|
+
key[:schedule].zadd time.to_f, event
|
|
63
73
|
end
|
|
64
74
|
|
|
65
|
-
#
|
|
75
|
+
# Unsets a unique event from this schedule.
|
|
76
|
+
#
|
|
77
|
+
# Later[:reservations].unset 'event-1'
|
|
66
78
|
#
|
|
67
|
-
# Later[:reservations].unset 'event-2'
|
|
68
|
-
|
|
69
79
|
def unset(event)
|
|
70
80
|
key[:schedule].zrem event
|
|
71
81
|
end
|
|
72
82
|
|
|
73
|
-
# When called inside an `each` block, `stop!` signals the block to halt processing of
|
|
83
|
+
# When called inside an `each` block, `stop!` signals the block to halt processing of this schedule.
|
|
74
84
|
#
|
|
75
85
|
# Later[:reservations].each do |event|
|
|
76
86
|
# Later[:reservations].stop!
|
|
77
87
|
# end
|
|
78
|
-
|
|
88
|
+
#
|
|
79
89
|
def stop!
|
|
80
90
|
@stop = true
|
|
81
91
|
end
|
|
82
92
|
|
|
83
|
-
#
|
|
93
|
+
# Processes each scheduled unique event. The block only gets called when an event is due to run based on the current time.
|
|
94
|
+
#
|
|
95
|
+
# Accepts an optional `timeout` parameter with a default value of `1`. Passing an `Integer` will use Redis' blocking mechanism
|
|
96
|
+
# to process the schedule and is therefore more efficient. Passing a `Float` will poll Redis using the given timeout, and should
|
|
97
|
+
# only be used for events which need to be triggered with millisecond precision.
|
|
84
98
|
#
|
|
85
99
|
# Later[:reservations].each do |event|
|
|
86
100
|
# # Do something with the event
|
|
87
101
|
# end
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
#
|
|
103
|
+
# The schedule will be polled every 0.1 seconds:
|
|
104
|
+
#
|
|
105
|
+
# Later[:reservations].each(0.1) do |event|
|
|
106
|
+
# # Do something with the event
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
def each(timeout = 1, &block)
|
|
90
110
|
@stop = false
|
|
91
111
|
|
|
92
112
|
loop do
|
|
93
113
|
break if stop?
|
|
94
114
|
|
|
95
|
-
time = Time.now.
|
|
115
|
+
time = Time.now.to_f
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
schedule.zremrangebyscore '-inf', time
|
|
100
|
-
end.first
|
|
101
|
-
|
|
102
|
-
key.redis.multi do
|
|
103
|
-
ids.each { |id| queue.lpush id }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
event = queue.brpoplpush(backup, 1)
|
|
107
|
-
|
|
108
|
-
next unless event
|
|
117
|
+
push_to_queue pop_from_schedules(time)
|
|
118
|
+
next unless event = pop_from_queue(timeout)
|
|
109
119
|
|
|
110
120
|
begin
|
|
111
121
|
block.call event
|
|
112
122
|
rescue Exception => e
|
|
113
123
|
exceptions.rpush JSON(time: Time.now, event: event, message: e.inspect)
|
|
114
124
|
ensure
|
|
115
|
-
|
|
125
|
+
local.del
|
|
116
126
|
end
|
|
117
127
|
end
|
|
118
128
|
end
|
|
@@ -127,22 +137,49 @@ module Later
|
|
|
127
137
|
@queue ||= key[:queue]
|
|
128
138
|
end
|
|
129
139
|
|
|
130
|
-
def
|
|
131
|
-
@
|
|
140
|
+
def local
|
|
141
|
+
@local ||= queue[Socket.gethostname][Process.pid]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def pop_from_schedules(time)
|
|
145
|
+
schedule.redis.multi do
|
|
146
|
+
schedule.zrangebyscore '-inf', time
|
|
147
|
+
schedule.zremrangebyscore '-inf', time
|
|
148
|
+
end.first
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def push_to_queue(ids)
|
|
152
|
+
key.redis.multi do
|
|
153
|
+
ids.each { |id| queue.lpush id }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def pop_from_queue(timeout)
|
|
158
|
+
if timeout.is_a? Integer
|
|
159
|
+
queue.brpoplpush(local, timeout)
|
|
160
|
+
else
|
|
161
|
+
result = queue.rpoplpush(local)
|
|
162
|
+
sleep timeout unless result
|
|
163
|
+
result
|
|
164
|
+
end
|
|
132
165
|
end
|
|
133
166
|
end
|
|
134
167
|
|
|
135
168
|
@schedules = {}
|
|
136
169
|
|
|
170
|
+
# Returns the Nest key of this module.
|
|
171
|
+
#
|
|
172
|
+
# Later[:reservations].key #=> Later::reservations
|
|
173
|
+
#
|
|
137
174
|
def self.key
|
|
138
175
|
Nest.new('Later')
|
|
139
176
|
end
|
|
140
177
|
|
|
178
|
+
# The easiest way to create or reference a schedule. Returns an instance of a Later::Schedule with the given key.
|
|
179
|
+
#
|
|
180
|
+
# Later[:reservations] #=> #<Later::Schedule:0x007faf3b054f50 @key="Later:reservations">
|
|
181
|
+
#
|
|
141
182
|
def self.[](schedule)
|
|
142
|
-
|
|
143
|
-
@schedules[schedule.to_sym]
|
|
144
|
-
else
|
|
145
|
-
@schedules[schedule.to_sym] = Schedule.new schedule
|
|
146
|
-
end
|
|
183
|
+
@schedules[schedule.to_sym] ||= Schedule.new schedule
|
|
147
184
|
end
|
|
148
185
|
end
|
data/lib/later/version.rb
CHANGED
data/test/helper.rb
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
1
|
require 'bundler/setup'
|
|
2
2
|
require 'later'
|
|
3
3
|
require 'minitest/autorun'
|
|
4
|
-
|
|
5
|
-
class MiniTest::Unit::TestCase
|
|
6
|
-
def setup
|
|
7
|
-
system 'redis-server', File.join(File.dirname(__FILE__), 'redis.conf')
|
|
8
|
-
until File.exist? 'redis.pid'
|
|
9
|
-
sleep 0.1
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def teardown
|
|
14
|
-
system "kill `cat redis.pid`"
|
|
15
|
-
sleep 0.1
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require_relative '../helper.rb'
|
|
2
|
+
|
|
3
|
+
describe Later::Schedule do
|
|
4
|
+
let(:redis) { Redis.new db: 15 }
|
|
5
|
+
let(:key) { Nest.new 'Events', redis }
|
|
6
|
+
let(:schedule) { Later::Schedule.new key }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
redis.flushdb
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#set' do
|
|
13
|
+
it 'sets a unique event schedule' do
|
|
14
|
+
time = Time.now
|
|
15
|
+
|
|
16
|
+
schedule.set 'event', time
|
|
17
|
+
|
|
18
|
+
assert_equal 1, schedule.count
|
|
19
|
+
assert_in_delta time, schedule['event'], 0.1
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe '#unset' do
|
|
24
|
+
it 'unsets a unique event schedule' do
|
|
25
|
+
time = Time.now
|
|
26
|
+
|
|
27
|
+
schedule.set 'event', time
|
|
28
|
+
schedule.unset 'event'
|
|
29
|
+
|
|
30
|
+
assert_equal 0, schedule.count
|
|
31
|
+
assert_equal nil, schedule['event']
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#count' do
|
|
36
|
+
it 'counts the number of unique event schedules' do
|
|
37
|
+
time = Time.now
|
|
38
|
+
|
|
39
|
+
1.upto(3) do |i|
|
|
40
|
+
schedule.set i.to_s, time
|
|
41
|
+
assert_equal i, schedule.count
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
1.upto(3) do |i|
|
|
45
|
+
schedule.unset i.to_s
|
|
46
|
+
assert_equal 3 - i, schedule.count
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe '#each' do
|
|
52
|
+
it 'logs exceptions raised within the block' do
|
|
53
|
+
0.upto(2) do |i|
|
|
54
|
+
schedule.set i, Time.now
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Thread.new do
|
|
58
|
+
sleep 3
|
|
59
|
+
schedule.stop!
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
schedule.each do |event|
|
|
63
|
+
raise Exception, "an unknown error for #{event} has occurred"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
exceptions = schedule.exceptions.lrange(0, -1).map{ |e| JSON(e) }
|
|
67
|
+
|
|
68
|
+
0.upto(2) do |i|
|
|
69
|
+
assert_equal i.to_s, exceptions[i]['event']
|
|
70
|
+
assert_equal "#<Exception: an unknown error for #{i} has occurred>", exceptions[i]['message']
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/test/specs/later.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative '../helper.rb'
|
|
2
|
+
|
|
3
|
+
describe Later do
|
|
4
|
+
let(:redis) { Redis.new db: 15 }
|
|
5
|
+
let(:key) { Nest.new 'Events', redis }
|
|
6
|
+
|
|
7
|
+
describe '.[]' do
|
|
8
|
+
it 'returns a schedule with the given key' do
|
|
9
|
+
schedule = Later[key]
|
|
10
|
+
|
|
11
|
+
assert_instance_of Later::Schedule, schedule
|
|
12
|
+
assert_equal key, schedule.key
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: later
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-10-21 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: redis
|
|
@@ -91,7 +91,7 @@ dependencies:
|
|
|
91
91
|
- - ! '>='
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
93
|
version: '0'
|
|
94
|
-
description:
|
|
94
|
+
description: Lean Redis-backed event scheduling library for Ruby
|
|
95
95
|
email:
|
|
96
96
|
- erol.fornoles@gmail.com
|
|
97
97
|
executables: []
|
|
@@ -107,8 +107,9 @@ files:
|
|
|
107
107
|
- lib/later.rb
|
|
108
108
|
- lib/later/version.rb
|
|
109
109
|
- test/helper.rb
|
|
110
|
-
- test/lib/later.rb
|
|
111
110
|
- test/redis.conf
|
|
111
|
+
- test/specs/later-schedule.rb
|
|
112
|
+
- test/specs/later.rb
|
|
112
113
|
homepage: http://erol.github.com/later
|
|
113
114
|
licenses: []
|
|
114
115
|
post_install_message:
|
|
@@ -132,8 +133,9 @@ rubyforge_project:
|
|
|
132
133
|
rubygems_version: 1.8.24
|
|
133
134
|
signing_key:
|
|
134
135
|
specification_version: 3
|
|
135
|
-
summary:
|
|
136
|
+
summary: Lean Redis-backed event scheduling library for Ruby
|
|
136
137
|
test_files:
|
|
137
138
|
- test/helper.rb
|
|
138
|
-
- test/lib/later.rb
|
|
139
139
|
- test/redis.conf
|
|
140
|
+
- test/specs/later-schedule.rb
|
|
141
|
+
- test/specs/later.rb
|
data/test/lib/later.rb
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
require_relative '../helper.rb'
|
|
2
|
-
|
|
3
|
-
class LaterTest < MiniTest::Unit::TestCase
|
|
4
|
-
def setup
|
|
5
|
-
super
|
|
6
|
-
|
|
7
|
-
@redis = Redis.new host: '127.0.0.1', port: 6666
|
|
8
|
-
@key = Nest.new 'Events', @redis
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def test_set_a_unique_event_schedule
|
|
12
|
-
time = Time.now
|
|
13
|
-
|
|
14
|
-
Later[@key].set '1', time
|
|
15
|
-
|
|
16
|
-
assert_equal 1, Later[@key].count
|
|
17
|
-
assert_in_delta time, Later[@key]['1'], 1
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def test_unset_a_unique_event_schedule
|
|
21
|
-
time = Time.now
|
|
22
|
-
|
|
23
|
-
Later[@key].set '1', time
|
|
24
|
-
Later[@key].unset '1'
|
|
25
|
-
|
|
26
|
-
assert_equal 0, Later[@key].count
|
|
27
|
-
assert_equal nil, Later[@key]['1']
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def test_count_set_and_unset_event_schedules
|
|
31
|
-
time = Time.now
|
|
32
|
-
|
|
33
|
-
1.upto(3) do |i|
|
|
34
|
-
Later[@key].set i.to_s, time
|
|
35
|
-
assert_equal i, Later[@key].count
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
1.upto(3) do |i|
|
|
39
|
-
Later[@key].unset i.to_s
|
|
40
|
-
assert_equal 3 - i, Later[@key].count
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def test_process_many_event_schedules
|
|
45
|
-
start = Time.now + 2
|
|
46
|
-
|
|
47
|
-
times = []
|
|
48
|
-
schedules = Hash.new { |h,k| h[k] = [] }
|
|
49
|
-
|
|
50
|
-
1.upto(100) do |i|
|
|
51
|
-
time = start + i / 10
|
|
52
|
-
times.unshift time
|
|
53
|
-
schedules[time].unshift event: i.to_s, time: time
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
times.map{ |time| schedules[time] }.flatten.shuffle.each do |schedule|
|
|
57
|
-
Later[@key].set schedule[:event], schedule[:time]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
Thread.new do
|
|
61
|
-
sleep 2 + 10 + (start - Time.now).to_i
|
|
62
|
-
Later[@key].stop!
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
Later[@key].each do |event|
|
|
66
|
-
time = times.pop
|
|
67
|
-
|
|
68
|
-
assert_in_delta time, Time.now, 1.2
|
|
69
|
-
assert_includes schedules[time], {event: event, time: time}
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
assert_equal 0, Later[@key].count
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def test_exceptions_raised_within_the_worker_loop_are_handled_and_logged
|
|
76
|
-
start = Time.now + 2
|
|
77
|
-
|
|
78
|
-
1.upto(3) do |i|
|
|
79
|
-
Later[@key].set i.to_s, start + i
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
Thread.new do
|
|
83
|
-
sleep 2 + 3 + (start - Time.now).to_i
|
|
84
|
-
Later[@key].stop!
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
Later[@key].each do |event|
|
|
88
|
-
raise Exception, "an unknown error for #{event} has occurred"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
assert_equal 0, Later[@key].count
|
|
92
|
-
|
|
93
|
-
exceptions = Later[@key].exceptions.lrange 0, -1
|
|
94
|
-
exceptions = exceptions.map{ |e| JSON(e) }
|
|
95
|
-
|
|
96
|
-
assert_equal '1', exceptions[0]['event']
|
|
97
|
-
assert_equal '#<Exception: an unknown error for 1 has occurred>', exceptions[0]['message']
|
|
98
|
-
assert_equal '2', exceptions[1]['event']
|
|
99
|
-
assert_equal '#<Exception: an unknown error for 2 has occurred>', exceptions[1]['message']
|
|
100
|
-
assert_equal '3', exceptions[2]['event']
|
|
101
|
-
assert_equal '#<Exception: an unknown error for 3 has occurred>', exceptions[2]['message']
|
|
102
|
-
end
|
|
103
|
-
end
|