rufus-scheduler 1.0.14 → 2.0.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 +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
|
+
|