rufus-scheduler 3.0.9 → 3.1.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/CHANGELOG.txt +7 -0
- data/CREDITS.txt +3 -0
- data/LICENSE.txt +1 -1
- data/README.md +6 -13
- data/lib/rufus/scheduler.rb +4 -3
- data/lib/rufus/scheduler/cronline.rb +78 -74
- data/lib/rufus/scheduler/job_array.rb +2 -1
- data/lib/rufus/scheduler/jobs.rb +16 -15
- data/lib/rufus/scheduler/locks.rb +1 -1
- data/lib/rufus/scheduler/util.rb +2 -25
- data/lib/rufus/scheduler/zones.rb +174 -0
- data/lib/rufus/scheduler/zotime.rb +154 -0
- data/rufus-scheduler.gemspec +2 -27
- data/spec/cronline_spec.rb +152 -103
- data/spec/job_cron_spec.rb +22 -0
- data/spec/job_every_spec.rb +14 -0
- data/spec/job_repeat_spec.rb +1 -0
- data/spec/parse_spec.rb +28 -30
- data/spec/schedule_at_spec.rb +1 -1
- data/spec/scheduler_spec.rb +4 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/zotime_spec.rb +396 -0
- metadata +15 -18
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
= rufus-scheduler CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-scheduler - 3.1.0 released 2015/04/18
|
6
|
+
|
7
|
+
- go without tzinfo (and its dependencies)
|
8
|
+
- include @ketan's #next_time improvements
|
9
|
+
- remove 2.x warning message on install
|
10
|
+
|
11
|
+
|
5
12
|
== rufus-scheduler - 3.0.9 released 2014/08/30
|
6
13
|
|
7
14
|
- fix TZ with underscores, thanks https://github.com/gnilrets
|
data/CREDITS.txt
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
== Contributors
|
6
6
|
|
7
|
+
- Ketan Padegaonkar (https://github.com/ketan) .brute_frequency improvement
|
8
|
+
- Gabriel Gilder (https://github.com/ggilder) LA DST specs
|
7
9
|
- Sterling Paramore (https://github.com/gnilrets) underscore TZ fix
|
8
10
|
- ecin (https://github.com/ecin) new lock mecha
|
9
11
|
- Adam Jonas (https://github.com/adamjonas) migrate specs to "expect"
|
@@ -33,6 +35,7 @@
|
|
33
35
|
|
34
36
|
== Feedback
|
35
37
|
|
38
|
+
- Michael Guymon - https://github.com/mguymon - #next_time vs :first_at
|
36
39
|
- junhanamaki - https://github.com/junhanamaki - #next_time and dst ambiguities
|
37
40
|
- kreynolds (tossrock) - inspiration for #occurrences
|
38
41
|
- Matteo - https://github.com/m4ce - dst and cron issue
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2005-
|
2
|
+
Copyright (c) 2005-2015, John Mettraux, jmettraux@gmail.com
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -61,6 +61,7 @@ It does not persist your schedules. When the process is gone and the scheduler i
|
|
61
61
|
|
62
62
|
* [whenever](https://github.com/javan/whenever) - let cron call back your Ruby code, trusted and reliable cron drives your schedule
|
63
63
|
* [clockwork](https://github.com/tomykaira/clockwork) - rufus-scheduler inspired gem
|
64
|
+
* [crono](https://github.com/plashchynski/crono) - an in-Rails cron scheduler
|
64
65
|
|
65
66
|
(please note: rufus-scheduler is not a cron replacement)
|
66
67
|
|
@@ -72,6 +73,11 @@ It's a complete rewrite of rufus-scheduler.
|
|
72
73
|
There is no EventMachine-based scheduler anymore.
|
73
74
|
|
74
75
|
|
76
|
+
## I don't know what this Ruby thing is, where are my Rails?
|
77
|
+
|
78
|
+
Sir, I'll drive you right to the [tracks](#so-rails).
|
79
|
+
|
80
|
+
|
75
81
|
## Notables changes:
|
76
82
|
|
77
83
|
* As said, no more EventMachine-based scheduler
|
@@ -1370,19 +1376,6 @@ Rufus::Scheduler.parse("2013-12-12 14:00 Pacific/Saipan")
|
|
1370
1376
|
# => 2013-12-12 04:00:00 UTC
|
1371
1377
|
```
|
1372
1378
|
|
1373
|
-
Behind the scenes, rufus-scheduler uses [tzinfo](http://tzinfo.github.io/) to deal with timezones.
|
1374
|
-
|
1375
|
-
Here is a [list of timezones](misc/tz_all.txt) known to my Debian GNU/Linux 7. It was generated with this script:
|
1376
|
-
|
1377
|
-
```ruby
|
1378
|
-
require 'tzinfo'
|
1379
|
-
TZInfo::Timezone.all.each { |tz| puts tz.name }
|
1380
|
-
```
|
1381
|
-
|
1382
|
-
Unknown timezones, typos, will be rejected by tzinfo thus rufus-scheduler.
|
1383
|
-
|
1384
|
-
On its own tzinfo derives the timezones from the system's information. On some system it needs some help, one can install the 'tzinfo-data' gem to provide the missing information.
|
1385
|
-
|
1386
1379
|
|
1387
1380
|
## so Rails?
|
1388
1381
|
|
data/lib/rufus/scheduler.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006-
|
2
|
+
# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -25,7 +25,7 @@
|
|
25
25
|
require 'date' if RUBY_VERSION < '1.9.0'
|
26
26
|
require 'time'
|
27
27
|
require 'thread'
|
28
|
-
require 'tzinfo'
|
28
|
+
#require 'tzinfo'
|
29
29
|
|
30
30
|
|
31
31
|
module Rufus
|
@@ -33,12 +33,13 @@ module Rufus
|
|
33
33
|
class Scheduler
|
34
34
|
|
35
35
|
require 'rufus/scheduler/util'
|
36
|
+
require 'rufus/scheduler/zotime'
|
36
37
|
require 'rufus/scheduler/jobs'
|
37
38
|
require 'rufus/scheduler/cronline'
|
38
39
|
require 'rufus/scheduler/job_array'
|
39
40
|
require 'rufus/scheduler/locks'
|
40
41
|
|
41
|
-
VERSION = '3.0
|
42
|
+
VERSION = '3.1.0'
|
42
43
|
|
43
44
|
#
|
44
45
|
# A common error class for rufus-scheduler
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006-
|
2
|
+
# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -22,6 +22,8 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
require 'set'
|
26
|
+
|
25
27
|
|
26
28
|
class Rufus::Scheduler
|
27
29
|
|
@@ -54,8 +56,7 @@ class Rufus::Scheduler
|
|
54
56
|
|
55
57
|
items = line.split
|
56
58
|
|
57
|
-
@timezone =
|
58
|
-
items.pop if @timezone
|
59
|
+
@timezone = items.pop if ZoTime.is_timezone?(items.last)
|
59
60
|
|
60
61
|
raise ArgumentError.new(
|
61
62
|
"not a valid cronline : '#{line}'"
|
@@ -82,9 +83,7 @@ class Rufus::Scheduler
|
|
82
83
|
#
|
83
84
|
def matches?(time)
|
84
85
|
|
85
|
-
time =
|
86
|
-
|
87
|
-
time = @timezone.utc_to_local(time.getutc) if @timezone
|
86
|
+
time = ZoTime.new(time.to_f, @timezone || ENV['TZ']).time
|
88
87
|
|
89
88
|
return false unless sub_match?(time, :sec, @seconds)
|
90
89
|
return false unless sub_match?(time, :min, @minutes)
|
@@ -122,37 +121,34 @@ class Rufus::Scheduler
|
|
122
121
|
#
|
123
122
|
def next_time(from=Time.now)
|
124
123
|
|
125
|
-
time =
|
126
|
-
|
127
|
-
|
128
|
-
# start at the next second
|
129
|
-
time = time + 1
|
124
|
+
time = nil
|
125
|
+
zotime = ZoTime.new(from.to_i + 1, @timezone || ENV['TZ'])
|
130
126
|
|
131
127
|
loop do
|
128
|
+
|
129
|
+
time = zotime.time
|
130
|
+
|
132
131
|
unless date_match?(time)
|
133
|
-
|
134
|
-
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
|
135
|
-
time -= 3600 if time.isdst != dst # not necessary for winter, but...
|
132
|
+
zotime.add((24 - time.hour) * 3600 - time.min * 60 - time.sec)
|
136
133
|
next
|
137
134
|
end
|
138
135
|
unless sub_match?(time, :hour, @hours)
|
139
|
-
|
136
|
+
zotime.add((60 - time.min) * 60 - time.sec)
|
137
|
+
next
|
140
138
|
end
|
141
139
|
unless sub_match?(time, :min, @minutes)
|
142
|
-
|
140
|
+
zotime.add(60 - time.sec)
|
141
|
+
next
|
143
142
|
end
|
144
143
|
unless sub_match?(time, :sec, @seconds)
|
145
|
-
time
|
144
|
+
zotime.add(next_second(time))
|
145
|
+
next
|
146
146
|
end
|
147
147
|
|
148
148
|
break
|
149
149
|
end
|
150
150
|
|
151
|
-
|
152
|
-
|
153
|
-
rescue TZInfo::PeriodNotFound
|
154
|
-
|
155
|
-
next_time(from + 3600)
|
151
|
+
time
|
156
152
|
end
|
157
153
|
|
158
154
|
# Returns the previous time the cronline matched. It's like next_time, but
|
@@ -160,35 +156,48 @@ class Rufus::Scheduler
|
|
160
156
|
#
|
161
157
|
def previous_time(from=Time.now)
|
162
158
|
|
163
|
-
time =
|
164
|
-
|
165
|
-
|
166
|
-
# start at the previous second
|
167
|
-
time = time - 1
|
159
|
+
time = nil
|
160
|
+
zotime = ZoTime.new(from.to_i - 1, @timezone || ENV['TZ'])
|
168
161
|
|
169
162
|
loop do
|
163
|
+
|
164
|
+
time = zotime.time
|
165
|
+
|
170
166
|
unless date_match?(time)
|
171
|
-
time
|
167
|
+
zotime.substract(time.hour * 3600 + time.min * 60 + time.sec + 1)
|
168
|
+
next
|
172
169
|
end
|
173
170
|
unless sub_match?(time, :hour, @hours)
|
174
|
-
time
|
171
|
+
zotime.substract(time.min * 60 + time.sec + 1)
|
172
|
+
next
|
175
173
|
end
|
176
174
|
unless sub_match?(time, :min, @minutes)
|
177
|
-
time
|
175
|
+
zotime.substract(time.sec + 1)
|
176
|
+
next
|
178
177
|
end
|
179
178
|
unless sub_match?(time, :sec, @seconds)
|
180
|
-
time
|
179
|
+
zotime.substract(prev_second(time))
|
180
|
+
next
|
181
181
|
end
|
182
182
|
|
183
183
|
break
|
184
184
|
end
|
185
185
|
|
186
|
-
|
187
|
-
|
188
|
-
rescue TZInfo::PeriodNotFound
|
186
|
+
time
|
187
|
+
end
|
189
188
|
|
190
|
-
|
189
|
+
if RUBY_VERSION >= '1.9'
|
190
|
+
def toa(item)
|
191
|
+
item == nil ? nil : item.to_a
|
192
|
+
end
|
193
|
+
else
|
194
|
+
def toi(item); item.is_a?(String) ? item.hash.abs : item.to_i; end
|
195
|
+
protected :toi
|
196
|
+
def toa(item)
|
197
|
+
item.is_a?(Set) ? item.to_a.sort_by { |e| toi(e) } : item
|
198
|
+
end
|
191
199
|
end
|
200
|
+
protected :toa
|
192
201
|
|
193
202
|
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
194
203
|
# months, weekdays).
|
@@ -197,14 +206,14 @@ class Rufus::Scheduler
|
|
197
206
|
def to_array
|
198
207
|
|
199
208
|
[
|
200
|
-
@seconds,
|
201
|
-
@minutes,
|
202
|
-
@hours,
|
203
|
-
@days,
|
204
|
-
@months,
|
205
|
-
@weekdays,
|
206
|
-
@monthdays,
|
207
|
-
@timezone
|
209
|
+
toa(@seconds),
|
210
|
+
toa(@minutes),
|
211
|
+
toa(@hours),
|
212
|
+
toa(@days),
|
213
|
+
toa(@months),
|
214
|
+
toa(@weekdays),
|
215
|
+
toa(@monthdays),
|
216
|
+
@timezone
|
208
217
|
]
|
209
218
|
end
|
210
219
|
|
@@ -212,7 +221,7 @@ class Rufus::Scheduler
|
|
212
221
|
# cron line.
|
213
222
|
#
|
214
223
|
# #brute_frequency, on the other hand, will compute the frequency by
|
215
|
-
# examining a whole, that can take more than seconds for a seconds
|
224
|
+
# examining a whole year, that can take more than seconds for a seconds
|
216
225
|
# level cron...
|
217
226
|
#
|
218
227
|
def frequency
|
@@ -220,9 +229,10 @@ class Rufus::Scheduler
|
|
220
229
|
return brute_frequency unless @seconds && @seconds.length > 1
|
221
230
|
|
222
231
|
delta = 60
|
223
|
-
|
232
|
+
secs = toa(@seconds)
|
233
|
+
prev = secs[0]
|
224
234
|
|
225
|
-
|
235
|
+
secs[1..-1].each do |sec|
|
226
236
|
d = sec - prev
|
227
237
|
delta = d if d < delta
|
228
238
|
end
|
@@ -270,7 +280,7 @@ class Rufus::Scheduler
|
|
270
280
|
delta = d if d < delta
|
271
281
|
|
272
282
|
break if @months == nil && t1.month == 2
|
273
|
-
break if t1.year
|
283
|
+
break if t1.year >= 2001
|
274
284
|
|
275
285
|
t0 = t1
|
276
286
|
end
|
@@ -280,6 +290,26 @@ class Rufus::Scheduler
|
|
280
290
|
|
281
291
|
protected
|
282
292
|
|
293
|
+
def next_second(time)
|
294
|
+
|
295
|
+
secs = @seconds.sort
|
296
|
+
|
297
|
+
return secs.last + 60 - time.sec if time.sec > secs.last
|
298
|
+
|
299
|
+
secs.shift while secs.first < time.sec
|
300
|
+
|
301
|
+
secs.first - time.sec
|
302
|
+
end
|
303
|
+
|
304
|
+
def prev_second(time)
|
305
|
+
|
306
|
+
secs = @seconds.sort
|
307
|
+
|
308
|
+
secs.pop while time.sec < secs.last
|
309
|
+
|
310
|
+
time.sec - secs.last
|
311
|
+
end
|
312
|
+
|
283
313
|
WEEKDAYS = %w[ sun mon tue wed thu fri sat ]
|
284
314
|
DAY_S = 24 * 3600
|
285
315
|
WEEK_S = 7 * DAY_S
|
@@ -336,7 +366,7 @@ class Rufus::Scheduler
|
|
336
366
|
"found duplicates in #{item.inspect}"
|
337
367
|
) if r.uniq.size < r.size
|
338
368
|
|
339
|
-
r
|
369
|
+
Set.new(r)
|
340
370
|
end
|
341
371
|
|
342
372
|
RANGE_REGEX = /^(\*|\d{1,2})(?:-(\d{1,2}))?(?:\/(\d{1,2}))?$/
|
@@ -435,32 +465,6 @@ class Rufus::Scheduler
|
|
435
465
|
|
436
466
|
[ "#{WEEKDAYS[date.wday]}##{pos}", "#{WEEKDAYS[date.wday]}##{neg}" ]
|
437
467
|
end
|
438
|
-
|
439
|
-
def local_time(time)
|
440
|
-
|
441
|
-
@timezone ? @timezone.utc_to_local(time.getutc) : time
|
442
|
-
end
|
443
|
-
|
444
|
-
def global_time(time, from_in_utc)
|
445
|
-
|
446
|
-
if @timezone
|
447
|
-
time =
|
448
|
-
begin
|
449
|
-
@timezone.local_to_utc(time)
|
450
|
-
rescue TZInfo::AmbiguousTime
|
451
|
-
@timezone.local_to_utc(time, time.isdst)
|
452
|
-
end
|
453
|
-
time = time.getlocal unless from_in_utc
|
454
|
-
end
|
455
|
-
|
456
|
-
time
|
457
|
-
end
|
458
|
-
|
459
|
-
def round_to_seconds(time)
|
460
|
-
|
461
|
-
# Ruby 1.8 doesn't have #round
|
462
|
-
time.respond_to?(:round) ? time.round : time - time.usec * 1e-6
|
463
|
-
end
|
464
468
|
end
|
465
469
|
end
|
466
470
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006-
|
2
|
+
# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -22,6 +22,7 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
|
25
26
|
module Rufus
|
26
27
|
|
27
28
|
class Scheduler
|
data/lib/rufus/scheduler/jobs.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006-
|
2
|
+
# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -340,7 +340,7 @@ module Rufus
|
|
340
340
|
|
341
341
|
def occurrences(time0, time1)
|
342
342
|
|
343
|
-
time >= time0 && time <= time1 ? [ time ] : []
|
343
|
+
(time >= time0 && time <= time1) ? [ time ] : []
|
344
344
|
end
|
345
345
|
|
346
346
|
protected
|
@@ -409,15 +409,17 @@ module Rufus
|
|
409
409
|
self.first_at =
|
410
410
|
opts[:first] || opts[:first_time] ||
|
411
411
|
opts[:first_at] || opts[:first_in] ||
|
412
|
-
|
412
|
+
nil
|
413
413
|
self.last_at =
|
414
414
|
opts[:last] || opts[:last_at] || opts[:last_in]
|
415
415
|
end
|
416
416
|
|
417
417
|
def first_at=(first)
|
418
418
|
|
419
|
+
return @first_at = nil if first == nil
|
420
|
+
|
419
421
|
n = Time.now
|
420
|
-
first = n + 0.
|
422
|
+
first = n + 0.003 if first == :now || first == :immediately
|
421
423
|
|
422
424
|
@first_at = Rufus::Scheduler.parse_to_time(first)
|
423
425
|
|
@@ -440,9 +442,6 @@ module Rufus
|
|
440
442
|
def trigger(time)
|
441
443
|
|
442
444
|
return if @paused_at
|
443
|
-
return if time < @first_at
|
444
|
-
#
|
445
|
-
# TODO: remove me when @first_at gets reworked
|
446
445
|
|
447
446
|
return (@next_time = nil) if @times && @times < 1
|
448
447
|
return (@next_time = nil) if @last_at && time >= @last_at
|
@@ -538,10 +537,8 @@ module Rufus
|
|
538
537
|
return if is_post
|
539
538
|
|
540
539
|
@next_time =
|
541
|
-
if
|
542
|
-
trigger_time + @frequency
|
543
|
-
elsif @first_at < Time.now
|
544
|
-
Time.now + @frequency
|
540
|
+
if @first_at == nil || @first_at < Time.now
|
541
|
+
(trigger_time || Time.now) + @frequency
|
545
542
|
else
|
546
543
|
@first_at
|
547
544
|
end
|
@@ -579,7 +576,7 @@ module Rufus
|
|
579
576
|
if is_post
|
580
577
|
Time.now + @interval
|
581
578
|
elsif trigger_time.nil?
|
582
|
-
if @first_at < Time.now
|
579
|
+
if @first_at == nil || @first_at < Time.now
|
583
580
|
Time.now + @interval
|
584
581
|
else
|
585
582
|
@first_at
|
@@ -602,7 +599,7 @@ module Rufus
|
|
602
599
|
super(scheduler, cronline, opts, block)
|
603
600
|
|
604
601
|
@cron_line = opts[:_t] || CronLine.new(cronline)
|
605
|
-
|
602
|
+
set_next_time(nil)
|
606
603
|
end
|
607
604
|
|
608
605
|
def frequency
|
@@ -619,12 +616,16 @@ module Rufus
|
|
619
616
|
|
620
617
|
def set_next_time(trigger_time, is_post=false)
|
621
618
|
|
622
|
-
@next_time =
|
619
|
+
@next_time = next_time_from(trigger_time || Time.now)
|
623
620
|
end
|
624
621
|
|
625
622
|
def next_time_from(time)
|
626
623
|
|
627
|
-
@
|
624
|
+
if @first_at == nil || @first_at < time
|
625
|
+
@cron_line.next_time(time)
|
626
|
+
else
|
627
|
+
@first_at
|
628
|
+
end
|
628
629
|
end
|
629
630
|
end
|
630
631
|
end
|