rufus-scheduler 2.0.24 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/CHANGELOG.txt +6 -0
  2. data/CREDITS.txt +4 -0
  3. data/README.md +1064 -0
  4. data/Rakefile +1 -4
  5. data/TODO.txt +145 -55
  6. data/lib/rufus/scheduler.rb +502 -26
  7. data/lib/rufus/{sc → scheduler}/cronline.rb +46 -17
  8. data/lib/rufus/{sc/version.rb → scheduler/job_array.rb} +56 -4
  9. data/lib/rufus/scheduler/jobs.rb +548 -0
  10. data/lib/rufus/scheduler/util.rb +318 -0
  11. data/rufus-scheduler.gemspec +30 -4
  12. data/spec/cronline_spec.rb +29 -8
  13. data/spec/error_spec.rb +116 -0
  14. data/spec/job_array_spec.rb +39 -0
  15. data/spec/job_at_spec.rb +58 -0
  16. data/spec/job_cron_spec.rb +67 -0
  17. data/spec/job_every_spec.rb +71 -0
  18. data/spec/job_in_spec.rb +20 -0
  19. data/spec/job_interval_spec.rb +68 -0
  20. data/spec/job_repeat_spec.rb +308 -0
  21. data/spec/job_spec.rb +387 -115
  22. data/spec/lockfile_spec.rb +61 -0
  23. data/spec/parse_spec.rb +203 -0
  24. data/spec/schedule_at_spec.rb +129 -0
  25. data/spec/schedule_cron_spec.rb +66 -0
  26. data/spec/schedule_every_spec.rb +109 -0
  27. data/spec/schedule_in_spec.rb +80 -0
  28. data/spec/schedule_interval_spec.rb +128 -0
  29. data/spec/scheduler_spec.rb +831 -124
  30. data/spec/spec_helper.rb +65 -0
  31. data/spec/threads_spec.rb +75 -0
  32. metadata +64 -59
  33. data/README.rdoc +0 -661
  34. data/lib/rufus/otime.rb +0 -3
  35. data/lib/rufus/sc/jobqueues.rb +0 -160
  36. data/lib/rufus/sc/jobs.rb +0 -471
  37. data/lib/rufus/sc/rtime.rb +0 -363
  38. data/lib/rufus/sc/scheduler.rb +0 -636
  39. data/spec/at_in_spec.rb +0 -47
  40. data/spec/at_spec.rb +0 -125
  41. data/spec/blocking_spec.rb +0 -64
  42. data/spec/cron_spec.rb +0 -134
  43. data/spec/every_spec.rb +0 -304
  44. data/spec/exception_spec.rb +0 -113
  45. data/spec/in_spec.rb +0 -150
  46. data/spec/mutex_spec.rb +0 -159
  47. data/spec/rtime_spec.rb +0 -137
  48. data/spec/schedulable_spec.rb +0 -97
  49. data/spec/spec_base.rb +0 -87
  50. data/spec/stress_schedule_unschedule_spec.rb +0 -159
  51. data/spec/timeout_spec.rb +0 -148
  52. data/test/kjw.rb +0 -113
  53. data/test/t.rb +0 -20
