tanzeeb-rufus-scheduler 2.0.7.2
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/.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
|
+
|