tanzeeb-rufus-scheduler 2.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/CHANGELOG.txt +43 -0
- data/CREDITS.txt +36 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +388 -0
- data/Rakefile +86 -0
- data/TODO.txt +57 -0
- data/lib/rufus-scheduler.rb +3 -0
- data/lib/rufus/otime.rb +3 -0
- data/lib/rufus/sc/cronline.rb +281 -0
- data/lib/rufus/sc/jobqueues.rb +160 -0
- data/lib/rufus/sc/jobs.rb +363 -0
- data/lib/rufus/sc/rtime.rb +365 -0
- data/lib/rufus/sc/scheduler.rb +481 -0
- data/lib/rufus/sc/version.rb +32 -0
- data/lib/rufus/scheduler.rb +55 -0
- data/misc/cronline_next_time_cost.rb +14 -0
- data/spec/at_in_spec.rb +48 -0
- data/spec/at_spec.rb +121 -0
- data/spec/blocking_spec.rb +54 -0
- data/spec/cron_spec.rb +122 -0
- data/spec/cronline_spec.rb +163 -0
- data/spec/every_spec.rb +229 -0
- data/spec/exception_spec.rb +77 -0
- data/spec/in_spec.rb +165 -0
- data/spec/rtime_spec.rb +93 -0
- data/spec/schedulable_spec.rb +79 -0
- data/spec/scheduler_spec.rb +81 -0
- data/spec/spec.rb +14 -0
- data/spec/spec_base.rb +82 -0
- data/spec/stress_schedule_unschedule_spec.rb +155 -0
- data/spec/timeout_spec.rb +125 -0
- data/tanzeeb-rufus-scheduler.gemspec +97 -0
- data/test/kjw.rb +113 -0
- data/test/t.rb +20 -0
- metadata +160 -0
data/Rakefile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
|
6
|
+
load 'lib/rufus/sc/version.rb'
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
# CLEAN
|
11
|
+
|
12
|
+
require 'rake/clean'
|
13
|
+
CLEAN.include('pkg', 'tmp', 'html')
|
14
|
+
task :default => [ :clean ]
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# GEM
|
19
|
+
|
20
|
+
require 'jeweler'
|
21
|
+
|
22
|
+
Jeweler::Tasks.new do |gem|
|
23
|
+
|
24
|
+
gem.version = Rufus::Scheduler::VERSION
|
25
|
+
gem.name = 'tanzeeb-rufus-scheduler'
|
26
|
+
gem.summary = 'job scheduler for Ruby (at, cron, in and every jobs)'
|
27
|
+
|
28
|
+
gem.description = %{
|
29
|
+
job scheduler for Ruby (at, cron, in and every jobs).
|
30
|
+
|
31
|
+
By default uses a Ruby thread, if EventMachine is present, it will rely on it.
|
32
|
+
|
33
|
+
This fork adds timezone support to cron schedules.
|
34
|
+
}
|
35
|
+
gem.email = 'tanzeeb@gmail.com'
|
36
|
+
gem.homepage = 'http://github.com/tanzeeb/rufus-scheduler/'
|
37
|
+
gem.authors = [ 'John Mettraux', 'Tanzeeb Khalili', 'Matt Briggs', 'Sean Kirby' ]
|
38
|
+
gem.rubyforge_project = 'rufus'
|
39
|
+
|
40
|
+
gem.test_file = 'spec/spec.rb'
|
41
|
+
|
42
|
+
#gem.add_dependency 'yajl-ruby'
|
43
|
+
gem.add_development_dependency 'rake'
|
44
|
+
gem.add_development_dependency 'yard'
|
45
|
+
gem.add_development_dependency 'bacon'
|
46
|
+
gem.add_development_dependency 'jeweler'
|
47
|
+
gem.add_dependency 'tzinfo'
|
48
|
+
|
49
|
+
# gemspec spec : http://www.rubygems.org/read/chapter/20
|
50
|
+
end
|
51
|
+
Jeweler::GemcutterTasks.new
|
52
|
+
|
53
|
+
|
54
|
+
#
|
55
|
+
# DOC
|
56
|
+
|
57
|
+
begin
|
58
|
+
|
59
|
+
require 'yard'
|
60
|
+
|
61
|
+
YARD::Rake::YardocTask.new do |doc|
|
62
|
+
doc.options = [
|
63
|
+
'-o', 'html/rufus-scheduler', '--title',
|
64
|
+
"rufus-scheduler #{Rufus::Scheduler::VERSION}"
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
rescue LoadError
|
69
|
+
|
70
|
+
task :yard do
|
71
|
+
abort "YARD is not available : sudo gem install yard"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
#
|
77
|
+
# TO THE WEB
|
78
|
+
|
79
|
+
task :upload_website => [ :clean, :yard ] do
|
80
|
+
|
81
|
+
account = 'jmettraux@rubyforge.org'
|
82
|
+
webdir = '/var/www/gforge-projects/rufus'
|
83
|
+
|
84
|
+
sh "rsync -azv -e ssh html/rufus-scheduler #{account}:#{webdir}/"
|
85
|
+
end
|
86
|
+
|
data/TODO.txt
ADDED
@@ -0,0 +1,57 @@
|
|
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
|
+
|
52
|
+
[o] unify cron_jobs#trigger_matching_jobs(now) and jobs#job_to_trigger
|
53
|
+
[o] pluggable job queues
|
54
|
+
|
55
|
+
[ ] Joel's complaint about timeout jobs gone ballistic
|
56
|
+
[x] move trigger_job out of the scheduler
|
57
|
+
|
data/lib/rufus/otime.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006-2010, 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
|
+
require 'tzinfo'
|
26
|
+
|
27
|
+
module Rufus
|
28
|
+
|
29
|
+
#
|
30
|
+
# A 'cron line' is a line in the sense of a crontab
|
31
|
+
# (man 5 crontab) file line.
|
32
|
+
#
|
33
|
+
class CronLine
|
34
|
+
|
35
|
+
#
|
36
|
+
# The string used for creating this cronline instance.
|
37
|
+
#
|
38
|
+
attr_reader :original
|
39
|
+
|
40
|
+
attr_reader \
|
41
|
+
:seconds,
|
42
|
+
:minutes,
|
43
|
+
:hours,
|
44
|
+
:days,
|
45
|
+
:months,
|
46
|
+
:weekdays,
|
47
|
+
:zone
|
48
|
+
|
49
|
+
def initialize (line)
|
50
|
+
|
51
|
+
super()
|
52
|
+
|
53
|
+
@original = line
|
54
|
+
|
55
|
+
times, zone = line.split(' : ')
|
56
|
+
items = times.split
|
57
|
+
|
58
|
+
@zone = parse_zone(zone.to_s)
|
59
|
+
|
60
|
+
if zone && !@zone
|
61
|
+
raise "zone '#{zone}' is an invalid timezone"
|
62
|
+
end
|
63
|
+
|
64
|
+
unless items.length == 5 or items.length == 6
|
65
|
+
raise(
|
66
|
+
"cron '#{line}' string should hold 5 or 6 items, not #{items.length}")
|
67
|
+
end
|
68
|
+
|
69
|
+
offset = items.length - 5
|
70
|
+
|
71
|
+
@seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ]
|
72
|
+
@minutes = parse_item(items[0 + offset], 0, 59)
|
73
|
+
@hours = parse_item(items[1 + offset], 0, 24)
|
74
|
+
@days = parse_item(items[2 + offset], 1, 31)
|
75
|
+
@months = parse_item(items[3 + offset], 1, 12)
|
76
|
+
@weekdays = parse_weekdays(items[4 + offset])
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Returns true if the given time matches this cron line.
|
82
|
+
#
|
83
|
+
def matches? (time)
|
84
|
+
time = Time.at(time) unless time.kind_of?(Time)
|
85
|
+
|
86
|
+
if @zone
|
87
|
+
utc = time.utc?
|
88
|
+
time = time.getutc unless utc
|
89
|
+
time = @zone.utc_to_local time
|
90
|
+
end
|
91
|
+
|
92
|
+
[
|
93
|
+
sub_match?(time.sec, @seconds),
|
94
|
+
sub_match?(time.min, @minutes),
|
95
|
+
sub_match?(time.hour, @hours),
|
96
|
+
sub_match?(time.day, @days),
|
97
|
+
sub_match?(time.month, @months),
|
98
|
+
sub_match?(time.wday, @weekdays)
|
99
|
+
].all?
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
104
|
+
# months, weekdays).
|
105
|
+
# This method is used by the cronline unit tests.
|
106
|
+
#
|
107
|
+
def to_array
|
108
|
+
|
109
|
+
[ @seconds, @minutes, @hours, @days, @months, @weekdays, (@zone.name if @zone) ]
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Returns the next time that this cron line is supposed to 'fire'
|
114
|
+
#
|
115
|
+
# This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
|
116
|
+
# (Well, I was wrong, takes 0.001 sec on 1.8.7 and 1.9.1)
|
117
|
+
#
|
118
|
+
# This method accepts an optional Time parameter. It's the starting point
|
119
|
+
# for the 'search'. By default, it's Time.now
|
120
|
+
#
|
121
|
+
# Note that the time instance returned will be in the same time zone that
|
122
|
+
# the given start point Time (thus a result in the local time zone will
|
123
|
+
# be passed if no start time is specified (search start time set to
|
124
|
+
# Time.now))
|
125
|
+
#
|
126
|
+
# >> Rufus::CronLine.new('30 7 * * *').next_time( Time.mktime(2008,10,24,7,29) )
|
127
|
+
# => Fri Oct 24 07:30:00 -0500 2008
|
128
|
+
#
|
129
|
+
# >> Rufus::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) )
|
130
|
+
# => Fri Oct 24 07:30:00 UTC 2008
|
131
|
+
#
|
132
|
+
# >> Rufus::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) ).localtime
|
133
|
+
# => Fri Oct 24 02:30:00 -0500 2008
|
134
|
+
#
|
135
|
+
# (Thanks to K Liu for the note and the examples)
|
136
|
+
#
|
137
|
+
def next_time (time=Time.now)
|
138
|
+
|
139
|
+
if @zone
|
140
|
+
utc = time.utc?
|
141
|
+
time = time.getutc unless utc
|
142
|
+
time = @zone.utc_to_local time
|
143
|
+
end
|
144
|
+
|
145
|
+
time -= time.usec * 1e-6
|
146
|
+
time += 1
|
147
|
+
|
148
|
+
loop do
|
149
|
+
|
150
|
+
unless date_match?(time)
|
151
|
+
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
|
152
|
+
next
|
153
|
+
end
|
154
|
+
|
155
|
+
unless sub_match?(time.hour, @hours)
|
156
|
+
time += (60 - time.min) * 60 - time.sec
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
unless sub_match?(time.min, @minutes)
|
161
|
+
time += 60 - time.sec
|
162
|
+
next
|
163
|
+
end
|
164
|
+
|
165
|
+
unless sub_match?(time.sec, @seconds)
|
166
|
+
time += 1
|
167
|
+
next
|
168
|
+
end
|
169
|
+
|
170
|
+
break
|
171
|
+
end
|
172
|
+
|
173
|
+
if @zone
|
174
|
+
time = @zone.local_to_utc time
|
175
|
+
time = time.getlocal unless utc
|
176
|
+
end
|
177
|
+
|
178
|
+
time
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
WDS = %w[ sun mon tue wed thu fri sat ]
|
184
|
+
#
|
185
|
+
# used by parse_weekday()
|
186
|
+
|
187
|
+
def parse_weekdays (item)
|
188
|
+
|
189
|
+
item = item.downcase
|
190
|
+
|
191
|
+
WDS.each_with_index { |day, index| item = item.gsub(day, index.to_s) }
|
192
|
+
|
193
|
+
r = parse_item(item, 0, 7)
|
194
|
+
|
195
|
+
r.is_a?(Array) ?
|
196
|
+
r.collect { |e| e == 7 ? 0 : e }.uniq :
|
197
|
+
r
|
198
|
+
end
|
199
|
+
|
200
|
+
def parse_item (item, min, max)
|
201
|
+
|
202
|
+
return nil if item == '*'
|
203
|
+
return parse_list(item, min, max) if item.index(',')
|
204
|
+
return parse_range(item, min, max) if item.index('*') or item.index('-')
|
205
|
+
|
206
|
+
i = Integer(item)
|
207
|
+
|
208
|
+
i = min if i < min
|
209
|
+
i = max if i > max
|
210
|
+
|
211
|
+
[ i ]
|
212
|
+
end
|
213
|
+
|
214
|
+
def parse_list (item, min, max)
|
215
|
+
|
216
|
+
item.split(',').inject([]) { |r, i|
|
217
|
+
r.push(parse_range(i, min, max))
|
218
|
+
}.flatten
|
219
|
+
end
|
220
|
+
|
221
|
+
def parse_range (item, min, max)
|
222
|
+
|
223
|
+
i = item.index('-')
|
224
|
+
j = item.index('/')
|
225
|
+
|
226
|
+
return item.to_i if (not i and not j)
|
227
|
+
|
228
|
+
inc = j ? Integer(item[j+1..-1]) : 1
|
229
|
+
|
230
|
+
istart = -1
|
231
|
+
iend = -1
|
232
|
+
|
233
|
+
if i
|
234
|
+
|
235
|
+
istart = Integer(item[0..i - 1])
|
236
|
+
|
237
|
+
if j
|
238
|
+
iend = Integer(item[i + 1..j])
|
239
|
+
else
|
240
|
+
iend = Integer(item[i + 1..-1])
|
241
|
+
end
|
242
|
+
|
243
|
+
else # case */x
|
244
|
+
|
245
|
+
istart = min
|
246
|
+
iend = max
|
247
|
+
end
|
248
|
+
|
249
|
+
istart = min if istart < min
|
250
|
+
iend = max if iend > max
|
251
|
+
|
252
|
+
result = []
|
253
|
+
|
254
|
+
value = istart
|
255
|
+
loop do
|
256
|
+
result << value
|
257
|
+
value = value + inc
|
258
|
+
break if value > iend
|
259
|
+
end
|
260
|
+
|
261
|
+
result
|
262
|
+
end
|
263
|
+
|
264
|
+
def parse_zone zone
|
265
|
+
TZInfo::Timezone.get(zone) rescue nil
|
266
|
+
end
|
267
|
+
|
268
|
+
def sub_match?(value, values)
|
269
|
+
values.nil? || values.include?(value)
|
270
|
+
end
|
271
|
+
|
272
|
+
def date_match?(date)
|
273
|
+
return false unless sub_match?(date.day, @days)
|
274
|
+
return false unless sub_match?(date.month, @months)
|
275
|
+
return false unless sub_match?(date.wday, @weekdays)
|
276
|
+
true
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006-2010, 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
|
+
# Triggers all the jobs that are scheduled for 'now'.
|
54
|
+
#
|
55
|
+
def trigger_matching_jobs
|
56
|
+
|
57
|
+
now = Time.now
|
58
|
+
|
59
|
+
while job = job_to_trigger(now)
|
60
|
+
job.trigger
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Adds this job to the map.
|
65
|
+
#
|
66
|
+
def << (job)
|
67
|
+
|
68
|
+
@mutex.synchronize do
|
69
|
+
delete(job.job_id)
|
70
|
+
@jobs << job
|
71
|
+
@jobs.sort! { |j0, j1| j0.at <=> j1.at }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Removes a job (given its id). Returns nil if the job was not found.
|
76
|
+
#
|
77
|
+
def unschedule (job_id)
|
78
|
+
|
79
|
+
@mutex.synchronize { delete(job_id) }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a mapping job_id => job
|
83
|
+
#
|
84
|
+
def to_h
|
85
|
+
|
86
|
+
@jobs.inject({}) { |h, j| h[j.job_id] = j; h }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a list of jobs of the given type (:at|:in|:every)
|
90
|
+
#
|
91
|
+
def select (type)
|
92
|
+
|
93
|
+
type = JOB_TYPES[type]
|
94
|
+
@jobs.select { |j| j.is_a?(type) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def size
|
98
|
+
|
99
|
+
@jobs.size
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def delete (job_id)
|
105
|
+
|
106
|
+
j = @jobs.find { |j| j.job_id == job_id }
|
107
|
+
@jobs.delete(j) if j
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the next job to trigger. Returns nil if none eligible.
|
111
|
+
#
|
112
|
+
def job_to_trigger (now)
|
113
|
+
|
114
|
+
@mutex.synchronize do
|
115
|
+
if @jobs.size > 0 && now.to_f >= @jobs.first.at
|
116
|
+
@jobs.shift
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Tracking cron jobs.
|
126
|
+
#
|
127
|
+
class CronJobQueue < JobQueue
|
128
|
+
|
129
|
+
def initialize
|
130
|
+
|
131
|
+
super
|
132
|
+
@last_cron_second = nil
|
133
|
+
end
|
134
|
+
|
135
|
+
def trigger_matching_jobs
|
136
|
+
|
137
|
+
now = Time.now
|
138
|
+
|
139
|
+
return if now.sec == @last_cron_second
|
140
|
+
@last_cron_second = now.sec
|
141
|
+
#
|
142
|
+
# ensuring the crons are checked within 1 second (not 1.2 second)
|
143
|
+
|
144
|
+
jobs = @mutex.synchronize { @jobs.dup }
|
145
|
+
|
146
|
+
jobs.each { |job| job.trigger_if_matches(now) }
|
147
|
+
end
|
148
|
+
|
149
|
+
def << (job)
|
150
|
+
|
151
|
+
@mutex.synchronize do
|
152
|
+
delete(job.job_id)
|
153
|
+
@jobs << job
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|