@@ -0,0 +1,109 @@
1
+
2
+ #
3
+ # Specifying rufus-scheduler
4
+ #
5
+ # Wed Apr 17 06:00:59 JST 2013
6
+ #
7
+
8
+ require 'spec_helper'
9
+
10
+
11
+ describe Rufus::Scheduler do
12
+
13
+ before :each do
14
+ @scheduler = Rufus::Scheduler.new
15
+ end
16
+ after :each do
17
+ @scheduler.shutdown
18
+ end
19
+
20
+ describe '#every' do
21
+
22
+ it 'adds a job' do
23
+
24
+ @scheduler.every(10) do
25
+ end
26
+
27
+ @scheduler.jobs.size.should == 1
28
+ @scheduler.jobs.first.class.should == Rufus::Scheduler::EveryJob
29
+ end
30
+
31
+ it 'triggers a job (2 times)' do
32
+
33
+ counter = 0
34
+
35
+ @scheduler.every(0.4) do
36
+ counter += 1
37
+ end
38
+
39
+ sleep 2.0
40
+
41
+ counter.should > 2
42
+ end
43
+
44
+ it 'does not remove the job after execution' do
45
+
46
+ @scheduler.every(0.4) do
47
+ end
48
+
49
+ sleep 0.9
50
+
51
+ @scheduler.jobs.size.should == 1
52
+ end
53
+
54
+ it 'raises on negative frequencies' do
55
+
56
+ lambda {
57
+ @scheduler.every(-1) do
58
+ end
59
+ }.should raise_error(ArgumentError)
60
+ end
61
+
62
+ it 'raises on zero frequencies' do
63
+
64
+ lambda {
65
+ @scheduler.every(0) do
66
+ end
67
+ }.should raise_error(ArgumentError)
68
+ end
69
+
70
+ it 'does not reschedule if the job was unscheduled' do
71
+
72
+ counter = 0
73
+
74
+ job =
75
+ @scheduler.schedule_every '0.5s' do
76
+ counter = counter + 1
77
+ end
78
+
79
+ sleep 1.6
80
+
81
+ job.unschedule
82
+ c = counter
83
+
84
+ sleep 1.6
85
+
86
+ counter.should == c
87
+ end
88
+
89
+ it 'raises if the job frequency is higher than the scheduler frequency' do
90
+
91
+ @scheduler.frequency = 10
92
+
93
+ lambda {
94
+ @scheduler.every '1s' do; end
95
+ }.should raise_error(ArgumentError)
96
+ end
97
+ end
98
+
99
+ describe '#schedule_every' do
100
+
101
+ it 'accepts a duration string' do
102
+
103
+ job = @scheduler.schedule_every('1h') do; end
104
+
105
+ job.frequency.should == 3600.0
106
+ end
107
+ end
108
+ end
109
+
@@ -0,0 +1,80 @@
1
+
2
+ #
3
+ # Specifying rufus-scheduler
4
+ #
5
+ # Wed Apr 17 06:00:59 JST 2013
6
+ #
7
+
8
+ require 'spec_helper'
9
+
10
+
11
+ describe Rufus::Scheduler do
12
+
13
+ before :each do
14
+ @scheduler = Rufus::Scheduler.new
15
+ end
16
+ after :each do
17
+ @scheduler.shutdown
18
+ end
19
+
20
+ describe '#in' do
21
+
22
+ it 'adds a job' do
23
+
24
+ @scheduler.in(3600) do
25
+ end
26
+
27
+ @scheduler.jobs.size.should == 1
28
+ @scheduler.jobs.first.class.should == Rufus::Scheduler::InJob
29
+ end
30
+
31
+ it 'triggers a job' do
32
+
33
+ a = false
34
+
35
+ @scheduler.in(0.4) do
36
+ a = true
37
+ end
38
+
39
+ sleep 0.9
40
+
41
+ a.should == true
42
+ end
43
+
44
+ it 'removes the job after execution' do
45
+
46
+ @scheduler.in(0.4) do
47
+ end
48
+
49
+ sleep 0.700
50
+
51
+ @scheduler.jobs.size.should == 0
52
+ end
53
+ end
54
+
55
+ describe '#schedule_in' do
56
+
57
+ it 'accepts a number' do
58
+
59
+ job = @scheduler.schedule_in(3600) {}
60
+
61
+ job.original.should == 3600
62
+ end
63
+
64
+ it 'accepts a duration string' do
65
+
66
+ job = @scheduler.schedule_in('1h') {}
67
+
68
+ job.original.should == '1h'
69
+ job.time.should >= job.scheduled_at + 3509
70
+ job.time.should <= job.scheduled_at + 3601
71
+ end
72
+
73
+ it 'accepts an ActiveSupport .from_now thinggy'
74
+ #
75
+ # schedule_in(2.days.from_now)
76
+ #
77
+ # that'd simply require "in" to be a bit like "at"...
78
+ end
79
+ end
80
+
@@ -0,0 +1,128 @@
1
+
2
+ #
3
+ # Specifying rufus-scheduler
4
+ #
5
+ # Wed Aug 7 06:20:55 JST 2013
6
+ #
7
+
8
+ require 'spec_helper'
9
+
10
+
11
+ describe Rufus::Scheduler do
12
+
13
+ before :each do
14
+ @scheduler = Rufus::Scheduler.new
15
+ end
16
+ after :each do
17
+ @scheduler.shutdown
18
+ end
19
+
20
+ describe '#interval' do
21
+
22
+ it 'adds a job' do
23
+
24
+ @scheduler.interval(10) do
25
+ end
26
+
27
+ @scheduler.jobs.size.should == 1
28
+ @scheduler.jobs.first.class.should == Rufus::Scheduler::IntervalJob
29
+ end
30
+
31
+ it 'triggers a job (2 times)' do
32
+
33
+ counter = 0
34
+
35
+ @scheduler.interval(0.4) do
36
+ counter += 1
37
+ end
38
+
39
+ sleep 2.0
40
+
41
+ counter.should > 2
42
+ end
43
+
44
+ it 'triggers, but reschedules after the trigger execution' do
45
+
46
+ chronos = []
47
+
48
+ @scheduler.interval(0.4) do
49
+ now = Time.now
50
+ last, delta = chronos.last
51
+ chronos << [ now, last ? now - last : nil ]
52
+ sleep 0.5
53
+ end
54
+
55
+ t = Time.now
56
+ sleep 0.1 while chronos.size < 4 && Time.now < t + 5
57
+
58
+ chronos.size.should == 4
59
+
60
+ deltas = chronos.collect(&:last).compact
61
+
62
+ #pp chronos
63
+ #pp deltas
64
+
65
+ deltas.each do |d|
66
+ d.should >= 0.9
67
+ end
68
+ end
69
+
70
+ it 'does not reschedule if the job was unscheduled' do
71
+
72
+ counter = 0
73
+
74
+ job =
75
+ @scheduler.schedule_interval '0.5s' do
76
+ counter = counter + 1
77
+ end
78
+
79
+ sleep 1.6
80
+
81
+ @scheduler.jobs(:all).size.should == 1
82
+
83
+ job.unschedule
84
+ c = counter
85
+
86
+ sleep 1.6
87
+
88
+ counter.should == c
89
+ @scheduler.jobs(:all).size.should == 0
90
+ end
91
+
92
+ it 'raises on negative intervals' do
93
+
94
+ lambda {
95
+ @scheduler.interval(-1) do
96
+ end
97
+ }.should raise_error(ArgumentError)
98
+ end
99
+
100
+ it 'raises on zero intervals' do
101
+
102
+ lambda {
103
+ @scheduler.interval(0) do
104
+ end
105
+ }.should raise_error(ArgumentError)
106
+ end
107
+
108
+ #it 'raises if the job frequency is higher than the scheduler frequency' do
109
+ #
110
+ # @scheduler.frequency = 10
111
+ #
112
+ # lambda {
113
+ # @scheduler.interval '1s' do; end
114
+ # }.should raise_error(ArgumentError)
115
+ #end
116
+ end
117
+
118
+ describe '#schedule_interval' do
119
+
120
+ it 'accepts a duration string' do
121
+
122
+ job = @scheduler.schedule_interval('1h') do; end
123
+
124
+ job.interval.should == 3600
125
+ end
126
+ end
127
+ end
128
+
@@ -2,247 +2,954 @@
2
2
  #
