rufus-scheduler 2.0.24 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +6 -0
- data/CREDITS.txt +4 -0
- data/README.md +1064 -0
- data/Rakefile +1 -4
- data/TODO.txt +145 -55
- data/lib/rufus/scheduler.rb +502 -26
- data/lib/rufus/{sc → scheduler}/cronline.rb +46 -17
- data/lib/rufus/{sc/version.rb → scheduler/job_array.rb} +56 -4
- data/lib/rufus/scheduler/jobs.rb +548 -0
- data/lib/rufus/scheduler/util.rb +318 -0
- data/rufus-scheduler.gemspec +30 -4
- data/spec/cronline_spec.rb +29 -8
- data/spec/error_spec.rb +116 -0
- data/spec/job_array_spec.rb +39 -0
- data/spec/job_at_spec.rb +58 -0
- data/spec/job_cron_spec.rb +67 -0
- data/spec/job_every_spec.rb +71 -0
- data/spec/job_in_spec.rb +20 -0
- data/spec/job_interval_spec.rb +68 -0
- data/spec/job_repeat_spec.rb +308 -0
- data/spec/job_spec.rb +387 -115
- data/spec/lockfile_spec.rb +61 -0
- data/spec/parse_spec.rb +203 -0
- data/spec/schedule_at_spec.rb +129 -0
- data/spec/schedule_cron_spec.rb +66 -0
- data/spec/schedule_every_spec.rb +109 -0
- data/spec/schedule_in_spec.rb +80 -0
- data/spec/schedule_interval_spec.rb +128 -0
- data/spec/scheduler_spec.rb +831 -124
- data/spec/spec_helper.rb +65 -0
- data/spec/threads_spec.rb +75 -0
- metadata +64 -59
- data/README.rdoc +0 -661
- data/lib/rufus/otime.rb +0 -3
- data/lib/rufus/sc/jobqueues.rb +0 -160
- data/lib/rufus/sc/jobs.rb +0 -471
- data/lib/rufus/sc/rtime.rb +0 -363
- data/lib/rufus/sc/scheduler.rb +0 -636
- data/spec/at_in_spec.rb +0 -47
- data/spec/at_spec.rb +0 -125
- data/spec/blocking_spec.rb +0 -64
- data/spec/cron_spec.rb +0 -134
- data/spec/every_spec.rb +0 -304
- data/spec/exception_spec.rb +0 -113
- data/spec/in_spec.rb +0 -150
- data/spec/mutex_spec.rb +0 -159
- data/spec/rtime_spec.rb +0 -137
- data/spec/schedulable_spec.rb +0 -97
- data/spec/spec_base.rb +0 -87
- data/spec/stress_schedule_unschedule_spec.rb +0 -159
- data/spec/timeout_spec.rb +0 -148
- data/test/kjw.rb +0 -113
- data/test/t.rb +0 -20
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Specifying rufus-scheduler
|
4
|
+
#
|
5
|
+
# Wed Apr 17 06:00:59 JST 2013
|
6
|
+
#
|
7
|
+
|
8
|
+
Thread.abort_on_exception = true
|
9
|
+
|
10
|
+
|
11
|
+
require 'stringio'
|
12
|
+
require 'rufus-scheduler'
|
13
|
+
|
14
|
+
|
15
|
+
def local(*args)
|
16
|
+
|
17
|
+
Time.local(*args)
|
18
|
+
end
|
19
|
+
alias lo local
|
20
|
+
|
21
|
+
def utc(*args)
|
22
|
+
|
23
|
+
Time.utc(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def sleep_until_next_minute
|
27
|
+
|
28
|
+
min = Time.now.min
|
29
|
+
while Time.now.min == min; sleep 2; end
|
30
|
+
end
|
31
|
+
|
32
|
+
def sleep_until_next_second
|
33
|
+
|
34
|
+
sec = Time.now.sec
|
35
|
+
while Time.now.sec == sec; sleep 0.2; end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#require 'rspec/expectations'
|
40
|
+
|
41
|
+
RSpec::Matchers.define :be_within_1s_of do |expected|
|
42
|
+
|
43
|
+
match do |actual|
|
44
|
+
|
45
|
+
if actual.respond_to?(:asctime)
|
46
|
+
(actual.to_f - expected.to_f).abs <= 1.0
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
failure_message_for_should do |actual|
|
53
|
+
|
54
|
+
if actual.respond_to?(:asctime)
|
55
|
+
"expected #{actual.inspect} to be within 1 second of #{expected}"
|
56
|
+
else
|
57
|
+
"expected Time instance, got a #{actual.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
#RSpec.configure do |config|
|
64
|
+
#end
|
65
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Specifying rufus-scheduler
|
4
|
+
#
|
5
|
+
# Thu Jul 25 05:53:51 JST 2013
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'spec_helper'
|
9
|
+
|
10
|
+
|
11
|
+
describe Rufus::Scheduler do
|
12
|
+
|
13
|
+
before :each do
|
14
|
+
@scheduler = Rufus::Scheduler.new
|
15
|
+
end
|
16
|
+
after :each do
|
17
|
+
@scheduler.shutdown
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'thread pool' do
|
21
|
+
|
22
|
+
it 'starts with an empty thread pool' do
|
23
|
+
|
24
|
+
@scheduler.work_threads.size.should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not cross the max_work_threads threshold' do
|
28
|
+
|
29
|
+
#@scheduler.min_work_threads = 2
|
30
|
+
@scheduler.max_work_threads = 5
|
31
|
+
|
32
|
+
10.times do
|
33
|
+
@scheduler.in '0s' do
|
34
|
+
sleep 5
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sleep 0.5
|
39
|
+
|
40
|
+
#@scheduler.job_threads.each do |t|
|
41
|
+
# p t.keys
|
42
|
+
# p t[:rufus_scheduler_job].class
|
43
|
+
#end
|
44
|
+
|
45
|
+
@scheduler.work_threads.size.should == 5
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'does not execute unscheduled jobs' do
|
49
|
+
|
50
|
+
@scheduler.max_work_threads = 1
|
51
|
+
|
52
|
+
counter = 0
|
53
|
+
|
54
|
+
job0 =
|
55
|
+
@scheduler.schedule_in '0.3s' do
|
56
|
+
counter += 1
|
57
|
+
sleep 1
|
58
|
+
end
|
59
|
+
job1 =
|
60
|
+
@scheduler.schedule_in '0.35s' do
|
61
|
+
counter += 1
|
62
|
+
sleep 1
|
63
|
+
end
|
64
|
+
|
65
|
+
sleep(0.1) while counter < 1
|
66
|
+
sleep(0.1) while @scheduler.work_queue.size < 1
|
67
|
+
job1.unschedule
|
68
|
+
|
69
|
+
sleep(2)
|
70
|
+
|
71
|
+
counter.should == 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
metadata
CHANGED
@@ -1,64 +1,64 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rufus-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 3.0.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- John Mettraux
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tzinfo
|
16
|
-
|
17
|
-
none: false
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
18
17
|
requirements:
|
19
|
-
- -
|
18
|
+
- - '>='
|
20
19
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
version: '0'
|
25
21
|
none: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0
|
26
|
+
version: '0'
|
27
|
+
none: false
|
28
|
+
prerelease: false
|
29
|
+
type: :runtime
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
|
-
|
33
|
-
none: false
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
34
33
|
requirements:
|
35
|
-
- -
|
34
|
+
- - '>='
|
36
35
|
- !ruby/object:Gem::Version
|
37
36
|
version: '0'
|
38
|
-
type: :development
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
37
|
none: false
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
42
39
|
requirements:
|
43
|
-
- -
|
40
|
+
- - '>='
|
44
41
|
- !ruby/object:Gem::Version
|
45
42
|
version: '0'
|
43
|
+
none: false
|
44
|
+
prerelease: false
|
45
|
+
type: :development
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: rspec
|
48
|
-
|
49
|
-
none: false
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
49
|
requirements:
|
51
|
-
- -
|
50
|
+
- - '>='
|
52
51
|
- !ruby/object:Gem::Version
|
53
|
-
version: 2.
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
version: 2.13.0
|
57
53
|
none: false
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
58
55
|
requirements:
|
59
|
-
- -
|
56
|
+
- - '>='
|
60
57
|
- !ruby/object:Gem::Version
|
61
|
-
version: 2.
|
58
|
+
version: 2.13.0
|
59
|
+
none: false
|
60
|
+
prerelease: false
|
61
|
+
type: :development
|
62
62
|
description: job scheduler for Ruby (at, cron, in and every jobs).
|
63
63
|
email:
|
64
64
|
- jmettraux@gmail.com
|
@@ -69,60 +69,65 @@ files:
|
|
69
69
|
- Rakefile
|
70
70
|
- lib/rufus-scheduler.rb
|
71
71
|
- lib/rufus/scheduler.rb
|
72
|
-
- lib/rufus/
|
73
|
-
- lib/rufus/
|
74
|
-
- lib/rufus/
|
75
|
-
- lib/rufus/
|
76
|
-
- lib/rufus/sc/jobs.rb
|
77
|
-
- lib/rufus/sc/jobqueues.rb
|
78
|
-
- lib/rufus/otime.rb
|
72
|
+
- lib/rufus/scheduler/cronline.rb
|
73
|
+
- lib/rufus/scheduler/util.rb
|
74
|
+
- lib/rufus/scheduler/jobs.rb
|
75
|
+
- lib/rufus/scheduler/job_array.rb
|
79
76
|
- spec/job_spec.rb
|
80
|
-
- spec/
|
81
|
-
- spec/
|
82
|
-
- spec/
|
83
|
-
- spec/
|
84
|
-
- spec/
|
85
|
-
- spec/
|
86
|
-
- spec/
|
87
|
-
- spec/
|
77
|
+
- spec/schedule_in_spec.rb
|
78
|
+
- spec/spec_helper.rb
|
79
|
+
- spec/job_array_spec.rb
|
80
|
+
- spec/schedule_interval_spec.rb
|
81
|
+
- spec/job_at_spec.rb
|
82
|
+
- spec/schedule_at_spec.rb
|
83
|
+
- spec/error_spec.rb
|
84
|
+
- spec/schedule_cron_spec.rb
|
85
|
+
- spec/job_interval_spec.rb
|
86
|
+
- spec/job_repeat_spec.rb
|
87
|
+
- spec/lockfile_spec.rb
|
88
|
+
- spec/schedule_every_spec.rb
|
89
|
+
- spec/threads_spec.rb
|
88
90
|
- spec/cronline_spec.rb
|
89
|
-
- spec/
|
90
|
-
- spec/
|
91
|
+
- spec/job_in_spec.rb
|
92
|
+
- spec/parse_spec.rb
|
91
93
|
- spec/scheduler_spec.rb
|
92
|
-
- spec/
|
93
|
-
- spec/
|
94
|
-
- spec/cron_spec.rb
|
95
|
-
- test/t.rb
|
96
|
-
- test/kjw.rb
|
94
|
+
- spec/job_cron_spec.rb
|
95
|
+
- spec/job_every_spec.rb
|
97
96
|
- rufus-scheduler.gemspec
|
98
97
|
- CHANGELOG.txt
|
99
98
|
- TODO.txt
|
100
99
|
- LICENSE.txt
|
101
100
|
- CREDITS.txt
|
102
|
-
- README.
|
101
|
+
- README.md
|
103
102
|
homepage: http://github.com/jmettraux/rufus-scheduler
|
104
103
|
licenses:
|
105
104
|
- MIT
|
106
|
-
post_install_message:
|
105
|
+
post_install_message: "\n***\n\nThanks for installing rufus-scheduler 3.0.0\n\nIt\
|
106
|
+
\ might not be 100% compatible with rufus-scheduler 2.x.\n\nIf you encounter issues\
|
107
|
+
\ with this new rufus-scheduler, especially\nif your app worked fine with previous\
|
108
|
+
\ versions of it, you can\n\nA) Forget it and peg your Gemfile to rufus-scheduler\
|
109
|
+
\ 2.0.24\n\nand / or\n\nB) Take some time to carefully report the issue at\n https://github.com/jmettraux/rufus-scheduler/issue\n\
|
110
|
+
\nFor general help about rufus-scheduler, ask via:\nhttp://stackoverflow.com/questions/ask?tags=rufus-scheduler+ruby\n\
|
111
|
+
\nCheers.\n\n***\n "
|
107
112
|
rdoc_options: []
|
108
113
|
require_paths:
|
109
114
|
- lib
|
110
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
-
none: false
|
112
116
|
requirements:
|
113
|
-
- -
|
117
|
+
- - '>='
|
114
118
|
- !ruby/object:Gem::Version
|
115
119
|
version: '0'
|
116
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
120
|
none: false
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
122
|
requirements:
|
119
|
-
- -
|
123
|
+
- - '>='
|
120
124
|
- !ruby/object:Gem::Version
|
121
125
|
version: '0'
|
126
|
+
none: false
|
122
127
|
requirements: []
|
123
128
|
rubyforge_project: rufus
|
124
|
-
rubygems_version: 1.8.
|
125
|
-
signing_key:
|
129
|
+
rubygems_version: 1.8.24
|
130
|
+
signing_key:
|
126
131
|
specification_version: 3
|
127
132
|
summary: job scheduler for Ruby (at, cron, in and every jobs)
|
128
133
|
test_files: []
|
data/README.rdoc
DELETED
@@ -1,661 +0,0 @@
|
|
1
|
-
= rufus-scheduler
|
2
|
-
|
3
|
-
rufus-scheduler is a Ruby gem for scheduling pieces of code (jobs). It understands running a job AT a certain time, IN a certain time, EVERY x time or simply via a CRON statement.
|
4
|
-
|
5
|
-
rufus-scheduler is no replacement for cron/at since it runs inside of Ruby.
|
6
|
-
|
7
|
-
|
8
|
-
== alternatives / complements
|
9
|
-
|
10
|
-
A list of related Ruby projects :
|
11
|
-
|
12
|
-
* http://github.com/javan/whenever
|
13
|
-
* http://github.com/yakischloba/em-timers
|
14
|
-
* http://github.com/adamwiggins/clockwork
|
15
|
-
|
16
|
-
More like complements :
|
17
|
-
|
18
|
-
* http://github.com/mojombo/chronic
|
19
|
-
* http://github.com/hpoydar/chronic_duration
|
20
|
-
|
21
|
-
|
22
|
-
== installation
|
23
|
-
|
24
|
-
gem install rufus-scheduler
|
25
|
-
|
26
|
-
|
27
|
-
== usage
|
28
|
-
|
29
|
-
The usage is similar to the one of the old rufus-scheduler. There are a few differences though.
|
30
|
-
|
31
|
-
require 'rubygems'
|
32
|
-
require 'rufus/scheduler'
|
33
|
-
|
34
|
-
scheduler = Rufus::Scheduler.start_new
|
35
|
-
|
36
|
-
scheduler.in '20m' do
|
37
|
-
puts "order ristretto"
|
38
|
-
end
|
39
|
-
|
40
|
-
scheduler.at 'Thu Mar 26 07:31:43 +0900 2009' do
|
41
|
-
puts 'order pizza'
|
42
|
-
end
|
43
|
-
|
44
|
-
scheduler.cron '0 22 * * 1-5' do
|
45
|
-
# every day of the week at 22:00 (10pm)
|
46
|
-
puts 'activate security system'
|
47
|
-
end
|
48
|
-
|
49
|
-
scheduler.every '5m' do
|
50
|
-
puts 'check blood pressure'
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
This code summons a plain version of the scheduler, this can be made more explicit via :
|
55
|
-
|
56
|
-
scheduler = Rufus::Scheduler::PlainScheduler.start_new
|
57
|
-
|
58
|
-
This PlainScheduler accepts a :thread_name option :
|
59
|
-
|
60
|
-
scheduler = Rufus::Scheduler::PlainScheduler.start_new(:thread_name => 'my scheduler')
|
61
|
-
|
62
|
-
which might be helpful when tracking threads.
|
63
|
-
|
64
|
-
|
65
|
-
Note that if there is an EventMachine present and running,
|
66
|
-
|
67
|
-
scheduler = Rufus::Scheduler.start_new
|
68
|
-
|
69
|
-
will return an instance of Rufus::Scheduler::EmScheduler (leveraging EventMachine).
|
70
|
-
|
71
|
-
|
72
|
-
== a note about cron jobs
|
73
|
-
|
74
|
-
This is a classical cron :
|
75
|
-
|
76
|
-
scheduler.cron '0 22 * * 1-5' do
|
77
|
-
# every day of the week at 22:00 (10pm)
|
78
|
-
end
|
79
|
-
|
80
|
-
Rufus-scheduler supports three variants to that notation : seconds, last day of month and timezones.
|
81
|
-
|
82
|
-
scheduler.cron '13 0 22 * * 1-5' do
|
83
|
-
# every day of the week at 22:00:13
|
84
|
-
end
|
85
|
-
|
86
|
-
scheduler.cron '0 22 L * *' do
|
87
|
-
# every month on the last day at 22:00
|
88
|
-
end
|
89
|
-
|
90
|
-
scheduler.cron '0 22 * * 1-5 Europe/Paris' do
|
91
|
-
# every day of the week when it's 22:00 in Paris
|
92
|
-
end
|
93
|
-
scheduler.cron '0 22 * * 1-5 Etc/GMT+2' do
|
94
|
-
# every day of the week when it's 22:00 in GMT+2
|
95
|
-
end
|
96
|
-
|
97
|
-
The timezones are the ones supported by the 'tzinfo' rubygem (http://tzinfo.rubyforge.org/).
|
98
|
-
|
99
|
-
The timezone support was contributed by Tanzeeb Khalili.
|
100
|
-
|
101
|
-
"monthdays" are supported
|
102
|
-
|
103
|
-
scheduler.cron '0 22 * * sun#1,sun#2' do
|
104
|
-
# every first and second sunday of the month, at 22:00
|
105
|
-
end
|
106
|
-
|
107
|
-
It's also OK (since 2.0.19) to use L (for last monthday) or negative numbers.
|
108
|
-
|
109
|
-
scheduler.cron '0 22 * * sun#-1' do
|
110
|
-
# every last sunday of the month, at 22:00
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
== scheduler.join
|
115
|
-
|
116
|
-
Note that if you have a tiny script like this one :
|
117
|
-
|
118
|
-
require 'rubygems'; require 'rufus-scheduler'
|
119
|
-
|
120
|
-
scheduler = Rufus::Scheduler.start_new
|
121
|
-
|
122
|
-
scheduler.at 'Thu Mar 26 07:31:43 +0900 2009' do
|
123
|
-
puts 'order pizza'
|
124
|
-
end
|
125
|
-
|
126
|
-
And you run it, it will exit immediately.
|
127
|
-
|
128
|
-
If you place
|
129
|
-
|
130
|
-
scheduler.join
|
131
|
-
|
132
|
-
at the end, it will make the current (main) thread join the scheduler and prevent the Ruby runtime from exiting.
|
133
|
-
|
134
|
-
You shouldn't be exposed to this issue when using EventMachine, since while running EM, your runtime won't exit.
|
135
|
-
|
136
|
-
=== important note about #join
|
137
|
-
|
138
|
-
DO NOT CALL this #join method if you're running rufus-scheduler from Rails or Sinatra or any application that's already some kind of 'daemon'. It's not necessary! #join is meant for small standalone scripts.
|
139
|
-
|
140
|
-
|
141
|
-
== schedule.stop
|
142
|
-
|
143
|
-
scheduler.stop
|
144
|
-
|
145
|
-
This call stops the scheduler. It doesn't unschedule jobs. If there are running jobs, they're left running.
|
146
|
-
|
147
|
-
If you need to stop the scheduler and wait for all the jobs currently running to finish (without killing them), rufus-scheduler 2.0.20 brings a new :terminate => true option.
|
148
|
-
|
149
|
-
scheduler.stop(:terminate => true)
|
150
|
-
# returns once all the jobs have been unscheduled and no jobs is running
|
151
|
-
|
152
|
-
|
153
|
-
== block parameters
|
154
|
-
|
155
|
-
Scheduled blocks accept 0 or 1 parameter (this unique parameter is the job
|
156
|
-
instance itself).
|
157
|
-
|
158
|
-
scheduler.every '5m' do
|
159
|
-
puts 'check blood pressure'
|
160
|
-
end
|
161
|
-
scheduler.every '1y' do |job|
|
162
|
-
puts "check cholesterol levels (#{job.job_id})"
|
163
|
-
end
|
164
|
-
|
165
|
-
See the class Job for more details :
|
166
|
-
|
167
|
-
http://rufus.rubyforge.org/rufus-scheduler/classes/Rufus/Scheduler/Job.html
|
168
|
-
|
169
|
-
|
170
|
-
== the time strings understood by rufus-scheduler
|
171
|
-
|
172
|
-
require 'rubygems'
|
173
|
-
require 'rufus/scheduler'
|
174
|
-
|
175
|
-
p Rufus.parse_time_string '500' # => 0.5
|
176
|
-
p Rufus.parse_time_string '1000' # => 1.0
|
177
|
-
p Rufus.parse_time_string '1h' # => 3600.0
|
178
|
-
p Rufus.parse_time_string '1h10s' # => 3610.0
|
179
|
-
p Rufus.parse_time_string '1w2d' # => 777600.0
|
180
|
-
|
181
|
-
p Rufus.to_time_string 60 # => "1m"
|
182
|
-
p Rufus.to_time_string 3661 # => "1h1m1s"
|
183
|
-
p Rufus.to_time_string 7 * 24 * 3600 # => "1w"
|
184
|
-
|
185
|
-
|
186
|
-
== :blocking => true
|
187
|
-
|
188
|
-
Jobs will, by default, trigger in their own thread. This is usually desirable since one expects the scheduler to continue scheduling even if a job is currently running.
|
189
|
-
|
190
|
-
Jobs scheduled with the :blocking parameter will run in the thread of the scheduler, blocking it.
|
191
|
-
|
192
|
-
scheduler.in '20m', :blocking => true do
|
193
|
-
puts "order ristretto"
|
194
|
-
sleep 2 * 60
|
195
|
-
end
|
196
|
-
scheduler.in '21m' do
|
197
|
-
puts "order espresso"
|
198
|
-
end
|
199
|
-
|
200
|
-
Hence, our espresso will come in 22 minutes instead of 21.
|
201
|
-
|
202
|
-
Warning, 'cron' behaves a bit differently than 'in' and 'at', if the scheduler is blocked working on a task, it may skip crons (while ins and ats get scheduled after).
|
203
|
-
|
204
|
-
scheduler.cron '0 16 * * * *' do
|
205
|
-
puts "four o'clock tea"
|
206
|
-
end
|
207
|
-
|
208
|
-
If at 4pm the scheduler is in a blocking task, there will be no four o'clock tea.
|
209
|
-
|
210
|
-
|
211
|
-
== :mutex => 'that_mutex'
|
212
|
-
|
213
|
-
:blocking is nice but it is blocking the whole scheduler. What about something more fine-grained ? And also something that can be used with in, at, every and cron ?
|
214
|
-
|
215
|
-
scheduler.in '20m', :mutex => 'that_mutex' do
|
216
|
-
puts "order ristretto"
|
217
|
-
sleep 2 * 60
|
218
|
-
puts "ah, that was delicious"
|
219
|
-
end
|
220
|
-
scheduler.in '21m' :mutex => 'that_mutex' do
|
221
|
-
puts "order espresso"
|
222
|
-
end
|
223
|
-
|
224
|
-
the "order espresso" will only get triggered once the ristretto has been consumed. Rufus-scheduler will create a 'that_mutex' mutex and keep track of it. Don't go on passing too many different mutex names, rufus-scheduler will keep track of each of them (they won't get garbage collected).
|
225
|
-
|
226
|
-
It's OK to use a mutex directly:
|
227
|
-
|
228
|
-
m = Mutex.new
|
229
|
-
# ...
|
230
|
-
scheduler.cron '0 18 * * *', :mutex => m do
|
231
|
-
# ...
|
232
|
-
end
|
233
|
-
scheduler.in '21m' :mutex => m do
|
234
|
-
# ...
|
235
|
-
end
|
236
|
-
|
237
|
-
It can be handy for even more fine-grained control:
|
238
|
-
|
239
|
-
m = Mutex.new
|
240
|
-
# ...
|
241
|
-
scheduler.cron '0 18 * * *', :mutex => m do
|
242
|
-
# ...
|
243
|
-
end
|
244
|
-
scheduler.in '21m' do
|
245
|
-
# non-critical
|
246
|
-
m.synchronize do
|
247
|
-
# critical
|
248
|
-
end
|
249
|
-
# non-critical
|
250
|
-
end
|
251
|
-
|
252
|
-
Please note that a mutex can also be used to prevent overlapping executions of the same job:
|
253
|
-
|
254
|
-
scheduler.every '5m', :mutex => 'the_mutex' do
|
255
|
-
puts "order ristretto"
|
256
|
-
# do something that might take more that 5 minutes...
|
257
|
-
puts "ah, that was delicious"
|
258
|
-
end
|
259
|
-
|
260
|
-
But beware the cascades...
|
261
|
-
|
262
|
-
|
263
|
-
== :mutex => ['mutex_a', 'mutex_b', ...]
|
264
|
-
|
265
|
-
Multiple mutexes can be used to ensure exlusivity:
|
266
|
-
|
267
|
-
scheduler.in '20m', :mutex => 'mutex_r' do
|
268
|
-
puts "order ristretto"
|
269
|
-
sleep 2 * 60
|
270
|
-
end
|
271
|
-
|
272
|
-
scheduler.in '20m' :mutex => 'mutex_e' do
|
273
|
-
puts "order espresso"
|
274
|
-
sleep 3 * 60
|
275
|
-
end
|
276
|
-
|
277
|
-
scheduler.in '1h' :mutex => ['mutex_r', 'mutex_e'] do
|
278
|
-
puts "code for fun"
|
279
|
-
end
|
280
|
-
|
281
|
-
This allow you order ristretto and espresso at same time, but when you coding it ensure you can't order any thing, and when you ordering anything it ensure you can't code.
|
282
|
-
|
283
|
-
Sure you can also use array of Mutex object directly:
|
284
|
-
|
285
|
-
mutex_r = Mutex.new
|
286
|
-
mutex_e = Mutex.new
|
287
|
-
# ...
|
288
|
-
scheduler.in '1h' :mutex => [mutex_r, mutex_e] do
|
289
|
-
puts "code for fun"
|
290
|
-
end
|
291
|
-
|
292
|
-
|
293
|
-
== :allow_overlapping => false
|
294
|
-
|
295
|
-
By default, every and cron jobs will "overlap":
|
296
|
-
|
297
|
-
scheduler.every '3s' do
|
298
|
-
4.times do |i|
|
299
|
-
puts "hello #{i}"
|
300
|
-
sleep 1
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
You might end up with something that looks like
|
305
|
-
|
306
|
-
hello 0
|
307
|
-
hello 1
|
308
|
-
hello 2
|
309
|
-
hello 3
|
310
|
-
hello 3
|
311
|
-
hello 4
|
312
|
-
...
|
313
|
-
|
314
|
-
This every job will have overlaps. To prevent that:
|
315
|
-
|
316
|
-
scheduler.every '3s', :allow_overlapping => false do
|
317
|
-
# ...
|
318
|
-
end
|
319
|
-
|
320
|
-
|
321
|
-
== 'every' jobs and :first_at / :first_in
|
322
|
-
|
323
|
-
This job will execute every 3 days, but first time will be in 5 days from now :
|
324
|
-
|
325
|
-
scheduler.every '3d', :first_in => '5d' do
|
326
|
-
# do something
|
327
|
-
end
|
328
|
-
|
329
|
-
This job will execute every 3 days, starting from Christmas Eve at noon :
|
330
|
-
|
331
|
-
scheduler.every '3d', :first_at => '2009/12/24 12:00' do
|
332
|
-
# do something
|
333
|
-
end
|
334
|
-
|
335
|
-
The chronic gem may help (http://chronic.rubyforge.org/) :
|
336
|
-
|
337
|
-
require 'chronic' # sudo gem install chronic
|
338
|
-
|
339
|
-
scheduler.every '3h', :first_at => Chronic.parse('this tuesday 5:00') do
|
340
|
-
# do something starting this tuesday
|
341
|
-
end
|
342
|
-
|
343
|
-
Note : setting a :first_at/:first_in in the past will get rufus-scheduler to trigger for all the past schedules until now. Adding :discard_past => true will prevent this.
|
344
|
-
|
345
|
-
|
346
|
-
== self unschedule for 'cron' and 'every' jobs
|
347
|
-
|
348
|
-
'at' and 'in' jobs fire once only. 'cron' and 'every' jobs do fire repeatedly, so it might be useful to stop them.
|
349
|
-
|
350
|
-
scheduler.every '3d' do |job|
|
351
|
-
l = determine_crop_maturity_level()
|
352
|
-
if l >= 7
|
353
|
-
puts "crop is ready."
|
354
|
-
job.unschedule
|
355
|
-
else
|
356
|
-
puts "crop not yet ready..."
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
In this example, the 'every' job will unschedule itself when the crop is ready.
|
361
|
-
|
362
|
-
|
363
|
-
== #running?
|
364
|
-
|
365
|
-
job = scheduler.every '3d' do
|
366
|
-
# ...
|
367
|
-
end
|
368
|
-
|
369
|
-
# ...
|
370
|
-
|
371
|
-
p job.running?
|
372
|
-
|
373
|
-
Job#running? will return true when the job got triggered and is actually performing.
|
374
|
-
|
375
|
-
Please note, that #running? is not related to the #paused? which is detailed in the next section.
|
376
|
-
|
377
|
-
|
378
|
-
== #pause, #resume and #paused?
|
379
|
-
|
380
|
-
Jobs, as well as the scheduler itself have a pair of #pause and #resume methods.
|
381
|
-
|
382
|
-
job = scheduler.every '2h' do
|
383
|
-
# ...
|
384
|
-
end
|
385
|
-
|
386
|
-
# ...
|
387
|
-
|
388
|
-
job.pause # the job will be scheduled but won't trigger
|
389
|
-
|
390
|
-
# ...
|
391
|
-
|
392
|
-
puts job.paused?
|
393
|
-
|
394
|
-
Pausing / resuming a job doesn't affect the scheduling of a job, it merely "silences" it, its block won't get executed. Calling resume will not reset the schedule of the job. If you schedule a job to trigger every 10 minutes at 10am and pause it from 1020 to 1025, it's next triggering time will be 1030 approximately.
|
395
|
-
|
396
|
-
One can pause an "at" or "in" job. If it's still paused at trigger time, it will simply become a dud.
|
397
|
-
|
398
|
-
As said, the scheduler has a #pause(job_or_job_id) and a #resume(job_or_job_id) pair of methods:
|
399
|
-
|
400
|
-
scheduler.pause(job)
|
401
|
-
scheduler.pause(job_id)
|
402
|
-
scheduler.resume(job)
|
403
|
-
scheduler.resume(job_id)
|
404
|
-
|
405
|
-
|
406
|
-
== Scheduler#running_jobs
|
407
|
-
|
408
|
-
One can get a list of the jobs just triggered (actually running) by doing
|
409
|
-
|
410
|
-
jobs = scheduler.running_jobs
|
411
|
-
|
412
|
-
|
413
|
-
== schedulables
|
414
|
-
|
415
|
-
Sometimes passing a block isn't that convenient :
|
416
|
-
|
417
|
-
class JobThing
|
418
|
-
def initialize(relevant_info)
|
419
|
-
@ri = relevant_info
|
420
|
-
end
|
421
|
-
def call(job)
|
422
|
-
do_something_about_it
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
# ...
|
427
|
-
|
428
|
-
scheduler.in '3d', JobThing.new('http://news.example.com/data_xyz')
|
429
|
-
scheduler.in '1w', JobThing.new('http://news.example.com/data_abc'), :timeout => '1d'
|
430
|
-
|
431
|
-
rufus-scheduler accepts anything that responds to a call method with a unique parameter (it will pass the job) as a 'schedulable'.
|
432
|
-
|
433
|
-
For compatibility with older (1.x) versions, schedulables with a trigger methods are accepted :
|
434
|
-
|
435
|
-
class JobThing
|
436
|
-
def trigger(params)
|
437
|
-
job = params[:job]
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
The 'params' correspond to the scheduler job params, and the key :job points to the rufus-scheduler job for the schedulable that is passed to a 'call schedulable'.
|
442
|
-
|
443
|
-
|
444
|
-
== looking up jobs
|
445
|
-
|
446
|
-
scheduler.jobs
|
447
|
-
# returns a map job_id => job of at/in/every jobs
|
448
|
-
|
449
|
-
scheduler.cron_jobs
|
450
|
-
# idem for cron jobs
|
451
|
-
|
452
|
-
scheduler.all_jobs
|
453
|
-
# idem but for every/at/in/cron jobs (all of them)
|
454
|
-
|
455
|
-
scheduler.find_by_tag(t)
|
456
|
-
# returns all the jobs with a given tag (passed at schedule time with :tags)
|
457
|
-
|
458
|
-
|
459
|
-
== unscheduling jobs
|
460
|
-
|
461
|
-
The 'scheduling' methods always return an instance of Rufus::Scheduler::Job. This object can be used for unscheduling :
|
462
|
-
|
463
|
-
job = scheduler.in '2d', :tags => 'admin' do
|
464
|
-
run_backlog_cleaning()
|
465
|
-
end
|
466
|
-
|
467
|
-
# later ...
|
468
|
-
|
469
|
-
job.unschedule
|
470
|
-
# or
|
471
|
-
scheduler.unschedule(job.job_id)
|
472
|
-
|
473
|
-
|
474
|
-
== tags
|
475
|
-
|
476
|
-
You can specify tags at schedule time :
|
477
|
-
|
478
|
-
scheduler.in '2d', :tags => 'admin' do
|
479
|
-
run_backlog_cleaning()
|
480
|
-
end
|
481
|
-
scheduler.every '3m', :tags => 'production' do
|
482
|
-
check_order_log()
|
483
|
-
end
|
484
|
-
|
485
|
-
And later query the scheduler for those jobs :
|
486
|
-
|
487
|
-
admin_jobs = scheduler.find_by_tag('admin')
|
488
|
-
production_jobs = scheduler.find_by_tag('production')
|
489
|
-
|
490
|
-
|
491
|
-
== timeout
|
492
|
-
|
493
|
-
One can specify a timeout for the triggering of a job.
|
494
|
-
|
495
|
-
scheduler.every '2d', :timeout => '40m' do
|
496
|
-
begin
|
497
|
-
run_backlog_cleaning()
|
498
|
-
rescue Rufus::Scheduler::TimeOutError => toe
|
499
|
-
# timeout occurred
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
This job will run every two days. If a run takes more than 40 minutes it will timeout (its thread will receive a TimeOutError).
|
504
|
-
|
505
|
-
This timeout feature relies on an 'in' job scheduled at the moment the main job gets triggered, hence the '40m' time string format.
|
506
|
-
|
507
|
-
|
508
|
-
== exceptions in jobs
|
509
|
-
|
510
|
-
By default, when exceptions occur when a job performs, the error messages will be output to the STDOUT.
|
511
|
-
|
512
|
-
It's easy to customize that behaviour :
|
513
|
-
|
514
|
-
scheduler = Rufus::Scheduler::PlainScheduler.start_new
|
515
|
-
# or
|
516
|
-
#scheduler = Rufus::Scheduler::EmScheduler.start_new
|
517
|
-
|
518
|
-
def scheduler.handle_exception(job, exception)
|
519
|
-
puts "job #{job.job_id} caught exception '#{exception}'"
|
520
|
-
end
|
521
|
-
|
522
|
-
These are OK too:
|
523
|
-
|
524
|
-
def scheduler.on_exception(job, exception)
|
525
|
-
puts "job #{job.job_id} caught exception '#{exception}'"
|
526
|
-
end
|
527
|
-
|
528
|
-
# or
|
529
|
-
|
530
|
-
def scheduler.on_exception(exception)
|
531
|
-
puts "caught exception '#{exception}'"
|
532
|
-
|
533
|
-
For backward compatibility, overriding #log_exception is still OK :
|
534
|
-
|
535
|
-
def scheduler.log_exception(exception)
|
536
|
-
puts "caught exception '#{exception}'"
|
537
|
-
end
|
538
|
-
|
539
|
-
Note that an every job or a cron job will stay scheduled even if it experiences an exception.
|
540
|
-
|
541
|
-
By default, all exceptions are rescued. It's easy to customize that behaviour :
|
542
|
-
|
543
|
-
scheduler = Rufus::Scheduler::PlainScheduler.start_new(:exception => StandardError)
|
544
|
-
# or
|
545
|
-
#scheduler = Rufus::Scheduler::EmScheduler.start_new(:exception => StandardError)
|
546
|
-
|
547
|
-
scheduler.in "3s" do
|
548
|
-
exit
|
549
|
-
end
|
550
|
-
|
551
|
-
== frequency
|
552
|
-
|
553
|
-
The default frequency for the scheduler is 0.330 seconds. This means that the usual scheduler implementation will wake up, trigger jobs that are to be triggered and then go back to sleep for 0.330 seconds. Note that this doesn't mean that the scheduler will wake up very 0.330 seconds (checking and triggering do take time).
|
554
|
-
|
555
|
-
You can set a different frequency when starting / initializing the scheduler :
|
556
|
-
|
557
|
-
require 'rubygems'
|
558
|
-
require 'rufus/scheduler'
|
559
|
-
|
560
|
-
scheduler = Rufus::Scheduler.start_new(:frequency => 60.0)
|
561
|
-
# for a lazy scheduler that only wakes up every 60 seconds
|
562
|
-
|
563
|
-
|
564
|
-
== usage with EventMachine
|
565
|
-
|
566
|
-
rufus-scheduler 2.0 can be used in conjunction with EventMachine (http://github.com/eventmachine/eventmachine/).
|
567
|
-
|
568
|
-
More and more ruby applications are using EventMachine. This flavour of the scheduler relies on EventMachine, thus it doesn't require a separate thread like the PlainScheduler does.
|
569
|
-
|
570
|
-
require 'rubygems'
|
571
|
-
require 'eventmachine'
|
572
|
-
|
573
|
-
EM.run {
|
574
|
-
|
575
|
-
scheduler = Rufus::Scheduler::EmScheduler.start_new
|
576
|
-
|
577
|
-
scheduler.in '20m' do
|
578
|
-
puts "order ristretto"
|
579
|
-
end
|
580
|
-
}
|
581
|
-
|
582
|
-
|
583
|
-
== with Passenger
|
584
|
-
|
585
|
-
"it terminates for no apparent reason!"
|
586
|
-
|
587
|
-
https://github.com/jmettraux/rufus-scheduler/issues/issue/10
|
588
|
-
|
589
|
-
|
590
|
-
== tested with
|
591
|
-
|
592
|
-
* 1.8.7-p249
|
593
|
-
* 1.9.2-p290
|
594
|
-
* jruby-1.5.1
|
595
|
-
|
596
|
-
on Mac OS X (Snow Leopard).
|
597
|
-
|
598
|
-
|
599
|
-
== dependencies
|
600
|
-
|
601
|
-
The 'tzinfo' rubygem.
|
602
|
-
|
603
|
-
The ruby gem 'eventmachine' if you use Rufus::Scheduler::EmScheduler, else no other dependencies.
|
604
|
-
|
605
|
-
|
606
|
-
== support
|
607
|
-
|
608
|
-
If you identify and pinpoint a bug, please use the issue tracker. If you are unsure whether the fault lies in rufus-scheduler or in your software, use the mailing list. The mailing list is Google-powered, so, yes, you can search it.
|
609
|
-
|
610
|
-
Please read carefully: http://www.chiark.greenend.org.uk/~sgtatham/bugs.html (then re-read it).
|
611
|
-
|
612
|
-
|
613
|
-
=== mailing list
|
614
|
-
|
615
|
-
On the rufus-ruby list :
|
616
|
-
|
617
|
-
http://groups.google.com/group/rufus-ruby
|
618
|
-
|
619
|
-
Newcomers' first message is held for moderation in order to prevent spam. Further messages are not held.
|
620
|
-
|
621
|
-
|
622
|
-
=== issue tracker
|
623
|
-
|
624
|
-
https://github.com/jmettraux/rufus-scheduler/issues
|
625
|
-
|
626
|
-
|
627
|
-
=== irc
|
628
|
-
|
629
|
-
If you come over to #ruote to ask for rufus-scheduler help, please make sure to 1) say hello 2) be polite 3) state that you're looking for rufus-scheduler help 4) remember that we cannot read your mind and guess whatever lies in your deployment.
|
630
|
-
|
631
|
-
irc.freenode.net #ruote
|
632
|
-
|
633
|
-
If there is no answer on IRC, use the mailing list.
|
634
|
-
|
635
|
-
|
636
|
-
== source
|
637
|
-
|
638
|
-
http://github.com/jmettraux/rufus-scheduler
|
639
|
-
|
640
|
-
git clone git://github.com/jmettraux/rufus-scheduler.git
|
641
|
-
|
642
|
-
|
643
|
-
== credits
|
644
|
-
|
645
|
-
http://github.com/jmettraux/rufus-scheduler/blob/master/CREDITS.txt
|
646
|
-
|
647
|
-
|
648
|
-
== authors
|
649
|
-
|
650
|
-
John Mettraux, jmettraux@gmail.com, http://jmettraux.github.com
|
651
|
-
|
652
|
-
|
653
|
-
== the rest of Rufus
|
654
|
-
|
655
|
-
http://rufus.rubyforge.org
|
656
|
-
|
657
|
-
|
658
|
-
== license
|
659
|
-
|
660
|
-
MIT
|
661
|
-
|