rufus-scheduler 1.0.14 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +2 -82
- data/CREDITS.txt +8 -2
- data/README.rdoc +359 -0
- data/TODO.txt +51 -0
- data/lib/rufus-scheduler.rb +1 -1
- data/lib/rufus/otime.rb +1 -1
- data/lib/rufus/{scheduler → sc}/cronline.rb +44 -80
- data/lib/rufus/sc/jobqueues.rb +157 -0
- data/lib/rufus/sc/jobs.rb +339 -0
- data/lib/rufus/{scheduler/otime.rb → sc/rtime.rb} +35 -45
- data/lib/rufus/sc/scheduler.rb +454 -0
- data/lib/rufus/scheduler.rb +53 -1
- data/spec/spec.rb +14 -0
- metadata +14 -11
- data/README.txt +0 -118
- data/lib/rufus/scheduler/jobs.rb +0 -334
- data/lib/rufus/scheduler/scheduler.rb +0 -1082
- data/test/test.rb +0 -20
data/TODO.txt
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
[o] spec for jobs in the past (in and at)
|
3
|
+
[o] :discard_past
|
4
|
+
|
5
|
+
[o] every
|
6
|
+
[o] cron
|
7
|
+
|
8
|
+
[o] CHECK every and unschedule !!!
|
9
|
+
|
10
|
+
[o] :tags
|
11
|
+
[o] timeout feature (at/in/every/cron) in Job class
|
12
|
+
|
13
|
+
[o] :first_in, :first_at
|
14
|
+
|
15
|
+
[x] :dont_reschedule (or block returns false ?)
|
16
|
+
|
17
|
+
[o] [get_]jobs methods
|
18
|
+
[o] find methods
|
19
|
+
|
20
|
+
[x] CTRL-C during tests : allow, trap_int...
|
21
|
+
|
22
|
+
[o] 1.9
|
23
|
+
[o] j1.2.0
|
24
|
+
|
25
|
+
[o] revise trigger block arity
|
26
|
+
use a compatibility switch ? yes
|
27
|
+
|
28
|
+
[o] synchronize @cron_jobs ?
|
29
|
+
|
30
|
+
[o] why not : make it work even if EM is not present
|
31
|
+
EmScheduler < Scheduler
|
32
|
+
FiberScheduler < Scheduler
|
33
|
+
|
34
|
+
[x] :blocking => 'blockname' idea, mutex = @mutexes['blockname'] ...
|
35
|
+
[o] eventually, make sleep frequency customizable
|
36
|
+
|
37
|
+
[o] PlainScheduler : name thread
|
38
|
+
|
39
|
+
[o] document :blocking
|
40
|
+
|
41
|
+
[o] README.rdoc
|
42
|
+
[o] fix jruby120 --em
|
43
|
+
|
44
|
+
[o] handle_exception (job, e)
|
45
|
+
|
46
|
+
[o] Schedulable
|
47
|
+
|
48
|
+
[o] Rufus::Scheduler.start_new() : autodetect EM ?
|
49
|
+
[o] check :blocking and every (reschedule blocking...)
|
50
|
+
[o] document :thread_name scheduler option
|
51
|
+
|
data/lib/rufus-scheduler.rb
CHANGED
data/lib/rufus/otime.rb
CHANGED
@@ -52,45 +52,34 @@ module Rufus
|
|
52
52
|
|
53
53
|
items = line.split
|
54
54
|
|
55
|
-
unless
|
56
|
-
raise
|
57
|
-
"cron '#{line}' string should hold 5 or 6 items, "
|
58
|
-
"not #{items.length}" \
|
55
|
+
unless items.length == 5 or items.length == 6
|
56
|
+
raise(
|
57
|
+
"cron '#{line}' string should hold 5 or 6 items, not #{items.length}")
|
59
58
|
end
|
60
59
|
|
61
60
|
offset = items.length - 5
|
62
61
|
|
63
|
-
@seconds =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@
|
69
|
-
@hours = parse_item(items[1+offset], 0, 24)
|
70
|
-
@days = parse_item(items[2+offset], 1, 31)
|
71
|
-
@months = parse_item(items[3+offset], 1, 12)
|
72
|
-
@weekdays = parse_weekdays(items[4+offset])
|
73
|
-
|
74
|
-
#adjust_arrays()
|
62
|
+
@seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ]
|
63
|
+
@minutes = parse_item(items[0 + offset], 0, 59)
|
64
|
+
@hours = parse_item(items[1 + offset], 0, 24)
|
65
|
+
@days = parse_item(items[2 + offset], 1, 31)
|
66
|
+
@months = parse_item(items[3 + offset], 1, 12)
|
67
|
+
@weekdays = parse_weekdays(items[4 + offset])
|
75
68
|
end
|
76
69
|
|
77
70
|
#
|
78
71
|
# Returns true if the given time matches this cron line.
|
79
72
|
#
|
80
|
-
# (the precision is passed as well to determine if it's
|
81
|
-
# worth checking seconds and minutes)
|
82
|
-
#
|
83
73
|
def matches? (time)
|
84
|
-
#def matches? (time, precision)
|
85
74
|
|
86
75
|
time = Time.at(time) unless time.kind_of?(Time)
|
87
76
|
|
88
|
-
return false unless sub_match?
|
89
|
-
return false unless sub_match?
|
90
|
-
return false unless sub_match?
|
91
|
-
return false unless sub_match?
|
92
|
-
return false unless sub_match?
|
93
|
-
return false unless sub_match?
|
77
|
+
return false unless sub_match?(time.sec, @seconds)
|
78
|
+
return false unless sub_match?(time.min, @minutes)
|
79
|
+
return false unless sub_match?(time.hour, @hours)
|
80
|
+
return false unless sub_match?(time.day, @days)
|
81
|
+
return false unless sub_match?(time.month, @months)
|
82
|
+
return false unless sub_match?(time.wday, @weekdays)
|
94
83
|
true
|
95
84
|
end
|
96
85
|
|
@@ -128,27 +117,29 @@ module Rufus
|
|
128
117
|
#
|
129
118
|
# (Thanks to K Liu for the note and the examples)
|
130
119
|
#
|
131
|
-
def next_time time=Time.now
|
120
|
+
def next_time (time=Time.now)
|
121
|
+
|
132
122
|
time -= time.usec * 1e-6
|
133
123
|
time += 1
|
134
124
|
|
135
125
|
loop do
|
136
|
-
|
126
|
+
|
127
|
+
unless date_match?(time)
|
137
128
|
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
|
138
129
|
next
|
139
130
|
end
|
140
131
|
|
141
|
-
unless sub_match?
|
132
|
+
unless sub_match?(time.hour, @hours)
|
142
133
|
time += (60 - time.min) * 60 - time.sec
|
143
134
|
next
|
144
135
|
end
|
145
136
|
|
146
|
-
unless sub_match?
|
137
|
+
unless sub_match?(time.min, @minutes)
|
147
138
|
time += 60 - time.sec
|
148
139
|
next
|
149
140
|
end
|
150
141
|
|
151
|
-
unless sub_match?
|
142
|
+
unless sub_match?(time.sec, @seconds)
|
152
143
|
time += 1
|
153
144
|
next
|
154
145
|
end
|
@@ -161,25 +152,6 @@ module Rufus
|
|
161
152
|
|
162
153
|
private
|
163
154
|
|
164
|
-
#--
|
165
|
-
# adjust values to Ruby
|
166
|
-
#
|
167
|
-
#def adjust_arrays()
|
168
|
-
# @hours = @hours.collect { |h|
|
169
|
-
# if h == 24
|
170
|
-
# 0
|
171
|
-
# else
|
172
|
-
# h
|
173
|
-
# end
|
174
|
-
# } if @hours
|
175
|
-
# @weekdays = @weekdays.collect { |wd|
|
176
|
-
# wd - 1
|
177
|
-
# } if @weekdays
|
178
|
-
#end
|
179
|
-
#
|
180
|
-
# dead code, keeping it as a reminder
|
181
|
-
#++
|
182
|
-
|
183
155
|
WDS = %w[ sun mon tue wed thu fri sat ]
|
184
156
|
#
|
185
157
|
# used by parse_weekday()
|
@@ -188,25 +160,20 @@ module Rufus
|
|
188
160
|
|
189
161
|
item = item.downcase
|
190
162
|
|
191
|
-
WDS.each_with_index
|
192
|
-
item = item.gsub day, "#{index}"
|
193
|
-
end
|
194
|
-
|
195
|
-
r = parse_item item, 0, 7
|
163
|
+
WDS.each_with_index { |day, index| item = item.gsub(day, index.to_s) }
|
196
164
|
|
197
|
-
|
165
|
+
r = parse_item(item, 0, 7)
|
198
166
|
|
199
|
-
r.
|
167
|
+
r.is_a?(Array) ?
|
168
|
+
r.collect { |e| e == 7 ? 0 : e }.uniq :
|
169
|
+
r
|
200
170
|
end
|
201
171
|
|
202
172
|
def parse_item (item, min, max)
|
203
173
|
|
204
|
-
return nil
|
205
|
-
|
206
|
-
return
|
207
|
-
if item.index(",")
|
208
|
-
return parse_range(item, min, max) \
|
209
|
-
if item.index("*") or item.index("-")
|
174
|
+
return nil if item == '*'
|
175
|
+
return parse_list(item, min, max) if item.index(',')
|
176
|
+
return parse_range(item, min, max) if item.index('*') or item.index('-')
|
210
177
|
|
211
178
|
i = Integer(item)
|
212
179
|
|
@@ -218,33 +185,31 @@ module Rufus
|
|
218
185
|
|
219
186
|
def parse_list (item, min, max)
|
220
187
|
|
221
|
-
|
222
|
-
|
223
|
-
|
188
|
+
item.split(',').inject([]) { |r, i|
|
189
|
+
r.push(parse_range(i, min, max))
|
190
|
+
}.flatten
|
224
191
|
end
|
225
192
|
|
226
193
|
def parse_range (item, min, max)
|
227
194
|
|
228
|
-
i = item.index(
|
229
|
-
j = item.index(
|
195
|
+
i = item.index('-')
|
196
|
+
j = item.index('/')
|
230
197
|
|
231
198
|
return item.to_i if (not i and not j)
|
232
199
|
|
233
|
-
inc = 1
|
234
|
-
|
235
|
-
inc = Integer(item[j+1..-1]) if j
|
200
|
+
inc = j ? Integer(item[j+1..-1]) : 1
|
236
201
|
|
237
202
|
istart = -1
|
238
203
|
iend = -1
|
239
204
|
|
240
205
|
if i
|
241
206
|
|
242
|
-
istart = Integer(item[0..i-1])
|
207
|
+
istart = Integer(item[0..i - 1])
|
243
208
|
|
244
209
|
if j
|
245
|
-
iend = Integer(item[i+1..j])
|
210
|
+
iend = Integer(item[i + 1..j])
|
246
211
|
else
|
247
|
-
iend = Integer(item[i+1..-1])
|
212
|
+
iend = Integer(item[i + 1..-1])
|
248
213
|
end
|
249
214
|
|
250
215
|
else # case */x
|
@@ -260,7 +225,6 @@ module Rufus
|
|
260
225
|
|
261
226
|
value = istart
|
262
227
|
loop do
|
263
|
-
|
264
228
|
result << value
|
265
229
|
value = value + inc
|
266
230
|
break if value > iend
|
@@ -269,14 +233,14 @@ module Rufus
|
|
269
233
|
result
|
270
234
|
end
|
271
235
|
|
272
|
-
def sub_match?
|
236
|
+
def sub_match?(value, values)
|
273
237
|
values.nil? || values.include?(value)
|
274
238
|
end
|
275
239
|
|
276
|
-
def date_match?
|
277
|
-
return false unless sub_match?
|
278
|
-
return false unless sub_match?
|
279
|
-
return false unless sub_match?
|
240
|
+
def date_match?(date)
|
241
|
+
return false unless sub_match?(date.day, @days)
|
242
|
+
return false unless sub_match?(date.month, @months)
|
243
|
+
return false unless sub_match?(date.wday, @weekdays)
|
280
244
|
true
|
281
245
|
end
|
282
246
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006-2009, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
require 'thread'
|
27
|
+
|
28
|
+
|
29
|
+
module Rufus
|
30
|
+
module Scheduler
|
31
|
+
|
32
|
+
#
|
33
|
+
# Tracking at/in/every jobs.
|
34
|
+
#
|
35
|
+
# In order of trigger time.
|
36
|
+
#
|
37
|
+
class JobQueue
|
38
|
+
|
39
|
+
# Mapping :at|:in|:every to their respective job classes.
|
40
|
+
#
|
41
|
+
JOB_TYPES = {
|
42
|
+
:at => Rufus::Scheduler::AtJob,
|
43
|
+
:in => Rufus::Scheduler::InJob,
|
44
|
+
:every => Rufus::Scheduler::EveryJob
|
45
|
+
}
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
|
49
|
+
@mutex = Mutex.new
|
50
|
+
@jobs = []
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the next job to trigger. Returns nil if none eligible.
|
54
|
+
#
|
55
|
+
def job_to_trigger
|
56
|
+
|
57
|
+
@mutex.synchronize do
|
58
|
+
if @jobs.size > 0 && Time.now.to_f >= @jobs.first.at
|
59
|
+
@jobs.shift
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Adds this job to the map.
|
67
|
+
#
|
68
|
+
def << (job)
|
69
|
+
|
70
|
+
@mutex.synchronize do
|
71
|
+
delete(job.job_id)
|
72
|
+
@jobs << job
|
73
|
+
@jobs.sort! { |j0, j1| j0.at <=> j1.at }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Removes a job (given its id). Returns nil if the job was not found.
|
78
|
+
#
|
79
|
+
def unschedule (job_id)
|
80
|
+
|
81
|
+
@mutex.synchronize { delete(job_id) }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a mapping job_id => job
|
85
|
+
#
|
86
|
+
def to_h
|
87
|
+
|
88
|
+
@jobs.inject({}) { |h, j| h[j.job_id] = j; h }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a list of jobs of the given type (:at|:in|:every)
|
92
|
+
#
|
93
|
+
def select (type)
|
94
|
+
|
95
|
+
type = JOB_TYPES[type]
|
96
|
+
@jobs.select { |j| j.is_a?(type) }
|
97
|
+
end
|
98
|
+
|
99
|
+
def size
|
100
|
+
|
101
|
+
@jobs.size
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def delete (job_id)
|
107
|
+
j = @jobs.find { |j| j.job_id == job_id }
|
108
|
+
@jobs.delete(j) if j
|
109
|
+
j
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Tracking cron jobs.
|
115
|
+
#
|
116
|
+
# (mostly synchronizing access to the map of cron jobs)
|
117
|
+
#
|
118
|
+
class CronJobQueue
|
119
|
+
|
120
|
+
def initialize
|
121
|
+
|
122
|
+
@mutex = Mutex.new
|
123
|
+
@jobs = {}
|
124
|
+
end
|
125
|
+
|
126
|
+
def unschedule (job_id)
|
127
|
+
|
128
|
+
@mutex.synchronize { @jobs.delete(job_id) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def trigger_matching_jobs (now)
|
132
|
+
|
133
|
+
js = @mutex.synchronize { @jobs.values }
|
134
|
+
# maybe this sync is a bit paranoid
|
135
|
+
|
136
|
+
js.each { |job| job.trigger_if_matches(now) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def << (job)
|
140
|
+
|
141
|
+
@mutex.synchronize { @jobs[job.job_id] = job }
|
142
|
+
end
|
143
|
+
|
144
|
+
def size
|
145
|
+
|
146
|
+
@jobs.size
|
147
|
+
end
|
148
|
+
|
149
|
+
def to_h
|
150
|
+
|
151
|
+
@jobs.dup
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
@@ -0,0 +1,339 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006-2009, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Rufus
|
27
|
+
module Scheduler
|
28
|
+
|
29
|
+
#
|
30
|
+
# The base class for all types of jobs.
|
31
|
+
#
|
32
|
+
class Job
|
33
|
+
|
34
|
+
# A reference to the scheduler owning this job
|
35
|
+
#
|
36
|
+
attr_reader :scheduler
|
37
|
+
|
38
|
+
# The initial, raw, scheduling info (at / in / every / cron)
|
39
|
+
#
|
40
|
+
attr_reader :t
|
41
|
+
|
42
|
+
# When the job is actually running, this attribute will hold the
|
43
|
+
# thread in which the job is running.
|
44
|
+
# Can be used to determine if a job is actually running.
|
45
|
+
#
|
46
|
+
attr_reader :job_thread
|
47
|
+
|
48
|
+
# The job parameters (passed via the schedule method)
|
49
|
+
#
|
50
|
+
attr_reader :params
|
51
|
+
|
52
|
+
# The block to call when triggering
|
53
|
+
#
|
54
|
+
attr_reader :block
|
55
|
+
|
56
|
+
# Last time the job executed
|
57
|
+
# (for an {At|In}Job, it will mean 'not executed' if nil or when
|
58
|
+
# it got executed if set)
|
59
|
+
#
|
60
|
+
# (
|
61
|
+
# Last time job got triggered (most useful with EveryJob, but can be
|
62
|
+
# useful with remaining instances of At/InJob (are they done ?))
|
63
|
+
# )
|
64
|
+
#
|
65
|
+
attr_reader :last
|
66
|
+
|
67
|
+
# The identifier for this job.
|
68
|
+
#
|
69
|
+
attr_reader :job_id
|
70
|
+
|
71
|
+
|
72
|
+
# Instantiating the job.
|
73
|
+
#
|
74
|
+
def initialize (scheduler, t, params, &block)
|
75
|
+
|
76
|
+
@scheduler = scheduler
|
77
|
+
@t = t
|
78
|
+
@params = params
|
79
|
+
@block = block || params[:schedulable]
|
80
|
+
|
81
|
+
raise ArgumentError.new(
|
82
|
+
'no block or :schedulable passed, nothing to schedule'
|
83
|
+
) unless @block
|
84
|
+
|
85
|
+
@params[:tags] = Array(@params[:tags])
|
86
|
+
|
87
|
+
@job_id = params[:job_id] || "#{self.class.name}_#{self.object_id.to_s}"
|
88
|
+
|
89
|
+
determine_at
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the list of tags attached to the job.
|
93
|
+
#
|
94
|
+
def tags
|
95
|
+
|
96
|
+
@params[:tags]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Sets the list of tags attached to the job (Usually they are set
|
100
|
+
# via the schedule every/at/in/cron method).
|
101
|
+
#
|
102
|
+
def tags= (tags)
|
103
|
+
|
104
|
+
@params[:tags] = Array(tags)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Generally returns the string/float/integer used to schedule the job
|
108
|
+
# (seconds, time string, date string)
|
109
|
+
#
|
110
|
+
def schedule_info
|
111
|
+
|
112
|
+
@t
|
113
|
+
end
|
114
|
+
|
115
|
+
# Triggers the job.
|
116
|
+
#
|
117
|
+
def trigger (t=Time.now)
|
118
|
+
|
119
|
+
@last = t
|
120
|
+
|
121
|
+
@scheduler.send(:trigger_job, @params[:blocking]) do
|
122
|
+
#
|
123
|
+
# Note that #trigger_job is protected, hence the #send
|
124
|
+
# (Only jobs know about this method of the scheduler)
|
125
|
+
|
126
|
+
@job_thread = Thread.current
|
127
|
+
|
128
|
+
begin
|
129
|
+
|
130
|
+
#args = prepare_args
|
131
|
+
#@block.call(*args)
|
132
|
+
|
133
|
+
#@block.call(self)
|
134
|
+
|
135
|
+
@block.respond_to?(:call) ?
|
136
|
+
@block.call(self) : @block.trigger(@params)
|
137
|
+
|
138
|
+
@job_thread = nil
|
139
|
+
|
140
|
+
rescue Exception => e
|
141
|
+
|
142
|
+
@scheduler.handle_exception(self, e)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# note that add_job and add_cron_job ensured that :blocking is
|
147
|
+
# not used along :timeout
|
148
|
+
|
149
|
+
if to = @params[:timeout]
|
150
|
+
|
151
|
+
@scheduler.in(to, :tags => 'timeout') do
|
152
|
+
|
153
|
+
# at this point, @job_thread might be set
|
154
|
+
|
155
|
+
@job_thread.raise(Rufus::Scheduler::TimeOutError) \
|
156
|
+
if @job_thread and @job_thread.alive?
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Unschedules this job.
|
162
|
+
#
|
163
|
+
def unschedule
|
164
|
+
|
165
|
+
@scheduler.unschedule(self.job_id)
|
166
|
+
end
|
167
|
+
|
168
|
+
#--
|
169
|
+
#protected
|
170
|
+
#
|
171
|
+
# Prepare the args given the triggered block arity.
|
172
|
+
#
|
173
|
+
#def prepare_args
|
174
|
+
# if @scheduler.options[:onezero_block_arity]
|
175
|
+
# case @block.arity
|
176
|
+
# when 0 then []
|
177
|
+
# when 1 then [ @params ]
|
178
|
+
# when 2 then [ @job_id, @params ]
|
179
|
+
# #else [ @job_id, schedule_info, @params ]
|
180
|
+
# else [ @job_id, self, @params ]
|
181
|
+
# end
|
182
|
+
# else
|
183
|
+
# [ self ]
|
184
|
+
# end
|
185
|
+
#end
|
186
|
+
#++
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# The base class of at/in/every jobs.
|
191
|
+
#
|
192
|
+
class SimpleJob < Job
|
193
|
+
|
194
|
+
# When the job is supposed to trigger
|
195
|
+
#
|
196
|
+
attr_reader :at
|
197
|
+
|
198
|
+
attr_reader :last
|
199
|
+
|
200
|
+
def determine_at
|
201
|
+
|
202
|
+
@at = Rufus.at_to_f(@t)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Job that occurs once, in a certain amount of time.
|
208
|
+
#
|
209
|
+
class InJob < SimpleJob
|
210
|
+
|
211
|
+
protected
|
212
|
+
|
213
|
+
def determine_at
|
214
|
+
|
215
|
+
iin = @t.is_a?(Fixnum) || @t.is_a?(Float) ?
|
216
|
+
@t : Rufus.parse_duration_string(@t)
|
217
|
+
|
218
|
+
@at = (Time.now + iin).to_f
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
#
|
223
|
+
# Job that occurs once, at a certain point in time.
|
224
|
+
#
|
225
|
+
class AtJob < SimpleJob
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Recurring job with a certain frequency.
|
230
|
+
#
|
231
|
+
class EveryJob < SimpleJob
|
232
|
+
|
233
|
+
# The frequency, in seconds, of this EveryJob
|
234
|
+
#
|
235
|
+
attr_reader :frequency
|
236
|
+
|
237
|
+
def initialize (scheduler, t, params, &block)
|
238
|
+
super
|
239
|
+
determine_frequency
|
240
|
+
determine_at
|
241
|
+
end
|
242
|
+
|
243
|
+
# Triggers the job (and reschedules it).
|
244
|
+
#
|
245
|
+
def trigger
|
246
|
+
|
247
|
+
schedule_next
|
248
|
+
|
249
|
+
super
|
250
|
+
|
251
|
+
#unschedule if @params[:dont_reschedule]
|
252
|
+
# obsolete
|
253
|
+
end
|
254
|
+
|
255
|
+
protected
|
256
|
+
|
257
|
+
def determine_frequency
|
258
|
+
|
259
|
+
@frequency = @t.is_a?(Fixnum) || @t.is_a?(Float) ?
|
260
|
+
@t : Rufus.parse_duration_string(@t)
|
261
|
+
end
|
262
|
+
|
263
|
+
def determine_at
|
264
|
+
|
265
|
+
return unless @frequency
|
266
|
+
|
267
|
+
@last = @at
|
268
|
+
# the first time, @last will be nil
|
269
|
+
|
270
|
+
@at = if @last
|
271
|
+
@last + @frequency
|
272
|
+
else
|
273
|
+
if fi = @params[:first_in]
|
274
|
+
Time.now.to_f + Rufus.duration_to_f(fi)
|
275
|
+
elsif fa = @params[:first_at]
|
276
|
+
Rufus.at_to_f(fa)
|
277
|
+
else
|
278
|
+
Time.now.to_f + @frequency
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# It's an every job, have to schedule next time it occurs...
|
284
|
+
#
|
285
|
+
def schedule_next
|
286
|
+
|
287
|
+
determine_at
|
288
|
+
|
289
|
+
@scheduler.send(:add_job, self)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
#
|
294
|
+
# Recurring job, cron style.
|
295
|
+
#
|
296
|
+
class CronJob < Job
|
297
|
+
|
298
|
+
# The CronLine instance, it holds all the info about the cron schedule
|
299
|
+
#
|
300
|
+
attr_reader :cron_line
|
301
|
+
|
302
|
+
# The job parameters (passed via the schedule method)
|
303
|
+
#
|
304
|
+
attr_reader :params
|
305
|
+
|
306
|
+
# The block to call when triggering
|
307
|
+
#
|
308
|
+
attr_reader :block
|
309
|
+
|
310
|
+
# Creates a new CronJob instance.
|
311
|
+
#
|
312
|
+
def initialize (scheduler, cron_string, params, &block)
|
313
|
+
|
314
|
+
super
|
315
|
+
|
316
|
+
@cron_line = case @t
|
317
|
+
|
318
|
+
when String then CronLine.new(@t)
|
319
|
+
when CronLine then @t
|
320
|
+
|
321
|
+
else raise "cannot initialize a CronJob out of #{@t.inspect}"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def trigger_if_matches (time)
|
326
|
+
|
327
|
+
trigger(time) if @cron_line.matches?(time)
|
328
|
+
end
|
329
|
+
|
330
|
+
protected
|
331
|
+
|
332
|
+
def determine_at
|
333
|
+
# empty
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|