3
3
  # Specifying rufus-scheduler
4
4
  #
5
- # Sat Mar 21 17:43:23 JST 2009
5
+ # Wed Apr 17 06:00:59 JST 2013
6
6
  #
7
7
 
8
- require 'spec_base'
8
+ require 'spec_helper'
9
9
 
10
10
 
11
- describe SCHEDULER_CLASS do
11
+ describe Rufus::Scheduler do
12
12
 
13
- it 'stops' do
13
+ describe '#initialize' do
14
14
 
15
- var = nil
15
+ it 'starts the scheduler thread' do
16
16
 
17
- s = start_scheduler
18
- s.in('3s') { var = true }
17
+ scheduler = Rufus::Scheduler.new
19
18
 
20
- stop_scheduler(s)
19
+ t = Thread.list.find { |t|
20
+ t[:name] == "rufus_scheduler_#{scheduler.object_id}_scheduler"
21
+ }
21
22
 
22
- var.should == nil
23
- sleep 4
24
- var.should == nil
23
+ t[:rufus_scheduler].should == scheduler
24
+ end
25
+
26
+ it 'sets a :rufus_scheduler thread local var' do
27
+
28
+ scheduler = Rufus::Scheduler.new
29
+ end
30
+
31
+ it 'accepts a :frequency => integer option' do
32
+
33
+ scheduler = Rufus::Scheduler.new(:frequency => 2)
34
+
35
+ scheduler.frequency.should == 2
36
+ end
37
+
38
+ it 'accepts a :frequency => "2h1m" option' do
39
+
40
+ scheduler = Rufus::Scheduler.new(:frequency => '2h1m')
41
+
42
+ scheduler.frequency.should == 3600 * 2 + 60
43
+ end
44
+
45
+ it 'accepts a :thread_name option' do
46
+
47
+ scheduler = Rufus::Scheduler.new(:thread_name => 'oliphant')
48
+
49
+ t = Thread.list.find { |t| t[:name] == 'oliphant' }
50
+
51
+ t[:rufus_scheduler].should == scheduler
52
+ end
53
+
54
+ #it 'accepts a :min_work_threads option' do
55
+ # scheduler = Rufus::Scheduler.new(:min_work_threads => 9)
56
+ # scheduler.min_work_threads.should == 9
57
+ #end
58
+
59
+ it 'accepts a :max_work_threads option' do
60
+
61
+ scheduler = Rufus::Scheduler.new(:max_work_threads => 9)
62
+
63
+ scheduler.max_work_threads.should == 9
64
+ end
25
65
  end
26
66
 
27
- unless SCHEDULER_CLASS == Rufus::Scheduler::EmScheduler
67
+ before :each do
68
+ @scheduler = Rufus::Scheduler.new
69
+ end
70
+ after :each do
71
+ @scheduler.shutdown
72
+ end
73
+
74
+ describe 'a schedule method' do
28
75
 
29
- it 'sets a default scheduler thread name' do
76
+ it 'passes the job to its block when it triggers' do
30
77
 
31
- s = start_scheduler
78
+ j = nil
79
+ job = @scheduler.schedule_in('0s') { |jj| j = jj }
80
+
81
+ sleep 0.4
82
+
83
+ j.should == job
84
+ end
32
85
 
33
- s.instance_variable_get(:@thread)['name'].should match(
34
- /Rufus::Scheduler::.*Scheduler - \d+\.\d+\.\d+/)
86
+ it 'passes the trigger time as second block argument' do
35
87
 
36
- stop_scheduler(s)
88
+ t = nil
89
+ @scheduler.schedule_in('0s') { |jj, tt| t = tt }
90
+
91
+ sleep 0.4
92
+
93
+ t.class.should == Time
94
+ end
95
+
96
+ class MyHandler
97
+ attr_reader :counter
98
+ def initialize
99
+ @counter = 0
100
+ end
101
+ def call(job, time)
102
+ @counter = @counter + 1
103
+ end
37
104
  end
38
105
 
39
- it 'sets the scheduler thread name' do
106
+ it 'accepts a callable object instead of a block' do
107
+
108
+ mh = MyHandler.new
109
+
110
+ @scheduler.schedule_in('0s', mh)
40
111
 
41
- s = start_scheduler(:thread_name => 'nada')
42
- s.instance_variable_get(:@thread)['name'].should == 'nada'
112
+ sleep 0.4
43
113
 
44
- stop_scheduler(s)
114
+ mh.counter.should == 1
115
+ end
116
+
117
+ class MyOtherHandler
118
+ attr_reader :counter
119
+ def initialize
120
+ @counter = 0
121
+ end
122
+ def call
123
+ @counter = @counter + 1
124
+ end
125
+ end
126
+
127
+ it 'accepts a callable obj instead of a block (#call with no args)' do
128
+
129
+ job = @scheduler.schedule_in('0s', MyOtherHandler.new)
130
+
131
+ sleep 0.4
132
+
133
+ job.handler.counter.should == 1
134
+ end
135
+
136
+ it 'accepts a class as callable' do
137
+
138
+ job =
139
+ @scheduler.schedule_in('0s', Class.new do
140
+ attr_reader :value
141
+ def call
142
+ @value = 7
143
+ end
144
+ end)
145
+
146
+ sleep 0.4
147
+
148
+ job.handler.value.should == 7
149
+ end
150
+
151
+ it 'raises if the scheduler is shutting down' do
152
+
153
+ @scheduler.shutdown
154
+
155
+ lambda {
156
+ @scheduler.in('0s') { puts 'hhhhhhhhhhhello!!' }
157
+ }.should raise_error(RuntimeError)
45
158
  end
