rufus-scheduler 2.0.6 → 2.0.7
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/.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
|
|