rufus-scheduler 2.0.6 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/CHANGELOG.txt +6 -0
- data/CREDITS.txt +2 -0
- data/README.rdoc +45 -12
- data/Rakefile +27 -22
- data/lib/rufus/sc/cronline.rb +61 -44
- data/lib/rufus/sc/jobqueues.rb +6 -7
- data/lib/rufus/sc/jobs.rb +27 -10
- data/lib/rufus/sc/rtime.rb +46 -38
- data/lib/rufus/sc/scheduler.rb +33 -23
- data/lib/rufus/sc/version.rb +1 -1
- data/lib/rufus/scheduler.rb +2 -2
- data/rufus-scheduler.gemspec +29 -13
- data/spec/at_in_spec.rb +8 -9
- data/spec/at_spec.rb +25 -26
- data/spec/blocking_spec.rb +7 -7
- data/spec/cron_spec.rb +23 -23
- data/spec/cronline_spec.rb +175 -40
- data/spec/every_spec.rb +71 -33
- data/spec/exception_spec.rb +11 -12
- data/spec/in_spec.rb +35 -36
- data/spec/rtime_spec.rb +47 -47
- data/spec/schedulable_spec.rb +15 -15
- data/spec/scheduler_spec.rb +14 -15
- data/spec/spec_base.rb +6 -13
- data/spec/stress_schedule_unschedule_spec.rb +124 -120
- data/spec/timeout_spec.rb +24 -24
- metadata +32 -10
- data/spec/spec.rb +0 -14
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
= rufus-scheduler CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-scheduler - 2.0.7 released 2010/11/09
|
6
|
+
|
7
|
+
- cron and timezones, thanks Tanzeeb Khalili
|
8
|
+
- Scheduler#trigger_threads, thanks Tim Uckun
|
9
|
+
|
10
|
+
|
5
11
|
== rufus-scheduler - 2.0.6 released 2010/05/01
|
6
12
|
|
7
13
|
- timeout jobs not outliving their parent job anymore, thanks Joel Wood
|
data/CREDITS.txt
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
== Contributors
|
6
6
|
|
7
|
+
- Tanzeeb Khalili (http://github.com/tanzeeb) cron and timezones
|
8
|
+
- Adam Davies (http://github.com/adz), @allow_overlap = false
|
7
9
|
- Klaas Jan Wierenga, at/every/in stress tests (1.0 and 2.0)
|
8
10
|
- TobyH (http://github.com/tobyh), faster and cleaner CronLine#next_time
|
9
11
|
|
data/README.rdoc
CHANGED
@@ -10,13 +10,14 @@ rufus-scheduler is no replacement for cron/at since it runs inside of Ruby.
|
|
10
10
|
|
11
11
|
A list of related Ruby projects :
|
12
12
|
|
13
|
-
http://github.com/javan/whenever
|
14
|
-
http://github.com/yakischloba/em-timers
|
13
|
+
* http://github.com/javan/whenever
|
14
|
+
* http://github.com/yakischloba/em-timers
|
15
|
+
* http://github.com/adamwiggins/clockwork
|
15
16
|
|
16
17
|
More like complements :
|
17
18
|
|
18
|
-
http://github.com/mojombo/chronic
|
19
|
-
http://github.com/hpoydar/chronic_duration
|
19
|
+
* http://github.com/mojombo/chronic
|
20
|
+
* http://github.com/hpoydar/chronic_duration
|
20
21
|
|
21
22
|
|
22
23
|
== installation
|
@@ -69,6 +70,32 @@ Note that is there is EventMachine present and running,
|
|
69
70
|
will return an instance of Rufus::Scheduler::EmScheduler (leveraging EventMachine).
|
70
71
|
|
71
72
|
|
73
|
+
== a note about cron jobs
|
74
|
+
|
75
|
+
This is a classical cron :
|
76
|
+
|
77
|
+
scheduler.cron '0 22 * * 1-5' do
|
78
|
+
# every day of the week at 22:00 (10pm)
|
79
|
+
end
|
80
|
+
|
81
|
+
Rufus-scheduler supports two variants to that notation : seconds and timezones.
|
82
|
+
|
83
|
+
scheduler.cron '13 0 22 * * 1-5' do
|
84
|
+
# every day of the week at 22:00:13
|
85
|
+
end
|
86
|
+
|
87
|
+
scheduler.cron '0 22 * * 1-5 Europe/Paris' do
|
88
|
+
# every day of the week when it's 22:00 in Paris
|
89
|
+
end
|
90
|
+
scheduler.cron '0 22 * * 1-5 Etc/GMT+2' do
|
91
|
+
# every day of the week when it's 22:00 in GMT+2
|
92
|
+
end
|
93
|
+
|
94
|
+
The timezones are the ones supported by the 'tzinfo' rubygem (http://tzinfo.rubyforge.org/).
|
95
|
+
|
96
|
+
The timezone support was contributed by Tanzeeb Khalili.
|
97
|
+
|
98
|
+
|
72
99
|
== scheduler.join
|
73
100
|
|
74
101
|
Note that if you have a tiny script like this one :
|
@@ -187,10 +214,10 @@ In this example, the 'every' job will unschedule itself when the crop is ready.
|
|
187
214
|
Sometimes passing a block isn't that convenient :
|
188
215
|
|
189
216
|
class JobThing
|
190
|
-
def initialize
|
217
|
+
def initialize(relevant_info)
|
191
218
|
@ri = relevant_info
|
192
219
|
end
|
193
|
-
def call
|
220
|
+
def call(job)
|
194
221
|
do_something_about_it
|
195
222
|
end
|
196
223
|
end
|
@@ -205,7 +232,7 @@ rufus-scheduler accepts anything that responds to a call method with a unique pa
|
|
205
232
|
For compatibility with older (1.x) versions, schedulables with a trigger methods are accepted :
|
206
233
|
|
207
234
|
class JobThing
|
208
|
-
def trigger
|
235
|
+
def trigger(params)
|
209
236
|
job = params[:job]
|
210
237
|
end
|
211
238
|
end
|
@@ -287,13 +314,13 @@ It's easy to customize that behaviour :
|
|
287
314
|
# or
|
288
315
|
#scheduler = Rufus::Scheduler::EmScheduler.start_new
|
289
316
|
|
290
|
-
def scheduler.handle_exception
|
317
|
+
def scheduler.handle_exception(job, exception)
|
291
318
|
puts "job #{job.job_id} caught exception '#{exception}'"
|
292
319
|
end
|
293
320
|
|
294
321
|
For backward compatibility, overriding #log_exception is still OK :
|
295
322
|
|
296
|
-
def scheduler.log_exception
|
323
|
+
def scheduler.log_exception(exception)
|
297
324
|
puts "caught exception '#{exception}'"
|
298
325
|
end
|
299
326
|
|
@@ -334,13 +361,19 @@ More and more ruby applications are using EventMachine. This flavour of the sche
|
|
334
361
|
|
335
362
|
== tested with
|
336
363
|
|
337
|
-
|
338
|
-
|
364
|
+
* 1.8.7-p249
|
365
|
+
* 1.9.1-p378
|
366
|
+
* 1.9.2-p0
|
367
|
+
* jruby-1.5.1
|
368
|
+
|
369
|
+
on Mac OS X (Snow Leopard).
|
339
370
|
|
340
371
|
|
341
372
|
== dependencies
|
342
373
|
|
343
|
-
|
374
|
+
The 'tzinfo' rubygem.
|
375
|
+
|
376
|
+
The ruby gem 'eventmachine' if you use Rufus::Scheduler::EmScheduler, else no other dependencies.
|
344
377
|
|
345
378
|
|
346
379
|
== mailing list
|
data/Rakefile
CHANGED
@@ -10,8 +10,18 @@ load 'lib/rufus/sc/version.rb'
|
|
10
10
|
# CLEAN
|
11
11
|
|
12
12
|
require 'rake/clean'
|
13
|
-
CLEAN.include('pkg', 'tmp', '
|
14
|
-
|
13
|
+
CLEAN.include('pkg', 'tmp', 'rdoc')
|
14
|
+
|
15
|
+
|
16
|
+
#
|
17
|
+
# TEST / SPEC
|
18
|
+
|
19
|
+
task :spec do
|
20
|
+
sh 'rspec spec/'
|
21
|
+
end
|
22
|
+
task :test => :spec
|
23
|
+
|
24
|
+
task :default => :spec
|
15
25
|
|
16
26
|
|
17
27
|
#
|
@@ -35,12 +45,11 @@ Jeweler::Tasks.new do |gem|
|
|
35
45
|
gem.authors = [ 'John Mettraux' ]
|
36
46
|
gem.rubyforge_project = 'rufus'
|
37
47
|
|
38
|
-
gem.test_file = 'spec/spec.rb'
|
48
|
+
#gem.test_file = 'spec/spec.rb'
|
39
49
|
|
40
|
-
|
50
|
+
gem.add_dependency 'tzinfo'
|
41
51
|
gem.add_development_dependency 'rake'
|
42
|
-
gem.add_development_dependency '
|
43
|
-
gem.add_development_dependency 'bacon'
|
52
|
+
gem.add_development_dependency 'rspec'
|
44
53
|
gem.add_development_dependency 'jeweler'
|
45
54
|
|
46
55
|
# gemspec spec : http://www.rubygems.org/read/chapter/20
|
@@ -51,33 +60,29 @@ Jeweler::GemcutterTasks.new
|
|
51
60
|
#
|
52
61
|
# DOC
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
doc.options = [
|
60
|
-
'-o', 'html/rufus-scheduler', '--title',
|
61
|
-
"rufus-scheduler #{Rufus::Scheduler::VERSION}"
|
62
|
-
]
|
63
|
-
end
|
63
|
+
#
|
64
|
+
# make sure to have rdoc 2.5.x to run that
|
65
|
+
#
|
66
|
+
require 'rake/rdoctask'
|
67
|
+
Rake::RDocTask.new do |rd|
|
64
68
|
|
65
|
-
|
69
|
+
rd.main = 'README.rdoc'
|
70
|
+
rd.rdoc_dir = 'rdoc/rufus-scheduler'
|
71
|
+
rd.title = "rufus-scheduler #{Rufus::Scheduler::VERSION}"
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
end
|
73
|
+
rd.rdoc_files.include(
|
74
|
+
'README.rdoc', 'CHANGELOG.txt', 'LICENSE.txt', 'CREDITS.txt', 'lib/**/*.rb')
|
70
75
|
end
|
71
76
|
|
72
77
|
|
73
78
|
#
|
74
79
|
# TO THE WEB
|
75
80
|
|
76
|
-
task :
|
81
|
+
task :upload_rdoc => [ :clean, :rdoc ] do
|
77
82
|
|
78
83
|
account = 'jmettraux@rubyforge.org'
|
79
84
|
webdir = '/var/www/gforge-projects/rufus'
|
80
85
|
|
81
|
-
sh "rsync -azv -e ssh
|
86
|
+
sh "rsync -azv -e ssh rdoc/rufus-scheduler #{account}:#{webdir}/"
|
82
87
|
end
|
83
88
|
|
data/lib/rufus/sc/cronline.rb
CHANGED
@@ -22,6 +22,8 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
require 'tzinfo'
|
26
|
+
|
25
27
|
|
26
28
|
module Rufus
|
27
29
|
|
@@ -31,20 +33,19 @@ module Rufus
|
|
31
33
|
#
|
32
34
|
class CronLine
|
33
35
|
|
34
|
-
#
|
35
36
|
# The string used for creating this cronline instance.
|
36
37
|
#
|
37
38
|
attr_reader :original
|
38
39
|
|
39
|
-
attr_reader
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
attr_reader :seconds
|
41
|
+
attr_reader :minutes
|
42
|
+
attr_reader :hours
|
43
|
+
attr_reader :days
|
44
|
+
attr_reader :months
|
45
|
+
attr_reader :weekdays
|
46
|
+
attr_reader :timezone
|
46
47
|
|
47
|
-
def initialize
|
48
|
+
def initialize(line)
|
48
49
|
|
49
50
|
super()
|
50
51
|
|
@@ -52,10 +53,12 @@ module Rufus
|
|
52
53
|
|
53
54
|
items = line.split
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
@timezone = (TZInfo::Timezone.get(items.last) rescue nil)
|
57
|
+
items.pop if @timezone
|
58
|
+
|
59
|
+
raise ArgumentError.new(
|
60
|
+
"not a valid cronline : '#{line}'"
|
61
|
+
) unless items.length == 5 or items.length == 6
|
59
62
|
|
60
63
|
offset = items.length - 5
|
61
64
|
|
@@ -67,13 +70,14 @@ module Rufus
|
|
67
70
|
@weekdays = parse_weekdays(items[4 + offset])
|
68
71
|
end
|
69
72
|
|
70
|
-
#
|
71
73
|
# Returns true if the given time matches this cron line.
|
72
74
|
#
|
73
|
-
def matches?
|
75
|
+
def matches?(time)
|
74
76
|
|
75
77
|
time = Time.at(time) unless time.kind_of?(Time)
|
76
78
|
|
79
|
+
time = @timezone.utc_to_local(time.getutc) if @timezone
|
80
|
+
|
77
81
|
return false unless sub_match?(time.sec, @seconds)
|
78
82
|
return false unless sub_match?(time.min, @minutes)
|
79
83
|
return false unless sub_match?(time.hour, @hours)
|
@@ -83,17 +87,6 @@ module Rufus
|
|
83
87
|
true
|
84
88
|
end
|
85
89
|
|
86
|
-
#
|
87
|
-
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
88
|
-
# months, weekdays).
|
89
|
-
# This method is used by the cronline unit tests.
|
90
|
-
#
|
91
|
-
def to_array
|
92
|
-
|
93
|
-
[ @seconds, @minutes, @hours, @days, @months, @weekdays ]
|
94
|
-
end
|
95
|
-
|
96
|
-
#
|
97
90
|
# Returns the next time that this cron line is supposed to 'fire'
|
98
91
|
#
|
99
92
|
# This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
|
@@ -107,21 +100,26 @@ module Rufus
|
|
107
100
|
# be passed if no start time is specified (search start time set to
|
108
101
|
# Time.now))
|
109
102
|
#
|
110
|
-
#
|
111
|
-
#
|
103
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
104
|
+
# Time.mktime(2008, 10, 24, 7, 29))
|
105
|
+
# #=> Fri Oct 24 07:30:00 -0500 2008
|
112
106
|
#
|
113
|
-
#
|
114
|
-
#
|
107
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
108
|
+
# Time.utc(2008, 10, 24, 7, 29))
|
109
|
+
# #=> Fri Oct 24 07:30:00 UTC 2008
|
115
110
|
#
|
116
|
-
#
|
117
|
-
#
|
111
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
112
|
+
# Time.utc(2008, 10, 24, 7, 29)).localtime
|
113
|
+
# #=> Fri Oct 24 02:30:00 -0500 2008
|
118
114
|
#
|
119
115
|
# (Thanks to K Liu for the note and the examples)
|
120
116
|
#
|
121
|
-
def next_time
|
117
|
+
def next_time(now=Time.now)
|
118
|
+
|
119
|
+
time = @timezone ? @timezone.utc_to_local(now.getutc) : now
|
122
120
|
|
123
|
-
time
|
124
|
-
|
121
|
+
time = time - time.usec * 1e-6 + 1
|
122
|
+
# little adjustment before starting
|
125
123
|
|
126
124
|
loop do
|
127
125
|
|
@@ -129,17 +127,14 @@ module Rufus
|
|
129
127
|
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
|
130
128
|
next
|
131
129
|
end
|
132
|
-
|
133
130
|
unless sub_match?(time.hour, @hours)
|
134
131
|
time += (60 - time.min) * 60 - time.sec
|
135
132
|
next
|
136
133
|
end
|
137
|
-
|
138
134
|
unless sub_match?(time.min, @minutes)
|
139
135
|
time += 60 - time.sec
|
140
136
|
next
|
141
137
|
end
|
142
|
-
|
143
138
|
unless sub_match?(time.sec, @seconds)
|
144
139
|
time += 1
|
145
140
|
next
|
@@ -148,16 +143,37 @@ module Rufus
|
|
148
143
|
break
|
149
144
|
end
|
150
145
|
|
146
|
+
if @timezone
|
147
|
+
time = @timezone.local_to_utc(time)
|
148
|
+
time = time.getlocal unless now.utc?
|
149
|
+
end
|
150
|
+
|
151
151
|
time
|
152
152
|
end
|
153
153
|
|
154
|
+
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
155
|
+
# months, weekdays).
|
156
|
+
# This method is used by the cronline unit tests.
|
157
|
+
#
|
158
|
+
def to_array
|
159
|
+
|
160
|
+
[
|
161
|
+
@seconds,
|
162
|
+
@minutes,
|
163
|
+
@hours,
|
164
|
+
@days,
|
165
|
+
@months,
|
166
|
+
@weekdays,
|
167
|
+
@timezone ? @timezone.name : nil
|
168
|
+
]
|
169
|
+
end
|
170
|
+
|
154
171
|
private
|
155
172
|
|
156
173
|
WDS = %w[ sun mon tue wed thu fri sat ]
|
157
|
-
#
|
158
174
|
# used by parse_weekday()
|
159
175
|
|
160
|
-
def parse_weekdays
|
176
|
+
def parse_weekdays(item)
|
161
177
|
|
162
178
|
item = item.downcase
|
163
179
|
|
@@ -170,7 +186,7 @@ module Rufus
|
|
170
186
|
r
|
171
187
|
end
|
172
188
|
|
173
|
-
def parse_item
|
189
|
+
def parse_item(item, min, max)
|
174
190
|
|
175
191
|
return nil if item == '*'
|
176
192
|
return parse_list(item, min, max) if item.index(',')
|
@@ -184,14 +200,14 @@ module Rufus
|
|
184
200
|
[ i ]
|
185
201
|
end
|
186
202
|
|
187
|
-
def parse_list
|
203
|
+
def parse_list(item, min, max)
|
188
204
|
|
189
205
|
item.split(',').inject([]) { |r, i|
|
190
206
|
r.push(parse_range(i, min, max))
|
191
207
|
}.flatten
|
192
208
|
end
|
193
209
|
|
194
|
-
def parse_range
|
210
|
+
def parse_range(item, min, max)
|
195
211
|
|
196
212
|
i = item.index('-')
|
197
213
|
j = item.index('/')
|
@@ -235,16 +251,17 @@ module Rufus
|
|
235
251
|
end
|
236
252
|
|
237
253
|
def sub_match?(value, values)
|
254
|
+
|
238
255
|
values.nil? || values.include?(value)
|
239
256
|
end
|
240
257
|
|
241
258
|
def date_match?(date)
|
259
|
+
|
242
260
|
return false unless sub_match?(date.day, @days)
|
243
261
|
return false unless sub_match?(date.month, @months)
|
244
262
|
return false unless sub_match?(date.wday, @weekdays)
|
245
263
|
true
|
246
264
|
end
|
247
265
|
end
|
248
|
-
|
249
266
|
end
|
250
267
|
|
data/lib/rufus/sc/jobqueues.rb
CHANGED
@@ -63,7 +63,7 @@ module Scheduler
|
|
63
63
|
|
64
64
|
# Adds this job to the map.
|
65
65
|
#
|
66
|
-
def <<
|
66
|
+
def <<(job)
|
67
67
|
|
68
68
|
@mutex.synchronize do
|
69
69
|
delete(job.job_id)
|
@@ -74,7 +74,7 @@ module Scheduler
|
|
74
74
|
|
75
75
|
# Removes a job (given its id). Returns nil if the job was not found.
|
76
76
|
#
|
77
|
-
def unschedule
|
77
|
+
def unschedule(job_id)
|
78
78
|
|
79
79
|
@mutex.synchronize { delete(job_id) }
|
80
80
|
end
|
@@ -88,7 +88,7 @@ module Scheduler
|
|
88
88
|
|
89
89
|
# Returns a list of jobs of the given type (:at|:in|:every)
|
90
90
|
#
|
91
|
-
def select
|
91
|
+
def select(type)
|
92
92
|
|
93
93
|
type = JOB_TYPES[type]
|
94
94
|
@jobs.select { |j| j.is_a?(type) }
|
@@ -101,7 +101,7 @@ module Scheduler
|
|
101
101
|
|
102
102
|
protected
|
103
103
|
|
104
|
-
def delete
|
104
|
+
def delete(job_id)
|
105
105
|
|
106
106
|
j = @jobs.find { |j| j.job_id == job_id }
|
107
107
|
@jobs.delete(j) if j
|
@@ -109,7 +109,7 @@ module Scheduler
|
|
109
109
|
|
110
110
|
# Returns the next job to trigger. Returns nil if none eligible.
|
111
111
|
#
|
112
|
-
def job_to_trigger
|
112
|
+
def job_to_trigger(now)
|
113
113
|
|
114
114
|
@mutex.synchronize do
|
115
115
|
if @jobs.size > 0 && now.to_f >= @jobs.first.at
|
@@ -146,7 +146,7 @@ module Scheduler
|
|
146
146
|
jobs.each { |job| job.trigger_if_matches(now) }
|
147
147
|
end
|
148
148
|
|
149
|
-
def <<
|
149
|
+
def <<(job)
|
150
150
|
|
151
151
|
@mutex.synchronize do
|
152
152
|
delete(job.job_id)
|
@@ -154,7 +154,6 @@ module Scheduler
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
end
|
157
|
-
|
158
157
|
end
|
159
158
|
end
|
160
159
|
|