46
159
  end
47
160
 
48
- it 'accepts a custom frequency' do
161
+ describe '#in / #at' do
162
+
163
+ # scheduler.in(2.hours.from_now) { ... }
49
164
 
50
- var = nil
165
+ it 'accepts point in time and duration indifferently (#in)' do
51
166
 
52
- s = start_scheduler(:frequency => 3.0)
167
+ seen = false
53
168
 
54
- s.in('1s') { var = true }
169
+ t = Time.now + 1
55
170
 
56
- sleep 1
57
- var.should == nil
171
+ @scheduler.in(t) { seen = true }
58
172
 
59
- sleep 1
60
- var.should == nil
173
+ sleep 0.1 while seen != true
174
+ end
175
+
176
+ it 'accepts point in time and duration indifferently (#at)' do
177
+
178
+ seen = false
61
179
 
62
- sleep 2
63
- var.should == true
180
+ t = 1
64
181
 
65
- stop_scheduler(s)
182
+ @scheduler.at(t) { seen = true }
183
+
184
+ sleep 0.1 while seen != true
185
+ end
66
186
  end
67
187
 
68
- context 'pause/resume' do
188
+ describe '#schedule' do
69
189
 
70
- before(:each) do
71
- @s = start_scheduler
190
+ it 'accepts a duration and schedules an InJob' do
191
+
192
+ j = @scheduler.schedule '1s' do; end
193
+
194
+ j.class.should == Rufus::Scheduler::InJob
195
+ j.original.should == '1s'
72
196
  end
73
- after(:each) do
74
- stop_scheduler(@s)
197
+
198
+ it 'accepts a point in time and schedules an AtJob' do
199
+
200
+ j = @scheduler.schedule '2070/12/24 23:00' do; end
201
+
202
+ j.class.should == Rufus::Scheduler::AtJob
203
+ j.next_time.strftime('%Y %m %d').should == '2070 12 24'
75
204
  end
76
205
 
77
- describe '#pause' do
206
+ it 'accepts a cron string and schedules a CronJob' do
207
+
208
+ j = @scheduler.schedule '* * * * *' do; end
209
+
210
+ j.class.should == Rufus::Scheduler::CronJob
211
+ end
212
+ end
213
+
214
+ describe '#repeat' do
215
+
216
+ it 'accepts a duration and schedules an EveryJob' do
217
+
218
+ j = @scheduler.repeat '1s' do; end
219
+
220
+ j.class.should == Rufus::Scheduler::EveryJob
221
+ end
222
+
223
+ it 'accepts a cron string and schedules a CronJob' do
224
+
225
+ j = @scheduler.repeat '* * * * *' do; end
226
+
227
+ j.class.should == Rufus::Scheduler::CronJob
228
+ end
229
+ end
230
+
231
+ describe '#unschedule(job_or_work_id)' do
232
+
233
+ it 'accepts job ids' do
234
+
235
+ job = @scheduler.schedule_in '10d' do; end
236
+
237
+ job.unscheduled_at.should == nil
238
+
239
+ @scheduler.unschedule(job.id)
240
+
241
+ job.unscheduled_at.should_not == nil
242
+ end
78
243
 
79
- it 'pauses a job (every)' do
244
+ it 'accepts jobs' do
80
245
 
81
- $count = 0
246
+ job = @scheduler.schedule_in '10d' do; end
82
247
 
83
- j = @s.every '1s' do
84
- $count = $count + 1
248
+ job.unscheduled_at.should == nil
249
+
250
+ @scheduler.unschedule(job)
251
+
252
+ job.unscheduled_at.should_not == nil
253
+ end
254
+
255
+ it 'carefully unschedules repeat jobs' do
256
+
257
+ counter = 0
258
+
259
+ job =
260
+ @scheduler.schedule_every '0.5s' do
261
+ counter = counter + 1
85
262
  end
86
263
 
