rufus-scheduler 1.0.7 → 1.0.8
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 +11 -0
- data/CREDITS.txt +1 -0
- data/README.txt +30 -2
- data/lib/rufus/cronline.rb +304 -0
- data/lib/rufus/otime.rb +14 -0
- data/lib/rufus/scheduler.rb +194 -447
- data/test/cronline_test.rb +0 -1
- data/test/scheduler_0_test.rb +7 -5
- data/test/scheduler_1_test.rb +3 -5
- data/test/scheduler_3_test.rb +1 -1
- data/test/scheduler_4_test.rb +1 -1
- data/test/scheduler_7_test.rb +51 -0
- data/test/test.rb +1 -0
- metadata +5 -3
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
= rufus-scheduler CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-scheduler - 1.0.8 released 2008/07/18
|
6
|
+
|
7
|
+
- todo #21251 : added 'at', 'cron', 'in' and 'every' shortcut methods
|
8
|
+
- todo #21203 : keeping track of At and EveryJob until last trigger is over
|
9
|
+
- todo #20823 : now raising exception in case of bad 'at' parameter
|
10
|
+
- todo #21194 : added trigger_thread to Job class
|
11
|
+
- todo #21193 : spinned CronLine out to rufus/cronline.rb
|
12
|
+
- bug #20893 : sometimes unschedule(every_job) not working when job was
|
13
|
+
active (performing). Fixed.
|
14
|
+
|
15
|
+
|
5
16
|
== rufus-scheduler - 1.0.7 released 2008/06/16
|
6
17
|
|
7
18
|
- todo #20669 : implemented CronJob.next_time()
|
data/CREDITS.txt
CHANGED
data/README.txt
CHANGED
@@ -17,7 +17,35 @@ http://rubyforge.org/frs/?group_id=4812
|
|
17
17
|
|
18
18
|
== usage
|
19
19
|
|
20
|
-
|
20
|
+
some examples :
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'rufus/scheduler'
|
24
|
+
|
25
|
+
scheduler = Rufus::Scheduler.start_new
|
26
|
+
|
27
|
+
scheduler.in("3d") do
|
28
|
+
regenerate_monthly_report()
|
29
|
+
end
|
30
|
+
#
|
31
|
+
# will call the regenerate_monthly_report method
|
32
|
+
# in 3 days from now
|
33
|
+
|
34
|
+
scheduler.every "10m10s" do
|
35
|
+
check_score(favourite_team) # every 10 minutes and 10 seconds
|
36
|
+
end
|
37
|
+
|
38
|
+
scheduler.cron "0 22 * * 1-5" do
|
39
|
+
log.info "activating security system..."
|
40
|
+
activate_security_system()
|
41
|
+
end
|
42
|
+
|
43
|
+
job_id = scheduler.at "Sun Oct 07 14:24:01 +0900 2009" do
|
44
|
+
init_self_destruction_sequence()
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
For all the scheduling related information, see the Rufus::Scheduler class rdoc itself (http://rufus.rubyforge.org/rufus-scheduler/classes/Rufus/Scheduler.html) or the original OpenWFEru scheduler documentation at http://openwferu.rubyforge.org/scheduler.html
|
21
49
|
|
22
50
|
Apart from scheduling, There are also two interesting methods in this gem, they are named parse_time_string and to_time_string :
|
23
51
|
|
@@ -66,7 +94,7 @@ http://github.com/jmettraux/rufus-scheduler
|
|
66
94
|
|
67
95
|
== author
|
68
96
|
|
69
|
-
John Mettraux, jmettraux@gmail.com
|
97
|
+
John Mettraux, jmettraux@gmail.com
|
70
98
|
http://jmettraux.wordpress.com
|
71
99
|
|
72
100
|
|
@@ -0,0 +1,304 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2006-2008, John Mettraux, jmettraux@gmail.com
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
#
|
24
|
+
|
25
|
+
#
|
26
|
+
# "made in Japan"
|
27
|
+
#
|
28
|
+
# John Mettraux at openwfe.org
|
29
|
+
#
|
30
|
+
|
31
|
+
module Rufus
|
32
|
+
|
33
|
+
#
|
34
|
+
# A 'cron line' is a line in the sense of a crontab
|
35
|
+
# (man 5 crontab) file line.
|
36
|
+
#
|
37
|
+
class CronLine
|
38
|
+
|
39
|
+
#
|
40
|
+
# The string used for creating this cronline instance.
|
41
|
+
#
|
42
|
+
attr_reader :original
|
43
|
+
|
44
|
+
attr_reader \
|
45
|
+
:seconds,
|
46
|
+
:minutes,
|
47
|
+
:hours,
|
48
|
+
:days,
|
49
|
+
:months,
|
50
|
+
:weekdays
|
51
|
+
|
52
|
+
def initialize (line)
|
53
|
+
|
54
|
+
super()
|
55
|
+
|
56
|
+
@original = line
|
57
|
+
|
58
|
+
items = line.split
|
59
|
+
|
60
|
+
unless [ 5, 6 ].include?(items.length)
|
61
|
+
raise \
|
62
|
+
"cron '#{line}' string should hold 5 or 6 items, " +
|
63
|
+
"not #{items.length}" \
|
64
|
+
end
|
65
|
+
|
66
|
+
offset = items.length - 5
|
67
|
+
|
68
|
+
@seconds = if offset == 1
|
69
|
+
parse_item(items[0], 0, 59)
|
70
|
+
else
|
71
|
+
[ 0 ]
|
72
|
+
end
|
73
|
+
@minutes = parse_item(items[0+offset], 0, 59)
|
74
|
+
@hours = parse_item(items[1+offset], 0, 24)
|
75
|
+
@days = parse_item(items[2+offset], 1, 31)
|
76
|
+
@months = parse_item(items[3+offset], 1, 12)
|
77
|
+
@weekdays = parse_weekdays(items[4+offset])
|
78
|
+
|
79
|
+
#adjust_arrays()
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Returns true if the given time matches this cron line.
|
84
|
+
#
|
85
|
+
# (the precision is passed as well to determine if it's
|
86
|
+
# worth checking seconds and minutes)
|
87
|
+
#
|
88
|
+
def matches? (time)
|
89
|
+
#def matches? (time, precision)
|
90
|
+
|
91
|
+
time = Time.at(time) unless time.kind_of?(Time)
|
92
|
+
|
93
|
+
return false \
|
94
|
+
if no_match?(time.sec, @seconds)
|
95
|
+
#if precision <= 1 and no_match?(time.sec, @seconds)
|
96
|
+
return false \
|
97
|
+
if no_match?(time.min, @minutes)
|
98
|
+
#if precision <= 60 and no_match?(time.min, @minutes)
|
99
|
+
return false \
|
100
|
+
if no_match?(time.hour, @hours)
|
101
|
+
return false \
|
102
|
+
if no_match?(time.day, @days)
|
103
|
+
return false \
|
104
|
+
if no_match?(time.month, @months)
|
105
|
+
return false \
|
106
|
+
if no_match?(time.wday, @weekdays)
|
107
|
+
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
113
|
+
# months, weekdays).
|
114
|
+
# This method is used by the cronline unit tests.
|
115
|
+
#
|
116
|
+
def to_array
|
117
|
+
|
118
|
+
[ @seconds, @minutes, @hours, @days, @months, @weekdays ]
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Returns the next time that this cron line is supposed to 'fire'
|
123
|
+
#
|
124
|
+
# This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
|
125
|
+
#
|
126
|
+
def next_time (now = Time.now)
|
127
|
+
|
128
|
+
#
|
129
|
+
# position now to the next cron second
|
130
|
+
|
131
|
+
if @seconds
|
132
|
+
next_sec = @seconds.find { |s| s > now.sec } || 60 + @seconds.first
|
133
|
+
now += next_sec - now.sec
|
134
|
+
else
|
135
|
+
now += 1
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# prepare sec jump array
|
140
|
+
|
141
|
+
sjarray = nil
|
142
|
+
|
143
|
+
if @seconds
|
144
|
+
|
145
|
+
sjarray = []
|
146
|
+
|
147
|
+
i = @seconds.index(now.sec)
|
148
|
+
ii = i
|
149
|
+
|
150
|
+
loop do
|
151
|
+
cur = @seconds[ii]
|
152
|
+
ii += 1
|
153
|
+
ii = 0 if ii == @seconds.size
|
154
|
+
nxt = @seconds[ii]
|
155
|
+
nxt += 60 if ii == 0
|
156
|
+
sjarray << (nxt - cur)
|
157
|
+
break if ii == i
|
158
|
+
end
|
159
|
+
|
160
|
+
else
|
161
|
+
|
162
|
+
sjarray = [ 1 ]
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# ok, seek...
|
167
|
+
|
168
|
+
i = 0
|
169
|
+
|
170
|
+
loop do
|
171
|
+
return now if matches?(now)
|
172
|
+
now += sjarray[i]
|
173
|
+
i += 1
|
174
|
+
i = 0 if i == sjarray.size
|
175
|
+
# danger... potentially no exit...
|
176
|
+
end
|
177
|
+
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
#--
|
184
|
+
# adjust values to Ruby
|
185
|
+
#
|
186
|
+
#def adjust_arrays()
|
187
|
+
# @hours = @hours.collect { |h|
|
188
|
+
# if h == 24
|
189
|
+
# 0
|
190
|
+
# else
|
191
|
+
# h
|
192
|
+
# end
|
193
|
+
# } if @hours
|
194
|
+
# @weekdays = @weekdays.collect { |wd|
|
195
|
+
# wd - 1
|
196
|
+
# } if @weekdays
|
197
|
+
#end
|
198
|
+
#
|
199
|
+
# dead code, keeping it as a reminder
|
200
|
+
#++
|
201
|
+
|
202
|
+
WDS = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ]
|
203
|
+
#
|
204
|
+
# used by parse_weekday()
|
205
|
+
|
206
|
+
def parse_weekdays (item)
|
207
|
+
|
208
|
+
item = item.downcase
|
209
|
+
|
210
|
+
WDS.each_with_index do |day, index|
|
211
|
+
item = item.gsub day, "#{index}"
|
212
|
+
end
|
213
|
+
|
214
|
+
r = parse_item item, 0, 7
|
215
|
+
|
216
|
+
return r unless r.is_a?(Array)
|
217
|
+
|
218
|
+
r.collect { |e| e == 7 ? 0 : e }.uniq
|
219
|
+
end
|
220
|
+
|
221
|
+
def parse_item (item, min, max)
|
222
|
+
|
223
|
+
return nil \
|
224
|
+
if item == "*"
|
225
|
+
return parse_list(item, min, max) \
|
226
|
+
if item.index(",")
|
227
|
+
return parse_range(item, min, max) \
|
228
|
+
if item.index("*") or item.index("-")
|
229
|
+
|
230
|
+
i = Integer(item)
|
231
|
+
|
232
|
+
i = min if i < min
|
233
|
+
i = max if i > max
|
234
|
+
|
235
|
+
[ i ]
|
236
|
+
end
|
237
|
+
|
238
|
+
def parse_list (item, min, max)
|
239
|
+
|
240
|
+
items = item.split(",")
|
241
|
+
|
242
|
+
items.inject([]) { |r, i| r.push(parse_range(i, min, max)) }.flatten
|
243
|
+
end
|
244
|
+
|
245
|
+
def parse_range (item, min, max)
|
246
|
+
|
247
|
+
i = item.index("-")
|
248
|
+
j = item.index("/")
|
249
|
+
|
250
|
+
return item.to_i if (not i and not j)
|
251
|
+
|
252
|
+
inc = 1
|
253
|
+
|
254
|
+
inc = Integer(item[j+1..-1]) if j
|
255
|
+
|
256
|
+
istart = -1
|
257
|
+
iend = -1
|
258
|
+
|
259
|
+
if i
|
260
|
+
|
261
|
+
istart = Integer(item[0..i-1])
|
262
|
+
|
263
|
+
if j
|
264
|
+
iend = Integer(item[i+1..j])
|
265
|
+
else
|
266
|
+
iend = Integer(item[i+1..-1])
|
267
|
+
end
|
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
|
277
|
+
|
278
|
+
result = []
|
279
|
+
|
280
|
+
value = istart
|
281
|
+
loop do
|
282
|
+
|
283
|
+
result << value
|
284
|
+
value = value + inc
|
285
|
+
break if value > iend
|
286
|
+
end
|
287
|
+
|
288
|
+
result
|
289
|
+
end
|
290
|
+
|
291
|
+
def no_match? (value, cron_values)
|
292
|
+
|
293
|
+
return false if not cron_values
|
294
|
+
|
295
|
+
cron_values.each do |v|
|
296
|
+
return false if value == v # ok, it matches
|
297
|
+
end
|
298
|
+
|
299
|
+
true # no match found
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
data/lib/rufus/otime.rb
CHANGED
@@ -336,6 +336,20 @@ module Rufus
|
|
336
336
|
h
|
337
337
|
end
|
338
338
|
|
339
|
+
#
|
340
|
+
# Ensures that a duration is a expressed as a Float instance.
|
341
|
+
#
|
342
|
+
# duration_to_f("10s")
|
343
|
+
#
|
344
|
+
# will yield 10.0
|
345
|
+
#
|
346
|
+
def Rufus.duration_to_f (s)
|
347
|
+
|
348
|
+
return s if s.kind_of?(Float)
|
349
|
+
return parse_time_string(s) if s.kind_of?(String)
|
350
|
+
Float(s.to_s)
|
351
|
+
end
|
352
|
+
|
339
353
|
protected
|
340
354
|
|
341
355
|
DURATIONS2M = [
|
data/lib/rufus/scheduler.rb
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
require 'thread'
|
32
32
|
require 'monitor'
|
33
33
|
require 'rufus/otime'
|
34
|
-
|
34
|
+
require 'rufus/cronline'
|
35
35
|
|
36
36
|
module Rufus
|
37
37
|
|
@@ -45,7 +45,7 @@ module Rufus
|
|
45
45
|
# params (usually an array or nil), either a block, which is more in the
|
46
46
|
# Ruby way.
|
47
47
|
#
|
48
|
-
# == The gem "
|
48
|
+
# == The gem "rufus-scheduler"
|
49
49
|
#
|
50
50
|
# This scheduler was previously known as the "openwferu-scheduler" gem.
|
51
51
|
#
|
@@ -114,6 +114,9 @@ module Rufus
|
|
114
114
|
# regenerate_latest_report()
|
115
115
|
# end
|
116
116
|
#
|
117
|
+
# (note : a schedule every isn't triggered immediately, thus this example
|
118
|
+
# will first trigger 1 hour and 20 minutes after being scheduled)
|
119
|
+
#
|
117
120
|
# The scheduler has a "exit_when_no_more_jobs" attribute. When set to
|
118
121
|
# 'true', the scheduler will exit as soon as there are no more jobs to
|
119
122
|
# run.
|
@@ -287,6 +290,20 @@ module Rufus
|
|
287
290
|
#
|
288
291
|
# scheduler.new :thread_name => "the crazy scheduler"
|
289
292
|
#
|
293
|
+
#
|
294
|
+
# == job.trigger_thread
|
295
|
+
#
|
296
|
+
# Since rufus-scheduler 1.0.8, you can have access to the thread of
|
297
|
+
# a job currently being triggered.
|
298
|
+
#
|
299
|
+
# job = scheduler.get_job(job_id)
|
300
|
+
# thread = job.trigger_thread
|
301
|
+
#
|
302
|
+
# This new method will return nil if the job is not currently being
|
303
|
+
# triggered. Not that in case of an every or cron job, this method
|
304
|
+
# will return the thread of the last triggered instance, thus, in case
|
305
|
+
# of overlapping executions, you only get the most recent thread.
|
306
|
+
#
|
290
307
|
class Scheduler
|
291
308
|
|
292
309
|
#
|
@@ -324,6 +341,7 @@ module Rufus
|
|
324
341
|
|
325
342
|
@pending_jobs = []
|
326
343
|
@cron_jobs = {}
|
344
|
+
@non_cron_jobs = {}
|
327
345
|
|
328
346
|
@schedule_queue = Queue.new
|
329
347
|
@unschedule_queue = Queue.new
|
@@ -346,7 +364,7 @@ module Rufus
|
|
346
364
|
#@correction = 0.00045
|
347
365
|
|
348
366
|
@exit_when_no_more_jobs = false
|
349
|
-
|
367
|
+
#@dont_reschedule_every = false
|
350
368
|
|
351
369
|
@last_cron_second = -1
|
352
370
|
|
@@ -467,6 +485,11 @@ module Rufus
|
|
467
485
|
&block)
|
468
486
|
end
|
469
487
|
|
488
|
+
#
|
489
|
+
# a shortcut for schedule_at
|
490
|
+
#
|
491
|
+
alias :at :schedule_at
|
492
|
+
|
470
493
|
|
471
494
|
#
|
472
495
|
# Schedules a job by stating in how much time it should trigger.
|
@@ -478,11 +501,16 @@ module Rufus
|
|
478
501
|
def schedule_in (duration, params={}, &block)
|
479
502
|
|
480
503
|
do_schedule_at(
|
481
|
-
Time.new.to_f + duration_to_f(duration),
|
504
|
+
Time.new.to_f + Rufus::duration_to_f(duration),
|
482
505
|
prepare_params(params),
|
483
506
|
&block)
|
484
507
|
end
|
485
508
|
|
509
|
+
#
|
510
|
+
# a shortcut for schedule_in
|
511
|
+
#
|
512
|
+
alias :in :schedule_in
|
513
|
+
|
486
514
|
#
|
487
515
|
# Schedules a job in a loop. After an execution, it will not execute
|
488
516
|
# before the time specified in 'freq'.
|
@@ -505,76 +533,33 @@ module Rufus
|
|
505
533
|
# # schedule something every two days, start in 5 hours...
|
506
534
|
# end
|
507
535
|
#
|
536
|
+
# (without setting a :first_in (or :first_at), our example schedule would
|
537
|
+
# have had been triggered after two days).
|
538
|
+
#
|
508
539
|
def schedule_every (freq, params={}, &block)
|
509
540
|
|
510
|
-
f = duration_to_f freq
|
511
|
-
|
512
541
|
params = prepare_params params
|
513
|
-
schedulable = params[:schedulable]
|
514
542
|
params[:every] = freq
|
515
543
|
|
516
|
-
first_at = params
|
517
|
-
first_in = params
|
518
|
-
|
519
|
-
previous_at = params[:previous_at]
|
544
|
+
first_at = params[:first_at]
|
545
|
+
first_in = params[:first_in]
|
520
546
|
|
521
|
-
|
522
|
-
first_at
|
547
|
+
first_at = if first_at
|
548
|
+
at_to_f(first_at)
|
523
549
|
elsif first_in
|
524
|
-
Time.now.to_f + duration_to_f(first_in)
|
525
|
-
elsif previous_at
|
526
|
-
previous_at + f
|
550
|
+
Time.now.to_f + Rufus.duration_to_f(first_in)
|
527
551
|
else
|
528
|
-
Time.now.to_f +
|
552
|
+
Time.now.to_f + Rufus.duration_to_f(freq) # not triggering immediately
|
529
553
|
end
|
530
554
|
|
531
|
-
do_schedule_at(
|
532
|
-
|
533
|
-
#
|
534
|
-
# trigger ...
|
535
|
-
|
536
|
-
hit_exception = false
|
537
|
-
|
538
|
-
begin
|
539
|
-
|
540
|
-
if schedulable
|
541
|
-
schedulable.trigger params
|
542
|
-
else
|
543
|
-
block.call job_id, at, params
|
544
|
-
end
|
545
|
-
|
546
|
-
rescue Exception => e
|
547
|
-
|
548
|
-
log_exception e
|
549
|
-
|
550
|
-
hit_exception = true
|
551
|
-
end
|
552
|
-
|
553
|
-
# cannot use a return here !!! (block)
|
554
|
-
|
555
|
-
unless \
|
556
|
-
@dont_reschedule_every or
|
557
|
-
(params[:dont_reschedule] == true) or
|
558
|
-
(hit_exception and params[:try_again] == false)
|
559
|
-
|
560
|
-
#
|
561
|
-
# ok, reschedule ...
|
562
|
-
|
563
|
-
params[:job_id] = job_id
|
564
|
-
params[:previous_at] = at
|
565
|
-
|
566
|
-
schedule_every params[:every], params, &block
|
567
|
-
#
|
568
|
-
# yes, this is a kind of recursion
|
569
|
-
|
570
|
-
# note that params[:every] might have been changed
|
571
|
-
# by the block/schedulable code
|
572
|
-
end
|
573
|
-
|
574
|
-
job_id
|
575
|
-
end
|
555
|
+
do_schedule_at(first_at, params, &block)
|
576
556
|
end
|
577
557
|
|
558
|
+
#
|
559
|
+
# a shortcut for schedule_every
|
560
|
+
#
|
561
|
+
alias :every :schedule_every
|
562
|
+
|
578
563
|
#
|
579
564
|
# Schedules a cron job, the 'cron_line' is a string
|
580
565
|
# following the Unix cron standard (see "man 5 crontab" in your command
|
@@ -607,11 +592,9 @@ module Rufus
|
|
607
592
|
#
|
608
593
|
# is a job with the same id already scheduled ?
|
609
594
|
|
610
|
-
cron_id = params[:cron_id]
|
611
|
-
cron_id = params[:job_id] unless cron_id
|
595
|
+
cron_id = params[:cron_id] || params[:job_id]
|
612
596
|
|
613
|
-
|
614
|
-
@unschedule_queue << [ :cron, cron_id ]
|
597
|
+
#@unschedule_queue << cron_id
|
615
598
|
|
616
599
|
#
|
617
600
|
# schedule
|
@@ -619,12 +602,16 @@ module Rufus
|
|
619
602
|
b = to_block(params, &block)
|
620
603
|
job = CronJob.new(self, cron_id, cron_line, params, &b)
|
621
604
|
|
622
|
-
#@cron_jobs[job.job_id] = job
|
623
605
|
@schedule_queue << job
|
624
606
|
|
625
607
|
job.job_id
|
626
608
|
end
|
627
609
|
|
610
|
+
#
|
611
|
+
# an alias for schedule()
|
612
|
+
#
|
613
|
+
alias :cron :schedule
|
614
|
+
|
628
615
|
#--
|
629
616
|
#
|
630
617
|
# The UNscheduling methods
|
@@ -637,15 +624,17 @@ module Rufus
|
|
637
624
|
#
|
638
625
|
def unschedule (job_id)
|
639
626
|
|
640
|
-
@unschedule_queue <<
|
627
|
+
@unschedule_queue << job_id
|
641
628
|
end
|
642
629
|
|
643
630
|
#
|
644
631
|
# Unschedules a cron job
|
645
632
|
#
|
633
|
+
# (deprecated : use unschedule(job_id) for all the jobs !)
|
634
|
+
#
|
646
635
|
def unschedule_cron_job (job_id)
|
647
636
|
|
648
|
-
|
637
|
+
unschedule(job_id)
|
649
638
|
end
|
650
639
|
|
651
640
|
#--
|
@@ -660,7 +649,7 @@ module Rufus
|
|
660
649
|
#
|
661
650
|
def get_job (job_id)
|
662
651
|
|
663
|
-
@cron_jobs[job_id] || @
|
652
|
+
@cron_jobs[job_id] || @non_cron_jobs[job_id]
|
664
653
|
end
|
665
654
|
|
666
655
|
#
|
@@ -669,13 +658,8 @@ module Rufus
|
|
669
658
|
#
|
670
659
|
def get_schedulable (job_id)
|
671
660
|
|
672
|
-
#return nil unless job_id
|
673
|
-
|
674
661
|
j = get_job(job_id)
|
675
|
-
|
676
|
-
return j.schedulable if j.respond_to?(:schedulable)
|
677
|
-
|
678
|
-
nil
|
662
|
+
j.respond_to?(:schedulable) ? j.schedulable : nil
|
679
663
|
end
|
680
664
|
|
681
665
|
#
|
@@ -684,7 +668,7 @@ module Rufus
|
|
684
668
|
def find_jobs (tag)
|
685
669
|
|
686
670
|
@cron_jobs.values.find_all { |job| job.has_tag?(tag) } +
|
687
|
-
@
|
671
|
+
@non_cron_jobs.values.find_all { |job| job.has_tag?(tag) }
|
688
672
|
end
|
689
673
|
|
690
674
|
#
|
@@ -720,7 +704,7 @@ module Rufus
|
|
720
704
|
#
|
721
705
|
def every_job_count
|
722
706
|
|
723
|
-
@
|
707
|
+
@non_cron_jobs.values.select { |j| j.class == EveryJob }.size
|
724
708
|
end
|
725
709
|
|
726
710
|
#
|
@@ -728,36 +712,43 @@ module Rufus
|
|
728
712
|
#
|
729
713
|
def at_job_count
|
730
714
|
|
731
|
-
@
|
715
|
+
@non_cron_jobs.values.select { |j| j.class == AtJob }.size
|
732
716
|
end
|
733
717
|
|
734
718
|
#
|
735
719
|
# Returns true if the given string seems to be a cron string.
|
736
720
|
#
|
737
|
-
def
|
721
|
+
def self.is_cron_string (s)
|
738
722
|
|
739
723
|
s.match ".+ .+ .+ .+ .+" # well...
|
740
724
|
end
|
741
725
|
|
742
726
|
private
|
743
727
|
|
728
|
+
#
|
729
|
+
# the unschedule work itself.
|
730
|
+
#
|
744
731
|
def do_unschedule (job_id)
|
745
732
|
|
733
|
+
job = get_job job_id
|
734
|
+
|
735
|
+
return (@cron_jobs.delete(job_id) != nil) if job.is_a?(CronJob)
|
736
|
+
|
737
|
+
return false unless job # not found
|
738
|
+
|
739
|
+
if job.is_a?(AtJob) # catches AtJob and EveryJob instances
|
740
|
+
@non_cron_jobs.delete(job_id)
|
741
|
+
job.params[:dont_reschedule] = true # for AtJob as well, no worries
|
742
|
+
end
|
743
|
+
|
746
744
|
for i in 0...@pending_jobs.length
|
747
745
|
if @pending_jobs[i].job_id == job_id
|
748
746
|
@pending_jobs.delete_at i
|
749
|
-
return true
|
747
|
+
return true # asap
|
750
748
|
end
|
751
749
|
end
|
752
|
-
#
|
753
|
-
# not using delete_if because it scans the whole list
|
754
|
-
|
755
|
-
do_unschedule_cron_job job_id
|
756
|
-
end
|
757
750
|
|
758
|
-
|
759
|
-
|
760
|
-
(@cron_jobs.delete(job_id) != nil)
|
751
|
+
true
|
761
752
|
end
|
762
753
|
|
763
754
|
#
|
@@ -765,9 +756,7 @@ module Rufus
|
|
765
756
|
#
|
766
757
|
def prepare_params (params)
|
767
758
|
|
768
|
-
params
|
769
|
-
if params.is_a?(Schedulable)
|
770
|
-
params
|
759
|
+
params.is_a?(Schedulable) ? { :schedulable => params } : params
|
771
760
|
end
|
772
761
|
|
773
762
|
#
|
@@ -776,47 +765,33 @@ module Rufus
|
|
776
765
|
#
|
777
766
|
def do_schedule_at (at, params={}, &block)
|
778
767
|
|
779
|
-
|
768
|
+
job = params.delete :job
|
780
769
|
|
781
|
-
|
770
|
+
unless job
|
782
771
|
|
783
|
-
|
772
|
+
jobClass = params[:every] ? EveryJob : AtJob
|
784
773
|
|
785
|
-
|
774
|
+
b = to_block params, &block
|
786
775
|
|
787
|
-
|
788
|
-
|
789
|
-
b = to_block params, &block
|
776
|
+
job = jobClass.new self, at_to_f(at), params[:job_id], params, &b
|
777
|
+
end
|
790
778
|
|
791
|
-
|
779
|
+
if jobClass == AtJob && job.at < (Time.new.to_f + @precision)
|
792
780
|
|
793
|
-
|
781
|
+
job.trigger() unless params[:discard_past]
|
794
782
|
|
795
|
-
|
783
|
+
@non_cron_jobs.delete job.job_id # just to be sure
|
796
784
|
|
797
|
-
job.trigger() unless params[:discard_past]
|
798
785
|
return nil
|
799
786
|
end
|
800
787
|
|
788
|
+
@non_cron_jobs[job.job_id] = job
|
789
|
+
|
801
790
|
@schedule_queue << job
|
802
791
|
|
803
792
|
job.job_id
|
804
793
|
end
|
805
794
|
|
806
|
-
#
|
807
|
-
# Ensures that a duration is a expressed as a Float instance.
|
808
|
-
#
|
809
|
-
# duration_to_f("10s")
|
810
|
-
#
|
811
|
-
# will yields 10.0
|
812
|
-
#
|
813
|
-
def duration_to_f (s)
|
814
|
-
|
815
|
-
return s if s.kind_of?(Float)
|
816
|
-
return Rufus::parse_time_string(s) if s.kind_of?(String)
|
817
|
-
Float(s.to_s)
|
818
|
-
end
|
819
|
-
|
820
795
|
#
|
821
796
|
# Ensures an 'at' instance is translated to a float
|
822
797
|
# (to be compared with the float coming from time.to_f)
|
@@ -826,6 +801,9 @@ module Rufus
|
|
826
801
|
at = Rufus::to_ruby_time(at) if at.kind_of?(String)
|
827
802
|
at = Rufus::to_gm_time(at) if at.kind_of?(DateTime)
|
828
803
|
at = at.to_f if at.kind_of?(Time)
|
804
|
+
|
805
|
+
raise "cannot schedule at : #{at.inspect}" unless at.is_a?(Float)
|
806
|
+
|
829
807
|
at
|
830
808
|
end
|
831
809
|
|
@@ -838,12 +816,10 @@ module Rufus
|
|
838
816
|
|
839
817
|
return block if block
|
840
818
|
|
841
|
-
schedulable = params
|
819
|
+
schedulable = params.delete(:schedulable)
|
842
820
|
|
843
821
|
return nil unless schedulable
|
844
822
|
|
845
|
-
params.delete :schedulable
|
846
|
-
|
847
823
|
l = lambda do
|
848
824
|
schedulable.trigger(params)
|
849
825
|
end
|
@@ -886,9 +862,6 @@ module Rufus
|
|
886
862
|
#
|
887
863
|
def step
|
888
864
|
|
889
|
-
#puts Time.now.to_f
|
890
|
-
#puts @pending_jobs.collect { |j| [ j.job_id, j.at ] }.inspect
|
891
|
-
|
892
865
|
step_unschedule
|
893
866
|
# unschedules any job in the unschedule queue before
|
894
867
|
# they have a chance to get triggered.
|
@@ -911,15 +884,7 @@ module Rufus
|
|
911
884
|
|
912
885
|
break if @unschedule_queue.empty?
|
913
886
|
|
914
|
-
|
915
|
-
|
916
|
-
if type == :cron
|
917
|
-
|
918
|
-
do_unschedule_cron_job job_id
|
919
|
-
else
|
920
|
-
|
921
|
-
do_unschedule job_id
|
922
|
-
end
|
887
|
+
do_unschedule(@unschedule_queue.pop)
|
923
888
|
end
|
924
889
|
end
|
925
890
|
|
@@ -947,22 +912,17 @@ module Rufus
|
|
947
912
|
end
|
948
913
|
|
949
914
|
#
|
950
|
-
# triggers every eligible pending jobs, then every eligible
|
915
|
+
# triggers every eligible pending (at or every) jobs, then every eligible
|
951
916
|
# cron jobs.
|
952
917
|
#
|
953
918
|
def step_trigger
|
954
919
|
|
955
|
-
now = Time.
|
956
|
-
|
957
|
-
if @exit_when_no_more_jobs
|
958
|
-
|
959
|
-
if @pending_jobs.size < 1
|
920
|
+
now = Time.now
|
960
921
|
|
961
|
-
|
962
|
-
return
|
963
|
-
end
|
922
|
+
if @exit_when_no_more_jobs && @pending_jobs.size < 1
|
964
923
|
|
965
|
-
@
|
924
|
+
@stopped = true
|
925
|
+
return
|
966
926
|
end
|
967
927
|
|
968
928
|
# TODO : eventually consider running cron / pending
|
@@ -977,12 +937,9 @@ module Rufus
|
|
977
937
|
|
978
938
|
@last_cron_second = now.sec
|
979
939
|
|
980
|
-
#puts "step() @cron_jobs.size #{@cron_jobs.size}"
|
981
|
-
|
982
940
|
@cron_jobs.each do |cron_id, cron_job|
|
983
|
-
#puts "step() cron_id : #{cron_id}"
|
984
941
|
#trigger(cron_job) if cron_job.matches?(now, @precision)
|
985
|
-
trigger
|
942
|
+
cron_job.trigger if cron_job.matches?(now)
|
986
943
|
end
|
987
944
|
end
|
988
945
|
|
@@ -1005,29 +962,12 @@ module Rufus
|
|
1005
962
|
#
|
1006
963
|
# obviously
|
1007
964
|
|
1008
|
-
trigger
|
965
|
+
job.trigger
|
1009
966
|
|
1010
967
|
@pending_jobs.delete_at 0
|
1011
968
|
end
|
1012
969
|
end
|
1013
970
|
|
1014
|
-
#
|
1015
|
-
# Triggers the job (in a dedicated thread).
|
1016
|
-
#
|
1017
|
-
def trigger (job)
|
1018
|
-
|
1019
|
-
Thread.new do
|
1020
|
-
begin
|
1021
|
-
|
1022
|
-
job.trigger
|
1023
|
-
|
1024
|
-
rescue Exception => e
|
1025
|
-
|
1026
|
-
log_exception e
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
|
1031
971
|
#
|
1032
972
|
# If an error occurs in the job, it well get caught and an error
|
1033
973
|
# message will be displayed to STDOUT.
|
@@ -1108,6 +1048,12 @@ module Rufus
|
|
1108
1048
|
#
|
1109
1049
|
attr_reader :params
|
1110
1050
|
|
1051
|
+
#
|
1052
|
+
# if the job is currently executing, this field points to
|
1053
|
+
# the 'trigger thread'
|
1054
|
+
#
|
1055
|
+
attr_reader :trigger_thread
|
1056
|
+
|
1111
1057
|
|
1112
1058
|
def initialize (scheduler, job_id, params, &block)
|
1113
1059
|
|
@@ -1147,6 +1093,31 @@ module Rufus
|
|
1147
1093
|
|
1148
1094
|
@scheduler.unschedule(@job_id)
|
1149
1095
|
end
|
1096
|
+
|
1097
|
+
#
|
1098
|
+
# Triggers the job (in a dedicated thread).
|
1099
|
+
#
|
1100
|
+
def trigger
|
1101
|
+
|
1102
|
+
Thread.new do
|
1103
|
+
|
1104
|
+
@trigger_thread = Thread.current
|
1105
|
+
# keeping track of the thread
|
1106
|
+
|
1107
|
+
begin
|
1108
|
+
|
1109
|
+
do_trigger
|
1110
|
+
|
1111
|
+
rescue Exception => e
|
1112
|
+
|
1113
|
+
@scheduler.send(:log_exception, e)
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
#@trigger_thread = nil if @trigger_thread = Thread.current
|
1117
|
+
@trigger_thread = nil
|
1118
|
+
# overlapping executions, what to do ?
|
1119
|
+
end
|
1120
|
+
end
|
1150
1121
|
end
|
1151
1122
|
|
1152
1123
|
#
|
@@ -1160,23 +1131,13 @@ module Rufus
|
|
1160
1131
|
#
|
1161
1132
|
attr_accessor :at
|
1162
1133
|
|
1163
|
-
|
1164
|
-
# The constructor.
|
1165
|
-
#
|
1134
|
+
|
1166
1135
|
def initialize (scheduler, at, at_id, params, &block)
|
1167
1136
|
|
1168
1137
|
super(scheduler, at_id, params, &block)
|
1169
1138
|
@at = at
|
1170
1139
|
end
|
1171
1140
|
|
1172
|
-
#
|
1173
|
-
# Triggers the job (calls the block)
|
1174
|
-
#
|
1175
|
-
def trigger
|
1176
|
-
|
1177
|
-
@block.call @job_id, @at
|
1178
|
-
end
|
1179
|
-
|
1180
1141
|
#
|
1181
1142
|
# Returns the Time instance at which this job is scheduled.
|
1182
1143
|
#
|
@@ -1193,6 +1154,18 @@ module Rufus
|
|
1193
1154
|
|
1194
1155
|
schedule_info
|
1195
1156
|
end
|
1157
|
+
|
1158
|
+
protected
|
1159
|
+
|
1160
|
+
#
|
1161
|
+
# Triggers the job (calls the block)
|
1162
|
+
#
|
1163
|
+
def do_trigger
|
1164
|
+
|
1165
|
+
@block.call @job_id, @at
|
1166
|
+
|
1167
|
+
@scheduler.instance_variable_get(:@non_cron_jobs).delete @job_id
|
1168
|
+
end
|
1196
1169
|
end
|
1197
1170
|
|
1198
1171
|
#
|
@@ -1208,6 +1181,48 @@ module Rufus
|
|
1208
1181
|
|
1209
1182
|
@params[:every]
|
1210
1183
|
end
|
1184
|
+
|
1185
|
+
protected
|
1186
|
+
|
1187
|
+
#
|
1188
|
+
# triggers the job, then reschedules it if necessary
|
1189
|
+
#
|
1190
|
+
def do_trigger
|
1191
|
+
|
1192
|
+
hit_exception = false
|
1193
|
+
|
1194
|
+
begin
|
1195
|
+
|
1196
|
+
@block.call @job_id, @at, @params
|
1197
|
+
|
1198
|
+
rescue Exception => e
|
1199
|
+
|
1200
|
+
@scheduler.send(:log_exception, e)
|
1201
|
+
|
1202
|
+
hit_exception = true
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
if \
|
1206
|
+
@scheduler.instance_variable_get(:@exit_when_no_more_jobs) or
|
1207
|
+
(@params[:dont_reschedule] == true) or
|
1208
|
+
(hit_exception and @params[:try_again] == false)
|
1209
|
+
|
1210
|
+
@scheduler.instance_variable_get(:@non_cron_jobs).delete(job_id)
|
1211
|
+
# maybe it'd be better to wipe that reference from here anyway...
|
1212
|
+
|
1213
|
+
return
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
#
|
1217
|
+
# ok, reschedule ...
|
1218
|
+
|
1219
|
+
|
1220
|
+
params[:job] = self
|
1221
|
+
|
1222
|
+
@at = @at + Rufus.duration_to_f(params[:every])
|
1223
|
+
|
1224
|
+
@scheduler.send(:do_schedule_at, @at, params)
|
1225
|
+
end
|
1211
1226
|
end
|
1212
1227
|
|
1213
1228
|
#
|
@@ -1252,14 +1267,6 @@ module Rufus
|
|
1252
1267
|
@cron_line.matches?(time)
|
1253
1268
|
end
|
1254
1269
|
|
1255
|
-
#
|
1256
|
-
# As the name implies.
|
1257
|
-
#
|
1258
|
-
def trigger
|
1259
|
-
|
1260
|
-
@block.call @job_id, @cron_line, @params
|
1261
|
-
end
|
1262
|
-
|
1263
1270
|
#
|
1264
1271
|
# Returns the original cron tab string used to schedule this
|
1265
1272
|
# Job. Like for example "60/3 * * * Sun".
|
@@ -1280,275 +1287,15 @@ module Rufus
|
|
1280
1287
|
|
1281
1288
|
@cron_line.next_time(from)
|
1282
1289
|
end
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
#
|
1286
|
-
# A 'cron line' is a line in the sense of a crontab
|
1287
|
-
# (man 5 crontab) file line.
|
1288
|
-
#
|
1289
|
-
class CronLine
|
1290
|
-
|
1291
|
-
#
|
1292
|
-
# The string used for creating this cronline instance.
|
1293
|
-
#
|
1294
|
-
attr_reader :original
|
1295
|
-
|
1296
|
-
attr_reader \
|
1297
|
-
:seconds,
|
1298
|
-
:minutes,
|
1299
|
-
:hours,
|
1300
|
-
:days,
|
1301
|
-
:months,
|
1302
|
-
:weekdays
|
1303
|
-
|
1304
|
-
def initialize (line)
|
1305
|
-
|
1306
|
-
super()
|
1307
|
-
|
1308
|
-
@original = line
|
1309
|
-
|
1310
|
-
items = line.split
|
1311
|
-
|
1312
|
-
unless [ 5, 6 ].include?(items.length)
|
1313
|
-
raise \
|
1314
|
-
"cron '#{line}' string should hold 5 or 6 items, " +
|
1315
|
-
"not #{items.length}" \
|
1316
|
-
end
|
1317
|
-
|
1318
|
-
offset = items.length - 5
|
1319
|
-
|
1320
|
-
@seconds = if offset == 1
|
1321
|
-
parse_item(items[0], 0, 59)
|
1322
|
-
else
|
1323
|
-
[ 0 ]
|
1324
|
-
end
|
1325
|
-
@minutes = parse_item(items[0+offset], 0, 59)
|
1326
|
-
@hours = parse_item(items[1+offset], 0, 24)
|
1327
|
-
@days = parse_item(items[2+offset], 1, 31)
|
1328
|
-
@months = parse_item(items[3+offset], 1, 12)
|
1329
|
-
@weekdays = parse_weekdays(items[4+offset])
|
1330
|
-
|
1331
|
-
#adjust_arrays()
|
1332
|
-
end
|
1333
|
-
|
1334
|
-
#
|
1335
|
-
# Returns true if the given time matches this cron line.
|
1336
|
-
#
|
1337
|
-
# (the precision is passed as well to determine if it's
|
1338
|
-
# worth checking seconds and minutes)
|
1339
|
-
#
|
1340
|
-
def matches? (time)
|
1341
|
-
#def matches? (time, precision)
|
1342
|
-
|
1343
|
-
time = Time.at(time) unless time.kind_of?(Time)
|
1344
|
-
|
1345
|
-
return false \
|
1346
|
-
if no_match?(time.sec, @seconds)
|
1347
|
-
#if precision <= 1 and no_match?(time.sec, @seconds)
|
1348
|
-
return false \
|
1349
|
-
if no_match?(time.min, @minutes)
|
1350
|
-
#if precision <= 60 and no_match?(time.min, @minutes)
|
1351
|
-
return false \
|
1352
|
-
if no_match?(time.hour, @hours)
|
1353
|
-
return false \
|
1354
|
-
if no_match?(time.day, @days)
|
1355
|
-
return false \
|
1356
|
-
if no_match?(time.month, @months)
|
1357
|
-
return false \
|
1358
|
-
if no_match?(time.wday, @weekdays)
|
1359
|
-
|
1360
|
-
true
|
1361
|
-
end
|
1362
|
-
|
1363
|
-
#
|
1364
|
-
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
1365
|
-
# months, weekdays).
|
1366
|
-
# This method is used by the cronline unit tests.
|
1367
|
-
#
|
1368
|
-
def to_array
|
1369
|
-
|
1370
|
-
[ @seconds, @minutes, @hours, @days, @months, @weekdays ]
|
1371
|
-
end
|
1372
|
-
|
1373
|
-
#
|
1374
|
-
# Returns the next time that this cron line is supposed to 'fire'
|
1375
|
-
#
|
1376
|
-
# This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
|
1377
|
-
#
|
1378
|
-
def next_time (now = Time.now)
|
1379
|
-
|
1380
|
-
#
|
1381
|
-
# position now to the next cron second
|
1382
1290
|
|
1383
|
-
|
1384
|
-
next_sec = @seconds.find { |s| s > now.sec } || 60 + @seconds.first
|
1385
|
-
now += next_sec - now.sec
|
1386
|
-
else
|
1387
|
-
now += 1
|
1388
|
-
end
|
1291
|
+
protected
|
1389
1292
|
|
1390
1293
|
#
|
1391
|
-
#
|
1392
|
-
|
1393
|
-
sjarray = nil
|
1394
|
-
|
1395
|
-
if @seconds
|
1396
|
-
|
1397
|
-
sjarray = []
|
1398
|
-
|
1399
|
-
i = @seconds.index(now.sec)
|
1400
|
-
ii = i
|
1401
|
-
|
1402
|
-
loop do
|
1403
|
-
cur = @seconds[ii]
|
1404
|
-
ii += 1
|
1405
|
-
ii = 0 if ii == @seconds.size
|
1406
|
-
nxt = @seconds[ii]
|
1407
|
-
nxt += 60 if ii == 0
|
1408
|
-
sjarray << (nxt - cur)
|
1409
|
-
break if ii == i
|
1410
|
-
end
|
1411
|
-
|
1412
|
-
else
|
1413
|
-
|
1414
|
-
sjarray = [ 1 ]
|
1415
|
-
end
|
1416
|
-
|
1294
|
+
# As the name implies.
|
1417
1295
|
#
|
1418
|
-
|
1419
|
-
|
1420
|
-
i = 0
|
1421
|
-
|
1422
|
-
loop do
|
1423
|
-
return now if matches?(now)
|
1424
|
-
now += sjarray[i]
|
1425
|
-
i += 1
|
1426
|
-
i = 0 if i == sjarray.size
|
1427
|
-
# danger... potentially no exit...
|
1428
|
-
end
|
1429
|
-
|
1430
|
-
nil
|
1431
|
-
end
|
1432
|
-
|
1433
|
-
private
|
1434
|
-
|
1435
|
-
#--
|
1436
|
-
# adjust values to Ruby
|
1437
|
-
#
|
1438
|
-
#def adjust_arrays()
|
1439
|
-
# @hours = @hours.collect { |h|
|
1440
|
-
# if h == 24
|
1441
|
-
# 0
|
1442
|
-
# else
|
1443
|
-
# h
|
1444
|
-
# end
|
1445
|
-
# } if @hours
|
1446
|
-
# @weekdays = @weekdays.collect { |wd|
|
1447
|
-
# wd - 1
|
1448
|
-
# } if @weekdays
|
1449
|
-
#end
|
1450
|
-
#
|
1451
|
-
# dead code, keeping it as a reminder
|
1452
|
-
#++
|
1453
|
-
|
1454
|
-
WDS = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ]
|
1455
|
-
#
|
1456
|
-
# used by parse_weekday()
|
1457
|
-
|
1458
|
-
def parse_weekdays (item)
|
1459
|
-
|
1460
|
-
item = item.downcase
|
1461
|
-
|
1462
|
-
WDS.each_with_index do |day, index|
|
1463
|
-
item = item.gsub day, "#{index}"
|
1464
|
-
end
|
1465
|
-
|
1466
|
-
r = parse_item item, 0, 7
|
1467
|
-
|
1468
|
-
return r unless r.is_a?(Array)
|
1469
|
-
|
1470
|
-
r.collect { |e| e == 7 ? 0 : e }.uniq
|
1471
|
-
end
|
1472
|
-
|
1473
|
-
def parse_item (item, min, max)
|
1474
|
-
|
1475
|
-
return nil \
|
1476
|
-
if item == "*"
|
1477
|
-
return parse_list(item, min, max) \
|
1478
|
-
if item.index(",")
|
1479
|
-
return parse_range(item, min, max) \
|
1480
|
-
if item.index("*") or item.index("-")
|
1481
|
-
|
1482
|
-
i = Integer(item)
|
1483
|
-
|
1484
|
-
i = min if i < min
|
1485
|
-
i = max if i > max
|
1486
|
-
|
1487
|
-
[ i ]
|
1488
|
-
end
|
1489
|
-
|
1490
|
-
def parse_list (item, min, max)
|
1491
|
-
|
1492
|
-
items = item.split(",")
|
1493
|
-
|
1494
|
-
items.inject([]) { |r, i| r.push(parse_range(i, min, max)) }.flatten
|
1495
|
-
end
|
1496
|
-
|
1497
|
-
def parse_range (item, min, max)
|
1498
|
-
|
1499
|
-
i = item.index("-")
|
1500
|
-
j = item.index("/")
|
1501
|
-
|
1502
|
-
return item.to_i if (not i and not j)
|
1503
|
-
|
1504
|
-
inc = 1
|
1505
|
-
|
1506
|
-
inc = Integer(item[j+1..-1]) if j
|
1507
|
-
|
1508
|
-
istart = -1
|
1509
|
-
iend = -1
|
1510
|
-
|
1511
|
-
if i
|
1512
|
-
|
1513
|
-
istart = Integer(item[0..i-1])
|
1514
|
-
|
1515
|
-
if j
|
1516
|
-
iend = Integer(item[i+1..j])
|
1517
|
-
else
|
1518
|
-
iend = Integer(item[i+1..-1])
|
1519
|
-
end
|
1520
|
-
|
1521
|
-
else # case */x
|
1522
|
-
|
1523
|
-
istart = min
|
1524
|
-
iend = max
|
1525
|
-
end
|
1526
|
-
|
1527
|
-
istart = min if istart < min
|
1528
|
-
iend = max if iend > max
|
1529
|
-
|
1530
|
-
result = []
|
1531
|
-
|
1532
|
-
value = istart
|
1533
|
-
loop do
|
1534
|
-
|
1535
|
-
result << value
|
1536
|
-
value = value + inc
|
1537
|
-
break if value > iend
|
1538
|
-
end
|
1539
|
-
|
1540
|
-
result
|
1541
|
-
end
|
1542
|
-
|
1543
|
-
def no_match? (value, cron_values)
|
1544
|
-
|
1545
|
-
return false if not cron_values
|
1546
|
-
|
1547
|
-
cron_values.each do |v|
|
1548
|
-
return false if value == v # ok, it matches
|
1549
|
-
end
|
1296
|
+
def do_trigger
|
1550
1297
|
|
1551
|
-
|
1298
|
+
@block.call @job_id, @cron_line, @params
|
1552
1299
|
end
|
1553
1300
|
end
|
1554
1301
|
|