rufus-scheduler 2.0.19 → 2.0.20

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt CHANGED
@@ -2,6 +2,15 @@
2
2
  = rufus-scheduler CHANGELOG.txt
3
3
 
4
4
 
5
+ == rufus-scheduler - 2.0.20 released 2013/07/15
6
+
7
+ - add Rufus::Scheduler.new (so that rs 3.0 quickstarts are OK with 2.0.20)
8
+ - implement CronLine#previous_time(now=Time.now) (Idea Matteo Cerutti)
9
+ - throw ArgumentError for invalid cron lines (Thanks Aimee Rose)
10
+ - cron 0 vs 24 hour case straightening (Thanks Aimee Rose)
11
+ - support for sun#L or sun#-2 in cron lines
12
+
13
+
5
14
  == rufus-scheduler - 2.0.19 released 2013/05/07
6
15
 
7
16
  - raise ArgumentError on <= 0.0 "every" frequency (Thanks Lucy Fu)
data/CREDITS.txt CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  == Contributors
6
6
 
7
+ - Thomas Sevestre (https://github.com/thomassevestre) :exception option
8
+ - Matteo Cerutti - last_time / previous_time idea (and initial implementation)
9
+ - Aimee Rose (https://github.com/AimeeRose) cronline and > 24
7
10
  - Lucy Fu (https://github.com/lfu) arg error on <= 0 "every" freq
8
11
  - Rainux Luo (https://github.com/rainux) multiple mutexes
9
12
  - Andrew Davey (https://github.com/asdavey) "L" in cron lines
@@ -22,6 +25,7 @@
22
25
 
23
26
  == Feedback
24
27
 
28
+ - Hongli Lai - Scheduler#stop(:terminate => true) request
25
29
  - Tero Tilus - raises on unsupported/unknown options
26
30
  - Louis Coilliot - Scheduler#running_jobs
27
31
  - Henrique G. Testa - pause/resume concept
data/README.rdoc CHANGED
@@ -98,12 +98,18 @@ The timezones are the ones supported by the 'tzinfo' rubygem (http://tzinfo.ruby
98
98
 
99
99
  The timezone support was contributed by Tanzeeb Khalili.
100
100
 
101
- Since 2.0.9, "monthdays" are supported
101
+ "monthdays" are supported
102
102
 
103
103
  scheduler.cron '0 22 * * sun#1,sun#2' do
104
104
  # every first and second sunday of the month, at 22:00
105
105
  end
106
106
 
107
+ It's also OK (since 2.0.19) to use L (for last monthday) or negative numbers.
108
+
109
+ scheduler.cron '0 22 * * sun#-1' do
110
+ # every last sunday of the month, at 22:00
111
+ end
112
+
107
113
 
108
114
  == scheduler.join
109
115
 
@@ -132,6 +138,18 @@ You shouldn't be exposed to this issue when using EventMachine, since while runn
132
138
  DO NOT CALL this #join method if you're running rufus-scheduler from Rails or Sinatra or any application that's already some kind of 'daemon'. It's not necessary! #join is meant for small standalone scripts.
133
139
 
134
140
 
141
+ == schedule.stop
142
+
143
+ scheduler.stop
144
+
145
+ This call stops the scheduler. It doesn't unschedule jobs. If there are running jobs, they're left running.
146
+
147
+ If you need to stop the scheduler and wait for all the jobs currently running to finish (without killing them), rufus-scheduler 2.0.20 brings a new :terminate => true option.
148
+
149
+ scheduler.stop(:terminate => true)
150
+ # returns once all the jobs have been unscheduled and no jobs is running
151
+
152
+
135
153
  == block parameters
136
154
 
137
155
  Scheduled blocks accept 0 or 1 parameter (this unique parameter is the job
@@ -319,7 +337,7 @@ The chronic gem may help (http://chronic.rubyforge.org/) :
319
337
  require 'chronic' # sudo gem install chronic
320
338
 
321
339
  scheduler.every '3h', :first_at => Chronic.parse('this tuesday 5:00') do
322
- # do something starting this tueday
340
+ # do something starting this tuesday
323
341
  end
324
342
 
325
343
  Note : setting a :first_at/:first_in in the past will get rufus-scheduler to trigger for all the past schedules until now. Adding :discard_past => true will prevent this.
@@ -520,6 +538,15 @@ For backward compatibility, overriding #log_exception is still OK :
520
538
 
521
539
  Note that an every job or a cron job will stay scheduled even if it experiences an exception.
522
540
 
541
+ By default, all exceptions are rescued. It's easy to customize that behaviour :
542
+
543
+ scheduler = Rufus::Scheduler::PlainScheduler.start_new(:exception => StandardError)
544
+ # or
545
+ #scheduler = Rufus::Scheduler::EmScheduler.start_new(:exception => StandardError)
546
+
547
+ scheduler.in "3s" do
548
+ exit
549
+ end
523
550
 
524
551
  == frequency
525
552
 
@@ -555,7 +582,7 @@ More and more ruby applications are using EventMachine. This flavour of the sche
555
582
 
556
583
  == with Passenger
557
584
 
558
- "it terminates for no apparent reason !"
585
+ "it terminates for no apparent reason!"
559
586
 
560
587
  https://github.com/jmettraux/rufus-scheduler/issues/issue/10
561
588
 
@@ -576,22 +603,35 @@ The 'tzinfo' rubygem.
576
603
  The ruby gem 'eventmachine' if you use Rufus::Scheduler::EmScheduler, else no other dependencies.
577
604
 
578
605
 
579
- == mailing list
606
+ == support
607
+
608
+ If you identify and pinpoint a bug, please use the issue tracker. If you are unsure whether the fault lies in rufus-scheduler or in your software, use the mailing list. The mailing list is Google-powered, so, yes, you can search it.
609
+
610
+ Please read carefully: http://www.chiark.greenend.org.uk/~sgtatham/bugs.html (then re-read it).
611
+
612
+
613
+ === mailing list
580
614
 
581
615
  On the rufus-ruby list :
582
616
 
583
617
  http://groups.google.com/group/rufus-ruby
584
618
 
619
+ Newcomers' first message is held for moderation in order to prevent spam. Further messages are not held.
620
+
621
+
622
+ === issue tracker
585
623
 
586
- == issue tracker
624
+ https://github.com/jmettraux/rufus-scheduler/issues
587
625
 
588
- http://rubyforge.org/tracker/?atid=18584&group_id=4812&func=browse
589
626
 
627
+ === irc
590
628
 
591
- == irc
629
+ If you come over to #ruote to ask for rufus-scheduler help, please make sure to 1) say hello 2) be polite 3) state that you're looking for rufus-scheduler help 4) remember that we cannot read your mind and guess whatever lies in your deployment.
592
630
 
593
631
  irc.freenode.net #ruote
594
632
 
633
+ If there is no answer on IRC, use the mailing list.
634
+
595
635
 
596
636
  == source
597
637
 
@@ -618,3 +658,4 @@ http://rufus.rubyforge.org
618
658
  == license
619
659
 
620
660
  MIT
661
+
@@ -33,6 +33,9 @@ module Rufus
33
33
  #
34
34
  class CronLine
35
35
 
36
+ DAY_S = 24 * 3600
37
+ WEEK_S = 7 * DAY_S
38
+
36
39
  # The string used for creating this cronline instance.
37
40
  #
38
41
  attr_reader :original
@@ -74,7 +77,7 @@ module Rufus
74
77
 
75
78
  raise ArgumentError.new(
76
79
  "invalid cronline: '#{line}'"
77
- ) if es && es.find { |e| ! e.is_a?(Integer) }
80
+ ) if es && es.find { |e| ! e.is_a?(Fixnum) }
78
81
  end
79
82
  end
80
83
 
@@ -125,25 +128,21 @@ module Rufus
125
128
  time = @timezone ? @timezone.utc_to_local(now.getutc) : now
126
129
 
127
130
  time = time - time.usec * 1e-6 + 1
128
- # little adjustment before starting
131
+ # small adjustment before starting
129
132
 
130
133
  loop do
131
134
 
132
135
  unless date_match?(time)
133
- time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
134
- next
136
+ time += (24 - time.hour) * 3600 - time.min * 60 - time.sec; next
135
137
  end
136
138
  unless sub_match?(time, :hour, @hours)
137
- time += (60 - time.min) * 60 - time.sec
138
- next
139
+ time += (60 - time.min) * 60 - time.sec; next
139
140
  end
140
141
  unless sub_match?(time, :min, @minutes)
141
- time += 60 - time.sec
142
- next
142
+ time += 60 - time.sec; next
143
143
  end
144
144
  unless sub_match?(time, :sec, @seconds)
145
- time += 1
146
- next
145
+ time += 1; next
147
146
  end
148
147
 
149
148
  break
@@ -157,6 +156,28 @@ module Rufus
157
156
  time
158
157
  end
159
158
 
159
+ # Returns the previous the cronline matched. It's like next_time, but
160
+ # for the past.
161
+ #
162
+ def previous_time(now=Time.now)
163
+
164
+ # looks back by slices of two hours,
165
+ #
166
+ # finds for '* * * * sun', '* * 13 * *' and '0 12 13 * *'
167
+ # starting 1970, 1, 1 in 1.8 to 2 seconds (says Rspec)
168
+
169
+ start = current = now - 2 * 3600
170
+ result = nil
171
+
172
+ loop do
173
+ nex = next_time(current)
174
+ return (result ? result : previous_time(start)) if nex > now
175
+ result = current = nex
176
+ end
177
+
178
+ # never reached
179
+ end
180
+
160
181
  # Returns an array of 6 arrays (seconds, minutes, hours, days,
161
182
  # months, weekdays).
162
183
  # This method is used by the cronline unit tests.
@@ -190,15 +211,18 @@ module Rufus
190
211
 
191
212
  items.each do |it|
192
213
 
193
- if it.match(/#[12345]$/)
214
+ if m = it.match(/^(.+)#(l|-?[12345])$/)
194
215
 
195
216
  raise ArgumentError.new(
196
217
  "ranges are not supported for monthdays (#{it})"
197
- ) if it.index('-')
218
+ ) if m[1].index('-')
219
+
220
+ expr = it.gsub(/#l/, '#-1')
198
221
 
199
- (monthdays ||= []) << it
222
+ (monthdays ||= []) << expr
200
223
 
201
224
  else
225
+
202
226
  expr = it.dup
203
227
  WEEKDAYS.each_with_index { |a, i| expr.gsub!(/#{a}/, i.to_s) }
204
228
 
@@ -221,110 +245,109 @@ module Rufus
221
245
  def parse_item(item, min, max)
222
246
 
223
247
  return nil if item == '*'
224
- return [ 'L' ] if item == 'L'
225
- return parse_list(item, min, max) if item.index(',')
226
- return parse_range(item, min, max) if item.match(/[*-\/]/)
227
248
 
228
- i = item.to_i
229
-
230
- i = min if i < min
231
- i = max if i > max
232
-
233
- [ i ]
234
- end
235
-
236
- def parse_list(item, min, max)
237
-
238
- l = item.split(',').collect { |i| parse_range(i, min, max) }.flatten
249
+ r = item.split(',').map { |i| parse_range(i.strip, min, max) }.flatten
239
250
 
240
251
  raise ArgumentError.new(
241
252
  "found duplicates in #{item.inspect}"
242
- ) if l.uniq.size < l.size
253
+ ) if r.uniq.size < r.size
243
254
 
244
- l
255
+ r
245
256
  end
246
257
 
258
+ RANGE_REGEX = /^(\*|\d{1,2})(?:-(\d{1,2}))?(?:\/(\d{1,2}))?$/
259
+
247
260
  def parse_range(item, min, max)
248
261
 
249
- dash = item.index('-')
250
- slash = item.index('/')
262
+ return %w[ L ] if item == 'L'
251
263
 
252
- return parse_item(item, min, max) if (not slash) and (not dash)
264
+ m = item.match(RANGE_REGEX)
253
265
 
254
266
  raise ArgumentError.new(
255
- "'L' (end of month) is not accepted in ranges, " +
256
- "#{item.inspect} is not valid"
257
- ) if item.index('L')
267
+ "cannot parse #{item.inspect}"
268
+ ) unless m
258
269
 
259
- inc = slash ? item[slash + 1..-1].to_i : 1
270
+ sta = m[1]
271
+ sta = sta == '*' ? min : sta.to_i
260
272
 
261
- istart = -1
262
- iend = -1
273
+ edn = m[2]
274
+ edn = edn ? edn.to_i : sta
275
+ edn = max if m[1] == '*'
263
276
 
264
- if dash
277
+ inc = m[3]
278
+ inc = inc ? inc.to_i : 1
265
279
 
266
- istart = item[0..dash - 1].to_i
267
- iend = (slash ? item[dash + 1..slash - 1] : item[dash + 1..-1]).to_i
268
-
269
- else # case */x
270
-
271
- istart = min
272
- iend = max
273
- end
274
-
275
- istart = min if istart < min
276
- iend = max if iend > max
280
+ raise ArgumentError.new(
281
+ "#{item.inspect} is not in range #{min}..#{max}"
282
+ ) if sta < min or edn > max
277
283
 
278
- result = []
284
+ r = []
285
+ val = sta
279
286
 
280
- value = istart
281
287
  loop do
282
- result << value
283
- value = value + inc
284
- break if value > iend
288
+ v = val
289
+ v = 0 if max == 24 && v == 24
290
+ r << v
291
+ break if inc == 1 && val == edn
292
+ val += inc
293
+ break if inc > 1 && val > edn
294
+ val = min if val > max
285
295
  end
286
296
 
287
- result
297
+ r.uniq
288
298
  end
289
299
 
290
- def sub_match?(time, accessor, values=:none)
300
+ def sub_match?(time, accessor, values)
291
301
 
292
- value, values =
293
- if values == :none
294
- [ time, accessor ]
295
- else
296
- [ time.send(accessor), values ]
297
- end
302
+ value = time.send(accessor)
298
303
 
299
304
  return true if values.nil?
300
- return true if values.include?('L') && (time + 24 * 3600).day == 1
305
+ return true if values.include?('L') && (time + DAY_S).day == 1
306
+
307
+ return true if value == 0 && accessor == :hour && values.include?(24)
301
308
 
302
309
  values.include?(value)
303
310
  end
304
311
 
312
+ def monthday_match?(date, values)
313
+
314
+ return true if values.nil?
315
+
316
+ today_values = monthdays(date)
317
+
318
+ (today_values & values).any?
319
+ end
320
+
305
321
  def date_match?(date)
306
322
 
307
323
  return false unless sub_match?(date, :day, @days)
308
324
  return false unless sub_match?(date, :month, @months)
309
325
  return false unless sub_match?(date, :wday, @weekdays)
310
- return false unless sub_match?(CronLine.monthday(date), @monthdays)
326
+ return false unless monthday_match?(date, @monthdays)
311
327
  true
312
328
  end
313
329
 
314
- DAY_IN_SECONDS = 7 * 24 * 3600
330
+ def monthdays(date)
331
+
332
+ pos = 1
333
+ d = date.dup
315
334
 
316
- def self.monthday(date)
335
+ loop do
336
+ d = d - WEEK_S
337
+ break if d.month != date.month
338
+ pos = pos + 1
339
+ end
317
340
 
318
- count = 1
319
- date2 = date.dup
341
+ neg = -1
342
+ d = date.dup
320
343
 
321
344
  loop do
322
- date2 = date2 - DAY_IN_SECONDS
323
- break if date2.month != date.month
324
- count = count + 1
345
+ d = d + WEEK_S
346
+ break if d.month != date.month
347
+ neg = neg - 1
325
348
  end
326
349
 
327
- "#{WEEKDAYS[date.wday]}##{count}"
350
+ [ "#{WEEKDAYS[date.wday]}##{pos}", "#{WEEKDAYS[date.wday]}##{neg}" ]
328
351
  end
329
352
  end
330
353
  end
data/lib/rufus/sc/jobs.rb CHANGED
@@ -198,7 +198,7 @@ module Scheduler
198
198
 
199
199
  to_job.unschedule if to_job
200
200
 
201
- rescue Exception => e
201
+ rescue (@scheduler.options[:exception] || Exception) => e
202
202
 
203
203
  @scheduler.do_handle_exception(self, e)
204
204
  end
@@ -343,8 +343,6 @@ module Rufus
343
343
  at
344
344
  end
345
345
 
346
- protected # well, somehow
347
-
348
346
  DURATIONS2M = [
349
347
  [ 'y', 365 * 24 * 3600 ],
350
348
  [ 'M', 30 * 24 * 3600 ],
@@ -327,6 +327,21 @@ module Rufus::Scheduler
327
327
  }.compact
328
328
  end
329
329
 
330
+ # This is a blocking call, it will return when all the jobs have been
331
+ # unscheduled, waiting for any running one to finish before unscheduling
332
+ # it.
333
+ #
334
+ def terminate_all_jobs
335
+
336
+ all_jobs.each do |job_id, job|
337
+ job.unschedule
338
+ end
339
+
340
+ while running_jobs.size > 0
341
+ sleep 0.01
342
+ end
343
+ end
344
+
330
345
  protected
331
346
 
332
347
  # Returns a job queue instance.
@@ -455,9 +470,23 @@ module Rufus::Scheduler
455
470
  "#{self.class} - #{Rufus::Scheduler::VERSION}"
456
471
  end
457
472
 
473
+ # Stops this scheduler.
474
+ #
475
+ # == :terminate => true
476
+ #
477
+ # If the option :terminate is set to true,
478
+ # the method will return once all the jobs have been unscheduled and
479
+ # are done with their current run if any.
480
+ #
481
+ # (note that if a job is
482
+ # currently running, this method will wait for it to terminate, it
483
+ # will not interrupt the job run).
484
+ #
458
485
  def stop(opts={})
459
486
 
460
487
  @thread.exit
488
+
489
+ terminate_all_jobs if opts[:terminate]
461
490
  end
462
491
 
463
492
  def join
@@ -487,9 +516,24 @@ module Rufus::Scheduler
487
516
  end
488
517
  end
489
518
 
490
- def stop
519
+
520
+ # Stops this scheduler.
521
+ #
522
+ # == :terminate => true
523
+ #
524
+ # If the option :terminate is set to true,
525
+ # the method will return once all the jobs have been unscheduled and
526
+ # are done with their current run if any.
527
+ #
528
+ # (note that if a job is
529
+ # currently running, this method will wait for it to terminate, it
530
+ # will not interrupt the job run).
531
+ #
532
+ def stop(opts={})
491
533
 
492
534
  trap(@options[:signal] || 10)
535
+
536
+ terminate_all_jobs if opts[:terminate]
493
537
  end
494
538
  end
495
539
 
@@ -530,13 +574,27 @@ module Rufus::Scheduler
530
574
 
531
575
  # Stops the scheduler.
532
576
  #
577
+ # == :stop_em => true
578
+ #
533
579
  # If the :stop_em option is passed and set to true, it will stop the
534
580
  # EventMachine (but only if it started the EM by itself !).
535
581
  #
582
+ # == :terminate => true
583
+ #
584
+ # If the option :terminate is set to true,
585
+ # the method will return once all the jobs have been unscheduled and
586
+ # are done with their current run if any.
587
+ #
588
+ # (note that if a job is
589
+ # currently running, this method will wait for it to terminate, it
590
+ # will not interrupt the job run).
591
+ #
536
592
  def stop(opts={})
537
593
 
538
594
  @timer.cancel
539
595
 
596
+ terminate_all_jobs if opts[:terminate]
597
+
540
598
  EM.stop if opts[:stop_em] and @em_thread
541
599
  end
542
600
 
@@ -26,7 +26,7 @@
26
26
  module Rufus
27
27
  module Scheduler
28
28
 
29
- VERSION = '2.0.19'
29
+ VERSION = '2.0.20'
30
30
  end
31
31
  end
32
32
 
@@ -28,6 +28,13 @@ require 'rufus/sc/scheduler'
28
28
 
29
29
  module Rufus::Scheduler
30
30
 
31
+ # Starts and return a new instance of a PlainScheduler.
32
+ #
33
+ def self.new(opts={})
34
+
35
+ PlainScheduler.start_new(opts)
36
+ end
37
+
31
38
  # A quick way to get a scheduler up an running
32
39
  #
33
40
  # require 'rubygems'
@@ -24,13 +24,6 @@ describe Rufus::CronLine do
24
24
  cl(line).to_array.should == array
25
25
  end
26
26
 
27
- def local(*args)
28
- Time.local(*args)
29
- end
30
- def utc(*args)
31
- Time.utc(*args)
32
- end
33
-
34
27
  describe '.new' do
35
28
 
36
29
  it 'interprets cron strings correctly' do
@@ -54,6 +47,13 @@ describe Rufus::CronLine do
54
47
  to_a '1-5 * * * * *', [ [1,2,3,4,5], nil, nil, nil, nil, nil, nil, nil ]
55
48
 
56
49
  to_a '0 0 1 1 *', [ [0], [0], [0], [1], [1], nil, nil, nil ]
50
+
51
+ to_a '0 23-24 * * *', [ [0], [0], [23, 0], nil, nil, nil, nil, nil ]
52
+ #
53
+ # as reported by Aimee Rose in
54
+ # https://github.com/jmettraux/rufus-scheduler/issues/56
55
+
56
+ to_a '0 23-2 * * *', [ [0], [0], [23, 0, 1, 2], nil, nil, nil, nil, nil ]
57
57
  end
58
58
 
59
59
  it 'rejects invalid weekday expressions' do
@@ -84,7 +84,7 @@ describe Rufus::CronLine do
84
84
 
85
85
  to_a(
86
86
  '0 */2 * * *',
87
- [ [0], [0], (0..12).collect { |e| e * 2 }, nil, nil, nil, nil, nil ])
87
+ [ [0], [0], (0..11).collect { |e| e * 2 }, nil, nil, nil, nil, nil ])
88
88
  to_a(
89
89
  '0 7-23/2 * * *',
90
90
  [ [0], [0], (7..23).select { |e| e.odd? }, nil, nil, nil, nil, nil ])
@@ -105,7 +105,8 @@ describe Rufus::CronLine do
105
105
  to_a '09 * * * *', [ [0], [9], nil, nil, nil, nil, nil, nil ]
106
106
  to_a '09-12 * * * *', [ [0], [9, 10, 11, 12], nil, nil, nil, nil, nil, nil ]
107
107
  to_a '07-08 * * * *', [ [0], [7, 8], nil, nil, nil, nil, nil, nil ]
108
- to_a '* */08 * * *', [ [0], nil, [0, 8, 16, 24], nil, nil, nil, nil, nil ]
108
+ to_a '* */08 * * *', [ [0], nil, [0, 8, 16], nil, nil, nil, nil, nil ]
109
+ to_a '* */07 * * *', [ [0], nil, [0, 7, 14, 21], nil, nil, nil, nil, nil ]
109
110
  to_a '* 01-09/04 * * *', [ [0], nil, [1, 5, 9], nil, nil, nil, nil, nil ]
110
111
  to_a '* * * * 06', [ [0], nil, nil, nil, nil, [6], nil, nil ]
111
112
  end
@@ -132,6 +133,18 @@ describe Rufus::CronLine do
132
133
 
133
134
  lambda { cl '* L * * *'}.should raise_error(ArgumentError)
134
135
  end
136
+
137
+ it 'raises for out of range input' do
138
+
139
+ lambda { cl '60-62 * * * *'}.should raise_error(ArgumentError)
140
+ lambda { cl '62 * * * *'}.should raise_error(ArgumentError)
141
+ lambda { cl '60 * * * *'}.should raise_error(ArgumentError)
142
+ lambda { cl '* 25-26 * * *'}.should raise_error(ArgumentError)
143
+ lambda { cl '* 25 * * *'}.should raise_error(ArgumentError)
144
+ #
145
+ # as reported by Aimee Rose in
146
+ # https://github.com/jmettraux/rufus-scheduler/pull/58
147
+ end
135
148
  end
136
149
 
137
150
  describe '#next_time' do
@@ -151,9 +164,16 @@ describe Rufus::CronLine do
151
164
 
152
165
  nt('10 12 13 12 *', now).should == now + 29938200
153
166
  # this one is slow (1 year == 3 seconds)
167
+ #
168
+ # historical note:
169
+ # (comment made in 2006 or 2007, the underlying libs got better and
170
+ # that slowness is gone)
154
171
 
155
172
  nt('0 0 * * thu', now).should == now + 604800
156
173
 
174
+ nt('0 0 * * *', now).should == now + 24 * 3600
175
+ nt('0 24 * * *', now).should == now + 24 * 3600
176
+
157
177
  now = local(2008, 12, 31, 23, 59, 59, 0)
158
178
 
159
179
  nt('* * * * *', now).should == now + 1
@@ -225,34 +245,56 @@ describe Rufus::CronLine do
225
245
 
226
246
  it 'computes the next time correctly when there is a sun#2 involved' do
227
247
 
228
- now = local(1970, 1, 1)
248
+ nt('* * * * sun#1', local(1970, 1, 1)).should == local(1970, 1, 4)
249
+ nt('* * * * sun#2', local(1970, 1, 1)).should == local(1970, 1, 11)
229
250
 
230
- nt('* * * * sun#1', now).should == local(1970, 1, 4)
231
- nt('* * * * sun#2', now).should == local(1970, 1, 11)
251
+ nt('* * * * sun#2', local(1970, 1, 12)).should == local(1970, 2, 8)
252
+ end
232
253
 
233
- now = local(1970, 1, 12)
254
+ it 'computes the next time correctly when there is a sun#2,sun#3 involved' do
234
255
 
235
- nt('* * * * sun#2', now).should == local(1970, 2, 8)
256
+ nt('* * * * sun#2,sun#3', local(1970, 1, 1)).should == local(1970, 1, 11)
257
+ nt('* * * * sun#2,sun#3', local(1970, 1, 12)).should == local(1970, 1, 18)
236
258
  end
237
259
 
238
- it 'computes the next time correctly when there is a sun#2,sun#3 involved' do
260
+ it 'understands sun#L' do
239
261
 
240
- now = local(1970, 1, 1)
262
+ nt('* * * * sun#L', local(1970, 1, 1)).should == local(1970, 1, 25)
263
+ end
264
+
265
+ it 'understands sun#-1' do
266
+
267
+ nt('* * * * sun#-1', local(1970, 1, 1)).should == local(1970, 1, 25)
268
+ end
269
+
270
+ it 'understands sun#-2' do
271
+
272
+ nt('* * * * sun#-2', local(1970, 1, 1)).should == local(1970, 1, 18)
273
+ end
241
274
 
242
- nt('* * * * sun#2,sun#3', now).should == local(1970, 1, 11)
275
+ it 'computes the next time correctly when "L" (last day of month)' do
243
276
 
244
- now = local(1970, 1, 12)
277
+ nt('* * L * *', lo(1970, 1, 1)).should == lo(1970, 1, 31)
278
+ nt('* * L * *', lo(1970, 2, 1)).should == lo(1970, 2, 28)
279
+ nt('* * L * *', lo(1972, 2, 1)).should == lo(1972, 2, 29)
280
+ nt('* * L * *', lo(1970, 4, 1)).should == lo(1970, 4, 30)
281
+ end
282
+ end
245
283
 
246
- nt('* * * * sun#2,sun#3', now).should == local(1970, 1, 18)
284
+ describe '#previous_time' do
285
+
286
+ def pt(cronline, now)
287
+ Rufus::CronLine.new(cronline).previous_time(now)
247
288
  end
248
289
 
249
- it 'computes the next time correctly when there is a L (last day of month)' do
290
+ it 'returns the previous time the cron should have triggered' do
291
+
292
+ pt('* * * * sun', lo(1970, 1, 1)).should == lo(1969, 12, 28, 23, 59, 00)
293
+ pt('* * 13 * *', lo(1970, 1, 1)).should == lo(1969, 12, 13, 23, 59, 00)
294
+ pt('0 12 13 * *', lo(1970, 1, 1)).should == lo(1969, 12, 13, 12, 00)
250
295
 
251
- nt('* * L * *', local(1970,1,1)).should == local(1970, 1, 31)
252
- nt('* * L * *', local(1970,2,1)).should == local(1970, 2, 28)
253
- nt('* * L * *', local(1972,2,1)).should == local(1972, 2, 29)
254
- nt('* * L * *', local(1970,4,1)).should == local(1970, 4, 30)
255
- end
296
+ pt('* * * * * sun', lo(1970, 1, 1)).should == lo(1969, 12, 28, 23, 59, 59)
297
+ end
256
298
  end
257
299
 
258
300
  describe '#matches?' do
@@ -325,16 +367,21 @@ describe Rufus::CronLine do
325
367
  end
326
368
  end
327
369
 
328
- describe '.monthday' do
370
+ describe '#monthdays' do
329
371
 
330
372
  it 'returns the appropriate "sun#2"-like string' do
331
373
 
332
- d = local(1970, 1, 1)
333
- Rufus::CronLine.monthday(d).should == 'thu#1'
334
- Rufus::CronLine.monthday(d + 6 * 24 * 3600).should == 'wed#1'
335
- Rufus::CronLine.monthday(d + 13 * 24 * 3600).should == 'wed#2'
374
+ class Rufus::CronLine
375
+ public :monthdays
376
+ end
377
+
378
+ cl = Rufus::CronLine.new('* * * * *')
379
+
380
+ cl.monthdays(local(1970, 1, 1)).should == %w[ thu#1 thu#-5 ]
381
+ cl.monthdays(local(1970, 1, 7)).should == %w[ wed#1 wed#-4 ]
382
+ cl.monthdays(local(1970, 1, 14)).should == %w[ wed#2 wed#-3 ]
336
383
 
337
- Rufus::CronLine.monthday(local(2011, 3, 11)).should == 'fri#2'
384
+ cl.monthdays(local(2011, 3, 11)).should == %w[ fri#2 fri#-3 ]
338
385
  end
339
386
  end
340
387
  end
@@ -1,4 +1,3 @@
1
-
2
1
  #
3
2
  # Specifying rufus-scheduler
4
3
  #
@@ -93,5 +92,22 @@ describe SCHEDULER_CLASS do
93
92
  $j.class.should == Rufus::Scheduler::InJob
94
93
  $e.to_s.should == 'Houston we have a problem'
95
94
  end
95
+
96
+ it 'allow custom exception rescue' do
97
+ @s.options[:exception]= StandardError
98
+
99
+ job = @s.in 0 do
100
+ exit
101
+ end
102
+
103
+ @e= nil
104
+ begin
105
+ wait_next_tick
106
+ rescue SystemExit => e
107
+ @e= e
108
+ end
109
+
110
+ @e.should_not == nil
111
+ end
96
112
  end
97
113
 
data/spec/rtime_spec.rb CHANGED
@@ -105,7 +105,7 @@ describe 'rufus/rtime' do
105
105
  end
106
106
  end
107
107
 
108
- describe 'rufus/rtime#at_to_f' do
108
+ describe 'Rufus.at_to_f' do
109
109
 
110
110
  def atf(o)
111
111
  Rufus.at_to_f(o)
@@ -200,6 +200,38 @@ describe SCHEDULER_CLASS do
200
200
  end
201
201
  end
202
202
  end
203
+
204
+ context 'termination' do
205
+
206
+ describe '#stop(true)' do
207
+
208
+ it 'terminates the scheduler, blocking until all the jobs are unscheduled' do
209
+
210
+ $every = nil
211
+ $cron = nil
212
+
213
+ s = start_scheduler
214
+ s.every '1s' do
215
+ $every = :in
216
+ sleep 0.5
217
+ $every = :out
218
+ end
219
+ s.cron '* * * * * *' do
220
+ $cron = :in
221
+ sleep 0.5
222
+ $cron = :out
223
+ end
224
+
225
+ sleep 2
226
+
227
+ s.stop(:terminate => true)
228
+
229
+ s.jobs.size.should == 0
230
+ $every.should == :out
231
+ $cron.should == :out
232
+ end
233
+ end
234
+ end
203
235
  end
204
236
 
205
237
  describe 'Rufus::Scheduler#start_new' do
data/spec/spec_base.rb CHANGED
@@ -76,3 +76,12 @@ def wait_next_tick
76
76
  #end
77
77
  end
78
78
 
79
+ def local(*args)
80
+ Time.local(*args)
81
+ end
82
+ alias lo local
83
+
84
+ def utc(*args)
85
+ Time.utc(*args)
86
+ end
87
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufus-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.19
4
+ version: 2.0.20
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: 2013-05-06 00:00:00.000000000 Z
12
+ date: 2013-07-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: tzinfo
@@ -67,39 +67,38 @@ extensions: []
67
67
  extra_rdoc_files: []
68
68
  files:
69
69
  - Rakefile
70
- - lib/rufus/otime.rb
71
- - lib/rufus/sc/cronline.rb
72
- - lib/rufus/sc/jobqueues.rb
73
- - lib/rufus/sc/jobs.rb
74
- - lib/rufus/sc/rtime.rb
70
+ - lib/rufus-scheduler.rb
71
+ - lib/rufus/scheduler.rb
75
72
  - lib/rufus/sc/scheduler.rb
73
+ - lib/rufus/sc/rtime.rb
76
74
  - lib/rufus/sc/version.rb
77
- - lib/rufus/scheduler.rb
78
- - lib/rufus-scheduler.rb
79
- - spec/at_in_spec.rb
80
- - spec/at_spec.rb
81
- - spec/blocking_spec.rb
82
- - spec/cron_spec.rb
83
- - spec/cronline_spec.rb
84
- - spec/every_spec.rb
75
+ - lib/rufus/sc/cronline.rb
76
+ - lib/rufus/sc/jobs.rb
77
+ - lib/rufus/sc/jobqueues.rb
78
+ - lib/rufus/otime.rb
79
+ - spec/job_spec.rb
80
+ - spec/stress_schedule_unschedule_spec.rb
81
+ - spec/schedulable_spec.rb
85
82
  - spec/exception_spec.rb
86
83
  - spec/in_spec.rb
87
- - spec/job_spec.rb
88
- - spec/mutex_spec.rb
84
+ - spec/timeout_spec.rb
85
+ - spec/at_spec.rb
86
+ - spec/at_in_spec.rb
87
+ - spec/spec_base.rb
88
+ - spec/cronline_spec.rb
89
89
  - spec/rtime_spec.rb
90
- - spec/schedulable_spec.rb
90
+ - spec/mutex_spec.rb
91
91
  - spec/scheduler_spec.rb
92
- - spec/spec_base.rb
93
- - spec/stress_schedule_unschedule_spec.rb
94
- - spec/timeout_spec.rb
95
- - test/kjw.rb
92
+ - spec/blocking_spec.rb
93
+ - spec/every_spec.rb
94
+ - spec/cron_spec.rb
96
95
  - test/t.rb
96
+ - test/kjw.rb
97
97
  - rufus-scheduler.gemspec
98
98
  - CHANGELOG.txt
99
- - CREDITS.txt
100
- - LICENSE.txt
101
- - out.txt
102
99
  - TODO.txt
100
+ - LICENSE.txt
101
+ - CREDITS.txt
103
102
  - README.rdoc
104
103
  homepage: http://github.com/jmettraux/rufus-scheduler
105
104
  licenses: []
@@ -113,21 +112,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
112
  - - ! '>='
114
113
  - !ruby/object:Gem::Version
115
114
  version: '0'
116
- segments:
117
- - 0
118
- hash: -1513262691769122142
119
115
  required_rubygems_version: !ruby/object:Gem::Requirement
120
116
  none: false
121
117
  requirements:
122
118
  - - ! '>='
123
119
  - !ruby/object:Gem::Version
124
120
  version: '0'
125
- segments:
126
- - 0
127
- hash: -1513262691769122142
128
121
  requirements: []
129
122
  rubyforge_project: rufus
130
- rubygems_version: 1.8.24
123
+ rubygems_version: 1.8.23
131
124
  signing_key:
132
125
  specification_version: 3
133
126
  summary: job scheduler for Ruby (at, cron, in and every jobs)
data/out.txt DELETED
@@ -1,4 +0,0 @@
1
- ruby-1.8.7-p249 :001 > nada
2
- NameError: undefined local variable or method `nada' for #<Object:0x1001d1298>
3
- from (irb):1
4
- ruby-1.8.7-p249 :002 > ^D