87
- @s.pause(j.job_id)
264
+ sleep 1.5
265
+ c = counter
266
+
267
+ @scheduler.unschedule(job)
268
+
269
+ sleep 1.5
270
+ counter.should == c
271
+ end
272
+ end
273
+
274
+ describe '#uptime' do
275
+
276
+ it 'returns the uptime as a float' do
277
+
278
+ @scheduler.uptime.should >= 0.0
279
+ end
280
+ end
281
+
282
+ describe '#uptime_s' do
283
+
284
+ it 'returns the uptime as a human readable string' do
285
+
286
+ sleep 1
287
+
288
+ @scheduler.uptime_s.should match(/^[12]s\d+$/)
289
+ end
290
+ end
291
+
292
+ describe '#join' do
293
+
294
+ it 'joins the scheduler thread' do
295
+
296
+ t = Thread.new { @scheduler.join; Thread.current['a'] = 'over' }
297
+
298
+ t['a'].should == nil
299
+
300
+ @scheduler.shutdown
301
+
302
+ sleep(1)
303
+
304
+ t['a'].should == 'over'
305
+ end
306
+ end
307
+
308
+ describe '#job(job_id)' do
309
+
310
+ it 'returns nil if there is no corresponding Job instance' do
311
+
312
+ @scheduler.job('nada').should == nil
313
+ end
314
+
315
+ it 'returns the corresponding Job instance' do
316
+
317
+ job_id = @scheduler.in '10d' do; end
318
+
319
+ sleep(1) # give it some time to get scheduled
320
+
321
+ @scheduler.job(job_id).job_id.should == job_id
322
+ end
323
+ end
324
+
325
+ # describe '#find_by_tag(t)' do
326
+ #
327
+ # it 'returns an empty list when there are no jobs with the given tag' do
328
+ #
329
+ # @scheduler.find_by_tag('nada').should == []
330
+ # end
331
+ #
332
+ # it 'returns all the jobs with the given tag' do
333
+ #
334
+ # @scheduler.in '10d', :tag => 't0' do; end
335
+ # @scheduler.every '2h', :tag => %w[ t0 t1 ] do; end
336
+ # @scheduler.every '3h' do; end
337
+ #
338
+ # @scheduler.find_by_tag('t0').map(&:original).should ==
339
+ # %w[ 2h 10d ]
340
+ # @scheduler.find_by_tag('t1').map(&:original).should ==
341
+ # %w[ 2h ]
342
+ # @scheduler.find_by_tag('t1', 't0').map(&:original).sort.should ==
343
+ # %w[ 2h ]
344
+ # end
345
+ # end
346
+
347
+ describe '#threads' do
348
+
349
+ it 'just lists the main thread (scheduler thread) when no job is scheduled' do
88
350
 
89
- sleep 2.5
351
+ @scheduler.threads.should == [ @scheduler.thread ]
352
+ end
90
353
 
91
- j.paused?.should == true
92
- $count.should == 0
354
+ it 'lists all the threads a scheduler uses' do
355
+
356
+ @scheduler.in '0s' do
357
+ sleep(2)
93
358
  end
94
359
 
95
- it 'pauses a job (cron)' do
360
+ sleep 0.4
96
361
 
97
- $count = 0
362
+ @scheduler.threads.size.should == 2
363
+ end
364
+ end
98
365
 
99
- j = @s.cron '* * * * * *' do
100
- $count = $count + 1
101
- end
366
+ describe '#work_threads(:all)' do
367
+
368
+ it 'returns an empty array when the scheduler has not yet done anything' do
102
369
 
103
- @s.pause(j.job_id)
370
+ @scheduler.work_threads.should == []
371
+ end
104
372
 
105
- sleep 2.5
373
+ it 'lists all the work threads in the pool' do
106
374
 
107
- j.paused?.should == true
108
- $count.should == 0
375
+ @scheduler.in '0s' do
376
+ # nada
377
+ end
378
+ @scheduler.in '0s' do
379
+ sleep(2)
109
380
  end
381
+
382
+ sleep 0.6
383
+
384
+ @scheduler.work_threads.size.should == 2
110
385
  end
386
+ end
387
+
388
+ describe '#work_threads(:vacant)' do
111
389
 
112
- describe '#resume' do
390
+ it 'returns an empty array when the scheduler has not yet done anything' do
113
391
 
114
- it 'resumes a job (every)' do
392
+ @scheduler.work_threads(:vacant).should == []
393
+ end
394
+
395
+ it 'lists all the work threads in the pool' do
396
+
397
+ @scheduler.in '0s' do
398
+ # nada
399
+ end
400
+ @scheduler.in '0s' do
401
+ sleep(2)
402
+ end
115
403
 
116
- $count = 0
404
+ sleep 0.4
117
405
 
118
- j = @s.every '1s' do
119
- $count = $count + 1
406
+ @scheduler.work_threads(:vacant).size.should == 1
407
+ end
408
+ end
409
+
410
+ describe '#work_threads(:active)' do
411
+
412
+ it 'returns [] when there are no jobs running' do
413
+
414
+ @scheduler.work_threads(:active).should == []
415
+ end
416
+
417
+ it 'returns the list of threads of the running jobs' do
418
+
419
+ job =
420
+ @scheduler.schedule_in('0s') do
421
+ sleep 1
120
422
  end
121
423
 
122
- @s.pause(j.job_id)
424
+ sleep 0.4
123
425
 
124
- sleep 2.5
426
+ @scheduler.work_threads(:active).size.should == 1
125
427
 
126
- c = $count
428
+ t = @scheduler.work_threads(:active).first
127
429
 
128
- @s.resume(j.job_id)
430
+ t.class.should == Thread
431
+ t[@scheduler.thread_key].should == true
432
+ t[:rufus_scheduler_job].should == job
433
+ t[:rufus_scheduler_time].should_not == nil
434
+ end
129
435
 
130
- sleep 1.5
436
+ it 'does not return threads from other schedulers' do
131
437
 
132
- j.paused?.should == false
133
- ($count > c).should == true
134
- end
438
+ scheduler = Rufus::Scheduler.new
135
439
 
136
- it 'pauses a job (cron)' do
440
+ job =
441
+ @scheduler.schedule_in('0s') do
442
+ sleep(1)
443
+ end
137
444
 
138
- $count = 0
445
+ sleep 0.4
446
+
447
+ scheduler.work_threads(:active).should == []
448
+
449
+ scheduler.shutdown
450
+ end
451
+ end
452
+
453
+ #describe '#min_work_threads' do
454
+ # it 'returns the min job thread count' do
455
+ # @scheduler.min_work_threads.should == 7
456
+ # end
457
+ #end
458
+ #describe '#min_work_threads=' do
459
+ # it 'sets the min job thread count' do
460
+ # @scheduler.min_work_threads = 1
461
+ # @scheduler.min_work_threads.should == 1
462
+ # end
463
+ #end
464
+
465
+ describe '#max_work_threads' do
466
+
467
+ it 'returns the max job thread count' do
468
+
469
+ @scheduler.max_work_threads.should == 35
470
+ end
471
+ end
139
472
 
140
- j = @s.cron '* * * * * *' do
141
- $count = $count + 1
473
+ describe '#max_work_threads=' do
474
+
475
+ it 'sets the max job thread count' do
476
+
477
+ @scheduler.max_work_threads = 14
478
+
479
+ @scheduler.max_work_threads.should == 14
480
+ end
481
+ end
482
+
483
+ #describe '#kill_all_work_threads' do
484
+ #
485
+ # it 'kills all the work threads' do
486
+ #
487
+ # @scheduler.in '0s' do; sleep(5); end
488
+ # @scheduler.in '0s' do; sleep(5); end
489
+ # @scheduler.in '0s' do; sleep(5); end
490
+ #
491
+ # sleep 0.5
492
+ #
493
+ # @scheduler.work_threads.size.should == 3
494
+ #
495
+ # @scheduler.send(:kill_all_work_threads)
496
+ #
497
+ # sleep 0.5
498
+ #
499
+ # @scheduler.work_threads.size.should == 0
500
+ # end
501
+ #end
502
+
503
+ describe '#running_jobs' do
504
+
505
+ it 'returns [] when there are no running jobs' do
506
+
507
+ @scheduler.running_jobs.should == []
508
+ end
509
+
510
+ it 'returns a list of running Job instances' do
511
+
512
+ job =
513
+ @scheduler.schedule_in('0s') do
514
+ sleep(1)
142
515
  end
143
516
 
144
- @s.pause(j.job_id)
517
+ sleep 0.4
518
+
519
+ job.running?.should == true
520
+ @scheduler.running_jobs.should == [ job ]
521
+ end
522
+
523
+ it 'does not return twice the same job' do
524
+
525
+ job =
526
+ @scheduler.schedule_every('0.3s') do
527
+ sleep(5)
528
+ end
145
529
 
146
- sleep 2.5
530
+ sleep 1.5
147
531
 
148
- c = $count
532
+ job.running?.should == true
533
+ @scheduler.running_jobs.should == [ job ]
534
+ end
535
+ end
149
536
 
150
- @s.resume(j.job_id)
537
+ describe '#running_jobs(:tag/:tags => x)' do
151
538
 
152
- sleep 1.5
539
+ it 'returns a list of running jobs filtered by tag' do
153
540
 
154
- j.paused?.should == false
155
- ($count > c).should == true
541
+ @scheduler.in '0.1s', :tag => 't0' do
542
+ sleep 3
543
+ end
544
+ @scheduler.in '0.2s', :tag => 't1' do
545
+ sleep 3
156
546
  end
547
+
548
+ sleep 0.4
549
+
550
+ @scheduler.running_jobs(:tag => 't0').map(&:original).should ==
551
+ %w[ 0.1s ]
552
+ @scheduler.running_jobs(:tag => 't1').map(&:original).should ==
553
+ %w[ 0.2s ]
554
+ @scheduler.running_jobs(:tags => %w[ t0 t1 ]).map(&:original).should ==
555
+ []
157
556
  end
158
557
  end
159
558
 
160
- context 'trigger threads' do
559
+ #--
560
+ # management methods
561
+ #++
161
562
 
162
- before(:each) do
163
- @s = start_scheduler
563
+ describe '#shutdown' do
564
+
565
+ it 'blanks the uptime' do
566
+
567
+ @scheduler.shutdown
568
+
569
+ @scheduler.uptime.should == nil
164
570
  end
165
- after(:each) do
166
- stop_scheduler(@s)
571
+
572
+ it 'shuts the scheduler down' do
573
+
574
+ @scheduler.shutdown
575
+
576
+ sleep 0.100
577
+ sleep 0.400 if RUBY_VERSION < '1.9.0'
578
+
579
+ t = Thread.list.find { |t|
580
+ t[:name] == "rufus_scheduler_#{@scheduler.object_id}"
581
+ }
582
+
583
+ t.should == nil
167
584
  end
168
585
 
169
- describe '#trigger_threads' do
586
+ it 'has a #stop alias' do
170
587
 
171
- it 'returns an empty list when no jobs are running' do
588
+ @scheduler.stop
172
589
 
173
- @s.trigger_threads.should == []
174
- end
590
+ @scheduler.uptime.should == nil
591
+ end
175
592
 
176
- it 'returns a list of the threads of the running jobs' do
593
+ #it 'has a #close alias'
594
+ end
177
595
 
178
- @s.in('100') { sleep 10 }
596
+ describe '#shutdown(:wait)' do
179
597
 
180
- sleep 0.5
598
+ it 'shuts down and blocks until all the jobs ended their current runs' do
181
599
 
182
- @s.trigger_threads.collect { |e| e.class }.should == [ Thread ]
600
+ counter = 0
601
+
602
+ @scheduler.in '0s' do
603
+ sleep 1
604
+ counter = counter + 1
183
605
  end
606
+
607
+ sleep 0.4
608
+
609
+ @scheduler.shutdown(:wait)
610
+
611
+ counter.should == 1
612
+ @scheduler.uptime.should == nil
613
+ @scheduler.running_jobs.should == []
614
+ @scheduler.threads.should == []
184
615
  end
616
+ end
185
617
 
186
- describe '#running_jobs' do
618
+ describe '#shutdown(:kill)' do
187
619
 
188
- it 'returns an empty list when no jobs are running' do
620
+ it 'kills all the jobs and then shuts down' do
189
621
 
190
- @s.running_jobs.should == []
622
+ counter = 0
623
+
624
+ @scheduler.in '0s' do
625
+ sleep 1
626
+ counter = counter + 1
627
+ end
628
+ @scheduler.at Time.now + 0.3 do
629
+ sleep 1
630
+ counter = counter + 1
191
631
  end
192
632
 
193
- it 'returns a list of the currently running jobs' do
633
+ sleep 0.4
634
+
635
+ @scheduler.shutdown(:kill)
636
+
637
+ sleep 1.4
638
+
639
+ counter.should == 0
640
+ @scheduler.uptime.should == nil
641
+ @scheduler.running_jobs.should == []
642
+ @scheduler.threads.should == []
643
+ end
644
+ end
645
+
646
+ describe '#pause' do
647
+
648
+ it 'pauses the scheduler' do
649
+
650
+ job = @scheduler.schedule_in '1s' do; end
651
+
652
+ @scheduler.pause
653
+
654
+ sleep(3)
655
+
656
+ job.last_time.should == nil
657
+ end
658
+ end
659
+
660
+ describe '#resume' do
661
+
662
+ it 'works' do
663
+
664
+ job = @scheduler.schedule_in '2s' do; end
194
665
 
195
- job = @s.in('100') { sleep 10 }
666
+ @scheduler.pause
667
+ sleep(1)
668
+ @scheduler.resume
669
+ sleep(2)
196
670
 
197
- sleep 0.5
671
+ job.last_time.should_not == nil
672
+ end
673
+ end
674
+
675
+ describe '#paused?' do
676
+
677
+ it 'returns true if the scheduler is paused' do
678
+
679
+ @scheduler.pause
680
+ @scheduler.paused?.should == true
681
+ end
198
682
 
199
- @s.running_jobs.should == [ job ]
683
+ it 'returns false if the scheduler is not paused' do
684
+
685
+ @scheduler.paused?.should == false
686
+
687
+ @scheduler.pause
688
+ @scheduler.resume
689
+
690
+ @scheduler.paused?.should == false
691
+ end
692
+ end
693
+
694
+ #--
695
+ # job methods
696
+ #++
697
+
698
+ describe '#jobs' do
699
+
700
+ it 'is empty at the beginning' do
701
+
702
+ @scheduler.jobs.should == []
703
+ end
704
+
705
+ it 'returns the list of scheduled jobs' do
706
+
707
+ @scheduler.in '10d' do; end
708
+ @scheduler.in '1w' do; end
709
+
710
+ sleep(1)
711
+
712
+ jobs = @scheduler.jobs
713
+
714
+ jobs.collect { |j| j.original }.sort.should == %w[ 10d 1w ]
715
+ end
716
+
717
+ it 'returns all the jobs (even those pending reschedule)' do
718
+
719
+ @scheduler.in '0s', :blocking => true do
720
+ sleep 2
200
721
  end
722
+
723
+ sleep 0.4
724
+
725
+ @scheduler.jobs.size.should == 1
726
+ end
727
+
728
+ it 'does not return unscheduled jobs' do
729
+
730
+ job =
731
+ @scheduler.schedule_in '0s', :blocking => true do
732
+ sleep 2
733
+ end
734
+
735
+ sleep 0.4
736
+
737
+ job.unschedule
738
+
739
+ @scheduler.jobs.size.should == 0
740
+ end
741
+ end
742
+
743
+ describe '#jobs(:tag / :tags => x)' do
744
+
745
+ it 'returns [] when there are no jobs with the corresponding tag' do
746
+
747
+ @scheduler.jobs(:tag => 'nada').should == []
748
+ @scheduler.jobs(:tags => %w[ nada hello ]).should == []
749
+ end
750
+
751
+ it 'returns the jobs with the corresponding tag' do
752
+
753
+ @scheduler.in '10d', :tag => 't0' do; end
754
+ @scheduler.every '2h', :tag => %w[ t0 t1 ] do; end
755
+ @scheduler.every '3h' do; end
756
+
757
+ @scheduler.jobs(:tags => 't0').map(&:original).sort.should ==
758
+ %w[ 10d 2h ]
759
+ @scheduler.jobs(:tags => 't1').map(&:original).sort.should ==
760
+ %w[ 2h ]
761
+ @scheduler.jobs(:tags => [ 't1', 't0' ]).map(&:original).sort.should ==
762
+ %w[ 2h ]
763
+ end
764
+ end
765
+
766
+ describe '#every_jobs' do
767
+
768
+ it 'returns EveryJob instances' do
769
+
770
+ @scheduler.at '2030/12/12 12:10:00' do; end
771
+ @scheduler.in '10d' do; end
772
+ @scheduler.every '5m' do; end
773
+
774
+ jobs = @scheduler.every_jobs
775
+
776
+ jobs.collect { |j| j.original }.sort.should == %w[ 5m ]
777
+ end
778
+ end
779
+
780
+ describe '#at_jobs' do
781
+
782
+ it 'returns AtJob instances' do
783
+
784
+ @scheduler.at '2030/12/12 12:10:00' do; end
785
+ @scheduler.in '10d' do; end
786
+ @scheduler.every '5m' do; end
787
+
788
+ jobs = @scheduler.at_jobs
789
+
790
+ jobs.collect { |j| j.original }.sort.should == [ '2030/12/12 12:10:00' ]
791
+ end
792
+ end
793
+
794
+ describe '#in_jobs' do
795
+
796
+ it 'returns InJob instances' do
797
+
798
+ @scheduler.at '2030/12/12 12:10:00' do; end
799
+ @scheduler.in '10d' do; end
800
+ @scheduler.every '5m' do; end
801
+
802
+ jobs = @scheduler.in_jobs
803
+
804
+ jobs.collect { |j| j.original }.sort.should == %w[ 10d ]
805
+ end
806
+ end
807
+
808
+ describe '#cron_jobs' do
809
+
810
+ it 'returns CronJob instances' do
811
+
812
+ @scheduler.at '2030/12/12 12:10:00' do; end
813
+ @scheduler.in '10d' do; end
814
+ @scheduler.every '5m' do; end
815
+ @scheduler.cron '* * * * *' do; end
816
+
817
+ jobs = @scheduler.cron_jobs
818
+
819
+ jobs.collect { |j| j.original }.sort.should == [ '* * * * *' ]
820
+ end
821
+ end
822
+
823
+ describe '#interval_jobs' do
824
+
825
+ it 'returns IntervalJob instances' do
826
+
827
+ @scheduler.at '2030/12/12 12:10:00' do; end
828
+ @scheduler.in '10d' do; end
829
+ @scheduler.every '5m' do; end
830
+ @scheduler.cron '* * * * *' do; end
831
+ @scheduler.interval '7m' do; end
832
+
833
+ jobs = @scheduler.interval_jobs
834
+
835
+ jobs.collect { |j| j.original }.sort.should == %w[ 7m ]
201
836
  end
202
837
  end
203
838
 
204
- context 'termination' do
839
+ #--
840
+ # callbacks
841
+ #++
205
842
 
206
- describe '#stop(true)' do
843
+ describe '#on_pre_trigger' do
207
844
 
208
- it 'terminates the scheduler, blocking until all the jobs are unscheduled' do
845
+ it 'is called right before a job triggers' do
209
846
 
210
- $every = nil
211
- $cron = nil
847
+ $out = []
212
848
 
213
- s = start_scheduler
214
- s.every '1s' do
215
- $every = :in
216
- sleep 0.5
217
- $every = :out
849
+ def @scheduler.on_pre_trigger(job)
850
+ $out << "pre #{job.id}"
851
+ end
852
+
853
+ job_id =
854
+ @scheduler.in '0.5s' do |job|
855
+ $out << job.id
218
856
  end
219
- s.cron '* * * * * *' do
220
- $cron = :in
221
- sleep 0.5
222
- $cron = :out
857
+
858
+ sleep 0.7
859
+
860
+ $out.should == [ "pre #{job_id}", job_id ]
861
+ end
862
+
863
+ it 'accepts the job and the triggerTime as argument' do
864
+
865
+ $tt = nil
866
+
867
+ def @scheduler.on_pre_trigger(job, trigger_time)
868
+ $tt = trigger_time
869
+ end
870
+
871
+ start = Time.now
872
+
873
+ @scheduler.in '0.5s' do; end
874
+
875
+ sleep 0.7
876
+
877
+ $tt.class.should == Time
878
+ $tt.should > start
879
+ $tt.should < Time.now
880
+ end
881
+
882
+ context 'when it returns false' do
883
+
884
+ it 'prevents the job from triggering' do
885
+
886
+ $out = []
887
+
888
+ def @scheduler.on_pre_trigger(job)
889
+ $out << "pre #{job.id}"
890
+ false
223
891
  end
224
892
 
225
- sleep 2
893
+ job_id =
894
+ @scheduler.in '0.5s' do |job|
895
+ $out << job.id
896
+ end
226
897
 
227
- s.stop(:terminate => true)
898
+ sleep 0.7
228
899
 
229
- s.jobs.size.should == 0
230
- $every.should == :out
231
- $cron.should == :out
900
+ $out.should == [ "pre #{job_id}" ]
232
901
  end
233
902
  end
234
903
  end
235
- end
236
904
 
237
- describe 'Rufus::Scheduler#start_new' do
905
+ describe '#on_post_trigger' do
906
+
907
+ it 'is called right after a job triggers' do
908
+
909
+ $out = []
238
910
 
239
- it 'piggybacks EM if present and running' do
911
+ def @scheduler.on_post_trigger(job)
912
+ $out << "post #{job.id}"
913
+ end
240
914
 
241
- s = Rufus::Scheduler.start_new
915
+ job_id =
916
+ @scheduler.in '0.5s' do |job|
917
+ $out << job.id
918
+ end
242
919
 
243
- s.class.should == SCHEDULER_CLASS
920
+ sleep 0.7
244
921
 
245
- stop_scheduler(s)
922
+ $out.should == [ job_id, "post #{job_id}" ]
923
+ end
924
+ end
925
+
926
+ #--
927
+ # misc
928
+ #++
929
+
930
+ describe '.singleton / .s' do
931
+
932
+ before(:each) do
933
+
934
+ Rufus::Scheduler.class_eval { @singleton = nil } # ;-)
935
+ end
936
+
937
+ it 'returns a singleton instance of the scheduler' do
938
+
939
+ s0 = Rufus::Scheduler.singleton
940
+ s1 = Rufus::Scheduler.s
941
+
942
+ s0.class.should == Rufus::Scheduler
943
+ s1.object_id.should == s0.object_id
944
+ end
945
+
946
+ it 'accepts initialization parameters' do
947
+
948
+ s = Rufus::Scheduler.singleton(:max_work_threads => 77)
949
+ s = Rufus::Scheduler.singleton(:max_work_threads => 42)
950
+
951
+ s.max_work_threads.should == 77
952
+ end
246
953
  end
247
954
  end
248
955