rufus-scheduler 3.3.4 → 3.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +24 -0
- data/CREDITS.txt +7 -0
- data/Makefile +4 -0
- data/README.md +8 -7
- data/lib/rufus/scheduler/job_array.rb +0 -24
- data/lib/rufus/scheduler/jobs.rb +25 -40
- data/lib/rufus/scheduler/locks.rb +0 -23
- data/lib/rufus/scheduler/util.rb +151 -240
- data/lib/rufus/scheduler.rb +52 -46
- data/rufus-scheduler.gemspec +9 -5
- metadata +23 -24
- data/TODO.txt +0 -151
- data/fail.txt +0 -2
- data/fail18.txt +0 -12
- data/lib/rufus/scheduler/cronline.rb +0 -523
- data/lib/rufus/scheduler/zotime.rb +0 -488
- data/n.txt +0 -38
- data/pics.txt +0 -15
- data/sofia.md +0 -89
data/lib/rufus/scheduler/util.rb
CHANGED
|
@@ -1,281 +1,192 @@
|
|
|
1
|
-
#--
|
|
2
|
-
# Copyright (c) 2006-2017, 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
1
|
|
|
26
2
|
module Rufus
|
|
27
3
|
|
|
28
4
|
class Scheduler
|
|
29
5
|
|
|
30
|
-
|
|
31
|
-
# time and string methods
|
|
32
|
-
#++
|
|
33
|
-
|
|
34
|
-
def self.parse(o, opts={})
|
|
35
|
-
|
|
36
|
-
opts[:no_error] = true
|
|
37
|
-
|
|
38
|
-
parse_cron(o, opts) ||
|
|
39
|
-
parse_in(o, opts) || # covers 'every' schedule strings
|
|
40
|
-
parse_at(o, opts) ||
|
|
41
|
-
fail(ArgumentError.new("couldn't parse #{o.inspect} (#{o.class})"))
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.parse_cron(o, opts)
|
|
45
|
-
|
|
46
|
-
CronLine.new(o)
|
|
47
|
-
|
|
48
|
-
rescue ArgumentError => ae
|
|
49
|
-
|
|
50
|
-
return nil if opts[:no_error]
|
|
51
|
-
fail ae
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def self.parse_in(o, opts={})
|
|
55
|
-
|
|
56
|
-
#o.is_a?(String) ? parse_duration(o, opts) : o
|
|
57
|
-
|
|
58
|
-
return parse_duration(o, opts) if o.is_a?(String)
|
|
59
|
-
return o if o.is_a?(Numeric)
|
|
6
|
+
class << self
|
|
60
7
|
|
|
61
|
-
|
|
8
|
+
#--
|
|
9
|
+
# time and string methods
|
|
10
|
+
#++
|
|
62
11
|
|
|
63
|
-
|
|
12
|
+
def parse(o, opts={})
|
|
64
13
|
|
|
65
|
-
|
|
66
|
-
fail ae
|
|
67
|
-
end
|
|
14
|
+
opts[:no_error] = true
|
|
68
15
|
|
|
69
|
-
|
|
16
|
+
parse_cron(o, opts) ||
|
|
17
|
+
parse_in(o, opts) || # covers 'every' schedule strings
|
|
18
|
+
parse_at(o, opts) ||
|
|
19
|
+
fail(ArgumentError.new("couldn't parse #{o.inspect} (#{o.class})"))
|
|
20
|
+
end
|
|
70
21
|
|
|
71
|
-
|
|
72
|
-
return Rufus::Scheduler::ZoTime.make(o) if o.is_a?(Time)
|
|
73
|
-
Rufus::Scheduler::ZoTime.parse(o, opts)
|
|
22
|
+
def parse_cron(o, opts)
|
|
74
23
|
|
|
75
|
-
|
|
24
|
+
Fugit.parse_cron(o)
|
|
25
|
+
end
|
|
76
26
|
|
|
77
|
-
|
|
78
|
-
fail se
|
|
79
|
-
end
|
|
27
|
+
def parse_in(o, opts={})
|
|
80
28
|
|
|
81
|
-
|
|
82
|
-
[ 'y', 365 * 24 * 3600 ],
|
|
83
|
-
[ 'M', 30 * 24 * 3600 ],
|
|
84
|
-
[ 'w', 7 * 24 * 3600 ],
|
|
85
|
-
[ 'd', 24 * 3600 ],
|
|
86
|
-
[ 'h', 3600 ],
|
|
87
|
-
[ 'm', 60 ],
|
|
88
|
-
[ 's', 1 ]
|
|
89
|
-
]
|
|
90
|
-
DURATIONS2 = DURATIONS2M.dup
|
|
91
|
-
DURATIONS2.delete_at(1)
|
|
92
|
-
|
|
93
|
-
DURATIONS = DURATIONS2M.inject({}) { |r, (k, v)| r[k] = v; r }
|
|
94
|
-
DURATION_LETTERS = DURATIONS.keys.join
|
|
95
|
-
|
|
96
|
-
DU_KEYS = DURATIONS2M.collect { |k, v| k.to_sym }
|
|
97
|
-
|
|
98
|
-
# Turns a string like '1m10s' into a float like '70.0', more formally,
|
|
99
|
-
# turns a time duration expressed as a string into a Float instance
|
|
100
|
-
# (millisecond count).
|
|
101
|
-
#
|
|
102
|
-
# w -> week
|
|
103
|
-
# d -> day
|
|
104
|
-
# h -> hour
|
|
105
|
-
# m -> minute
|
|
106
|
-
# s -> second
|
|
107
|
-
# M -> month
|
|
108
|
-
# y -> year
|
|
109
|
-
# 'nada' -> millisecond
|
|
110
|
-
#
|
|
111
|
-
# Some examples:
|
|
112
|
-
#
|
|
113
|
-
# Rufus::Scheduler.parse_duration "0.5" # => 0.5
|
|
114
|
-
# Rufus::Scheduler.parse_duration "500" # => 0.5
|
|
115
|
-
# Rufus::Scheduler.parse_duration "1000" # => 1.0
|
|
116
|
-
# Rufus::Scheduler.parse_duration "1h" # => 3600.0
|
|
117
|
-
# Rufus::Scheduler.parse_duration "1h10s" # => 3610.0
|
|
118
|
-
# Rufus::Scheduler.parse_duration "1w2d" # => 777600.0
|
|
119
|
-
#
|
|
120
|
-
# Negative time strings are OK (Thanks Danny Fullerton):
|
|
121
|
-
#
|
|
122
|
-
# Rufus::Scheduler.parse_duration "-0.5" # => -0.5
|
|
123
|
-
# Rufus::Scheduler.parse_duration "-1h" # => -3600.0
|
|
124
|
-
#
|
|
125
|
-
def self.parse_duration(string, opts={})
|
|
29
|
+
#o.is_a?(String) ? parse_duration(o, opts) : o
|
|
126
30
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
s = s[1..-1] if mod == -1
|
|
31
|
+
return parse_duration(o, opts) if o.is_a?(String)
|
|
32
|
+
return o if o.is_a?(Numeric)
|
|
130
33
|
|
|
131
|
-
|
|
132
|
-
r = 0.0
|
|
34
|
+
fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
|
|
133
35
|
|
|
134
|
-
|
|
135
|
-
ss += "#{f}#{d}"
|
|
136
|
-
r += f.to_f * (DURATIONS[d] || 1.0)
|
|
137
|
-
end
|
|
36
|
+
rescue ArgumentError => ae
|
|
138
37
|
|
|
139
|
-
if ss == '-' || ss != string.to_s.strip
|
|
140
38
|
return nil if opts[:no_error]
|
|
141
|
-
fail
|
|
39
|
+
fail ae
|
|
142
40
|
end
|
|
143
41
|
|
|
144
|
-
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
class << self
|
|
148
|
-
#-
|
|
149
|
-
# for compatibility with rufus-scheduler 2.x
|
|
150
|
-
#+
|
|
151
|
-
alias parse_duration_string parse_duration
|
|
152
|
-
alias parse_time_string parse_duration
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
# Turns a number of seconds into a a time string
|
|
157
|
-
#
|
|
158
|
-
# Rufus.to_duration 0 # => '0s'
|
|
159
|
-
# Rufus.to_duration 60 # => '1m'
|
|
160
|
-
# Rufus.to_duration 3661 # => '1h1m1s'
|
|
161
|
-
# Rufus.to_duration 7 * 24 * 3600 # => '1w'
|
|
162
|
-
# Rufus.to_duration 30 * 24 * 3600 + 1 # => "4w2d1s"
|
|
163
|
-
#
|
|
164
|
-
# It goes from seconds to the year. Months are not counted (as they
|
|
165
|
-
# are of variable length). Weeks are counted.
|
|
166
|
-
#
|
|
167
|
-
# For 30 days months to be counted, the second parameter of this
|
|
168
|
-
# method can be set to true.
|
|
169
|
-
#
|
|
170
|
-
# Rufus.to_duration 30 * 24 * 3600 + 1, true # => "1M1s"
|
|
171
|
-
#
|
|
172
|
-
# If a Float value is passed, milliseconds will be displayed without
|
|
173
|
-
# 'marker'
|
|
174
|
-
#
|
|
175
|
-
# Rufus.to_duration 0.051 # => "51"
|
|
176
|
-
# Rufus.to_duration 7.051 # => "7s51"
|
|
177
|
-
# Rufus.to_duration 0.120 + 30 * 24 * 3600 + 1 # => "4w2d1s120"
|
|
178
|
-
#
|
|
179
|
-
# (this behaviour mirrors the one found for parse_time_string()).
|
|
180
|
-
#
|
|
181
|
-
# Options are :
|
|
182
|
-
#
|
|
183
|
-
# * :months, if set to true, months (M) of 30 days will be taken into
|
|
184
|
-
# account when building up the result
|
|
185
|
-
# * :drop_seconds, if set to true, seconds and milliseconds will be trimmed
|
|
186
|
-
# from the result
|
|
187
|
-
#
|
|
188
|
-
def self.to_duration(seconds, options={})
|
|
189
|
-
|
|
190
|
-
h = to_duration_hash(seconds, options)
|
|
191
|
-
|
|
192
|
-
return (options[:drop_seconds] ? '0m' : '0s') if h.empty?
|
|
193
|
-
|
|
194
|
-
s =
|
|
195
|
-
DU_KEYS.inject('') { |r, key|
|
|
196
|
-
count = h[key]
|
|
197
|
-
count = nil if count == 0
|
|
198
|
-
r << "#{count}#{key}" if count
|
|
199
|
-
r
|
|
200
|
-
}
|
|
42
|
+
def parse_at(o, opts={})
|
|
201
43
|
|
|
202
|
-
|
|
203
|
-
|
|
44
|
+
return o if o.is_a?(EoTime)
|
|
45
|
+
return EoTime.make(o) if o.is_a?(Time)
|
|
46
|
+
EoTime.parse(o, opts)
|
|
204
47
|
|
|
205
|
-
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
class << self
|
|
209
|
-
#-
|
|
210
|
-
# for compatibility with rufus-scheduler 2.x
|
|
211
|
-
#+
|
|
212
|
-
alias to_duration_string to_duration
|
|
213
|
-
alias to_time_string to_duration
|
|
214
|
-
end
|
|
48
|
+
rescue StandardError => se
|
|
215
49
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
# # => { :ms => "51" }
|
|
220
|
-
# Rufus.to_duration_hash 7.051
|
|
221
|
-
# # => { :s => 7, :ms => "51" }
|
|
222
|
-
# Rufus.to_duration_hash 0.120 + 30 * 24 * 3600 + 1
|
|
223
|
-
# # => { :w => 4, :d => 2, :s => 1, :ms => "120" }
|
|
224
|
-
#
|
|
225
|
-
# This method is used by to_duration behind the scenes.
|
|
226
|
-
#
|
|
227
|
-
# Options are :
|
|
228
|
-
#
|
|
229
|
-
# * :months, if set to true, months (M) of 30 days will be taken into
|
|
230
|
-
# account when building up the result
|
|
231
|
-
# * :drop_seconds, if set to true, seconds and milliseconds will be trimmed
|
|
232
|
-
# from the result
|
|
233
|
-
#
|
|
234
|
-
def self.to_duration_hash(seconds, options={})
|
|
50
|
+
return nil if opts[:no_error]
|
|
51
|
+
fail se
|
|
52
|
+
end
|
|
235
53
|
|
|
236
|
-
|
|
54
|
+
# Turns a string like '1m10s' into a float like '70.0', more formally,
|
|
55
|
+
# turns a time duration expressed as a string into a Float instance
|
|
56
|
+
# (millisecond count).
|
|
57
|
+
#
|
|
58
|
+
# w -> week
|
|
59
|
+
# d -> day
|
|
60
|
+
# h -> hour
|
|
61
|
+
# m -> minute
|
|
62
|
+
# s -> second
|
|
63
|
+
# M -> month
|
|
64
|
+
# y -> year
|
|
65
|
+
# 'nada' -> millisecond
|
|
66
|
+
#
|
|
67
|
+
# Some examples:
|
|
68
|
+
#
|
|
69
|
+
# Rufus::Scheduler.parse_duration "0.5" # => 0.5
|
|
70
|
+
# Rufus::Scheduler.parse_duration "500" # => 0.5
|
|
71
|
+
# Rufus::Scheduler.parse_duration "1000" # => 1.0
|
|
72
|
+
# Rufus::Scheduler.parse_duration "1h" # => 3600.0
|
|
73
|
+
# Rufus::Scheduler.parse_duration "1h10s" # => 3610.0
|
|
74
|
+
# Rufus::Scheduler.parse_duration "1w2d" # => 777600.0
|
|
75
|
+
#
|
|
76
|
+
# Negative time strings are OK (Thanks Danny Fullerton):
|
|
77
|
+
#
|
|
78
|
+
# Rufus::Scheduler.parse_duration "-0.5" # => -0.5
|
|
79
|
+
# Rufus::Scheduler.parse_duration "-1h" # => -3600.0
|
|
80
|
+
#
|
|
81
|
+
def parse_duration(str, opts={})
|
|
82
|
+
|
|
83
|
+
d =
|
|
84
|
+
opts[:no_error] ?
|
|
85
|
+
Fugit::Duration.parse(str, opts) :
|
|
86
|
+
Fugit::Duration.do_parse(str, opts)
|
|
87
|
+
d ?
|
|
88
|
+
d.to_sec :
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
237
91
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
92
|
+
# Turns a number of seconds into a a time string
|
|
93
|
+
#
|
|
94
|
+
# Rufus.to_duration 0 # => '0s'
|
|
95
|
+
# Rufus.to_duration 60 # => '1m'
|
|
96
|
+
# Rufus.to_duration 3661 # => '1h1m1s'
|
|
97
|
+
# Rufus.to_duration 7 * 24 * 3600 # => '1w'
|
|
98
|
+
# Rufus.to_duration 30 * 24 * 3600 + 1 # => "4w2d1s"
|
|
99
|
+
#
|
|
100
|
+
# It goes from seconds to the year. Months are not counted (as they
|
|
101
|
+
# are of variable length). Weeks are counted.
|
|
102
|
+
#
|
|
103
|
+
# For 30 days months to be counted, the second parameter of this
|
|
104
|
+
# method can be set to true.
|
|
105
|
+
#
|
|
106
|
+
# Rufus.to_duration 30 * 24 * 3600 + 1, true # => "1M1s"
|
|
107
|
+
#
|
|
108
|
+
# If a Float value is passed, milliseconds will be displayed without
|
|
109
|
+
# 'marker'
|
|
110
|
+
#
|
|
111
|
+
# Rufus.to_duration 0.051 # => "51"
|
|
112
|
+
# Rufus.to_duration 7.051 # => "7s51"
|
|
113
|
+
# Rufus.to_duration 0.120 + 30 * 24 * 3600 + 1 # => "4w2d1s120"
|
|
114
|
+
#
|
|
115
|
+
# (this behaviour mirrors the one found for parse_time_string()).
|
|
116
|
+
#
|
|
117
|
+
# Options are :
|
|
118
|
+
#
|
|
119
|
+
# * :months, if set to true, months (M) of 30 days will be taken into
|
|
120
|
+
# account when building up the result
|
|
121
|
+
# * :drop_seconds, if set to true, seconds and milliseconds will be
|
|
122
|
+
# trimmed from the result
|
|
123
|
+
#
|
|
124
|
+
def to_duration(seconds, options={})
|
|
125
|
+
|
|
126
|
+
#d = Fugit::Duration.parse(seconds, options).deflate
|
|
127
|
+
#d = d.drop_seconds if options[:drop_seconds]
|
|
128
|
+
#d = d.deflate(:month => options[:months]) if options[:months]
|
|
129
|
+
#d.to_rufus_s
|
|
130
|
+
|
|
131
|
+
to_fugit_duration(seconds, options).to_rufus_s
|
|
241
132
|
end
|
|
242
133
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
134
|
+
# Turns a number of seconds (integer or Float) into a hash like in :
|
|
135
|
+
#
|
|
136
|
+
# Rufus.to_duration_hash 0.051
|
|
137
|
+
# # => { :s => 0.051 }
|
|
138
|
+
# Rufus.to_duration_hash 7.051
|
|
139
|
+
# # => { :s => 7.051 }
|
|
140
|
+
# Rufus.to_duration_hash 0.120 + 30 * 24 * 3600 + 1
|
|
141
|
+
# # => { :w => 4, :d => 2, :s => 1.120 }
|
|
142
|
+
#
|
|
143
|
+
# This method is used by to_duration behind the scenes.
|
|
144
|
+
#
|
|
145
|
+
# Options are :
|
|
146
|
+
#
|
|
147
|
+
# * :months, if set to true, months (M) of 30 days will be taken into
|
|
148
|
+
# account when building up the result
|
|
149
|
+
# * :drop_seconds, if set to true, seconds and milliseconds will be
|
|
150
|
+
# trimmed from the result
|
|
151
|
+
#
|
|
152
|
+
def to_duration_hash(seconds, options={})
|
|
153
|
+
|
|
154
|
+
to_fugit_duration(seconds, options).to_rufus_h
|
|
246
155
|
end
|
|
247
156
|
|
|
248
|
-
|
|
157
|
+
# Used by both .to_duration and .to_duration_hash
|
|
158
|
+
#
|
|
159
|
+
def to_fugit_duration(seconds, options={})
|
|
249
160
|
|
|
250
|
-
|
|
161
|
+
d = Fugit::Duration
|
|
162
|
+
.parse(seconds, options)
|
|
163
|
+
.deflate
|
|
251
164
|
|
|
252
|
-
|
|
253
|
-
|
|
165
|
+
d = d.drop_seconds if options[:drop_seconds]
|
|
166
|
+
d = d.deflate(:month => options[:months]) if options[:months]
|
|
254
167
|
|
|
255
|
-
|
|
168
|
+
d
|
|
256
169
|
end
|
|
257
170
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
#--
|
|
262
|
-
# misc
|
|
263
|
-
#++
|
|
171
|
+
#--
|
|
172
|
+
# misc
|
|
173
|
+
#++
|
|
264
174
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
175
|
+
# Produces the UTC string representation of a Time instance
|
|
176
|
+
#
|
|
177
|
+
# like "2009/11/23 11:11:50.947109 UTC"
|
|
178
|
+
#
|
|
179
|
+
def utc_to_s(t=Time.now)
|
|
270
180
|
|
|
271
|
-
|
|
272
|
-
|
|
181
|
+
"#{t.utc.strftime('%Y-%m-%d %H:%M:%S')}.#{sprintf('%06d', t.usec)} UTC"
|
|
182
|
+
end
|
|
273
183
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
184
|
+
# Produces a hour/min/sec/milli string representation of Time instance
|
|
185
|
+
#
|
|
186
|
+
def h_to_s(t=Time.now)
|
|
277
187
|
|
|
278
|
-
|
|
188
|
+
"#{t.strftime('%H:%M:%S')}.#{sprintf('%06d', t.usec)}"
|
|
189
|
+
end
|
|
279
190
|
end
|
|
280
191
|
|
|
281
192
|
# Debugging tools...
|
data/lib/rufus/scheduler.rb
CHANGED
|
@@ -1,46 +1,25 @@
|
|
|
1
|
-
#--
|
|
2
|
-
# Copyright (c) 2006-2017, 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
1
|
|
|
2
|
+
require 'set'
|
|
25
3
|
require 'date' if RUBY_VERSION < '1.9.0'
|
|
26
4
|
require 'time'
|
|
27
5
|
require 'thread'
|
|
28
|
-
|
|
6
|
+
|
|
7
|
+
require 'fugit'
|
|
29
8
|
|
|
30
9
|
|
|
31
10
|
module Rufus
|
|
32
11
|
|
|
33
12
|
class Scheduler
|
|
34
13
|
|
|
14
|
+
VERSION = '3.5.0'
|
|
15
|
+
|
|
16
|
+
EoTime = ::EtOrbi::EoTime
|
|
17
|
+
|
|
35
18
|
require 'rufus/scheduler/util'
|
|
36
|
-
require 'rufus/scheduler/zotime'
|
|
37
19
|
require 'rufus/scheduler/jobs'
|
|
38
|
-
require 'rufus/scheduler/cronline'
|
|
39
20
|
require 'rufus/scheduler/job_array'
|
|
40
21
|
require 'rufus/scheduler/locks'
|
|
41
22
|
|
|
42
|
-
VERSION = '3.3.4'
|
|
43
|
-
|
|
44
23
|
#
|
|
45
24
|
# A common error class for rufus-scheduler
|
|
46
25
|
#
|
|
@@ -104,7 +83,7 @@ module Rufus
|
|
|
104
83
|
@trigger_lock = opts[:trigger_lock] || Rufus::Scheduler::NullLock.new
|
|
105
84
|
|
|
106
85
|
# If we can't grab the @scheduler_lock, don't run.
|
|
107
|
-
|
|
86
|
+
lock || return
|
|
108
87
|
|
|
109
88
|
start
|
|
110
89
|
end
|
|
@@ -154,12 +133,12 @@ module Rufus
|
|
|
154
133
|
|
|
155
134
|
def uptime
|
|
156
135
|
|
|
157
|
-
@started_at ?
|
|
136
|
+
@started_at ? EoTime.now - @started_at : nil
|
|
158
137
|
end
|
|
159
138
|
|
|
160
139
|
def uptime_s
|
|
161
140
|
|
|
162
|
-
self.class.to_duration(uptime)
|
|
141
|
+
uptime ? self.class.to_duration(uptime) : ''
|
|
163
142
|
end
|
|
164
143
|
|
|
165
144
|
def join
|
|
@@ -258,9 +237,9 @@ module Rufus
|
|
|
258
237
|
opts[:_t] = Scheduler.parse(arg, opts)
|
|
259
238
|
|
|
260
239
|
case opts[:_t]
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
240
|
+
when ::Fugit::Cron then schedule_cron(arg, callable, opts, &block)
|
|
241
|
+
when ::EtOrbi::EoTime, Time then schedule_at(arg, callable, opts, &block)
|
|
242
|
+
else schedule_in(arg, callable, opts, &block)
|
|
264
243
|
end
|
|
265
244
|
end
|
|
266
245
|
|
|
@@ -272,8 +251,8 @@ module Rufus
|
|
|
272
251
|
opts[:_t] = Scheduler.parse(arg, opts)
|
|
273
252
|
|
|
274
253
|
case opts[:_t]
|
|
275
|
-
|
|
276
|
-
|
|
254
|
+
when ::Fugit::Cron then schedule_cron(arg, callable, opts, &block)
|
|
255
|
+
else schedule_every(arg, callable, opts, &block)
|
|
277
256
|
end
|
|
278
257
|
end
|
|
279
258
|
|
|
@@ -409,7 +388,7 @@ module Rufus
|
|
|
409
388
|
# Accepts a query option, which can be set to:
|
|
410
389
|
# * :all (default), returns all the threads that are work threads
|
|
411
390
|
# or are currently running a job
|
|
412
|
-
# * :active, returns all threads that are
|
|
391
|
+
# * :active, returns all threads that are currently running a job
|
|
413
392
|
# * :vacant, returns the threads that are not running a job
|
|
414
393
|
#
|
|
415
394
|
# If, thanks to :blocking => true, a job is scheduled to monopolize the
|
|
@@ -478,7 +457,9 @@ module Rufus
|
|
|
478
457
|
stderr.puts(" #{pre} tz:")
|
|
479
458
|
stderr.puts(" #{pre} ENV['TZ']: #{ENV['TZ']}")
|
|
480
459
|
stderr.puts(" #{pre} Time.now: #{Time.now}")
|
|
481
|
-
stderr.puts(" #{pre} local_tzone: #{
|
|
460
|
+
stderr.puts(" #{pre} local_tzone: #{EoTime.local_tzone.inspect}")
|
|
461
|
+
stderr.puts(" #{pre} et-orbi:")
|
|
462
|
+
stderr.puts(" #{pre} #{EoTime.platform_info}")
|
|
482
463
|
stderr.puts(" #{pre} scheduler:")
|
|
483
464
|
stderr.puts(" #{pre} object_id: #{object_id}")
|
|
484
465
|
stderr.puts(" #{pre} opts:")
|
|
@@ -558,7 +539,7 @@ module Rufus
|
|
|
558
539
|
|
|
559
540
|
def start
|
|
560
541
|
|
|
561
|
-
@started_at =
|
|
542
|
+
@started_at = EoTime.now
|
|
562
543
|
|
|
563
544
|
@thread =
|
|
564
545
|
Thread.new do
|
|
@@ -585,7 +566,7 @@ module Rufus
|
|
|
585
566
|
|
|
586
567
|
def trigger_jobs
|
|
587
568
|
|
|
588
|
-
now =
|
|
569
|
+
now = EoTime.now
|
|
589
570
|
|
|
590
571
|
@jobs.each(now) do |job|
|
|
591
572
|
|
|
@@ -604,9 +585,9 @@ module Rufus
|
|
|
604
585
|
next unless job && to && ts
|
|
605
586
|
# thread might just have become inactive (job -> nil)
|
|
606
587
|
|
|
607
|
-
to = ts + to unless to.is_a?(
|
|
588
|
+
to = ts + to unless to.is_a?(EoTime)
|
|
608
589
|
|
|
609
|
-
next if to >
|
|
590
|
+
next if to > EoTime.now
|
|
610
591
|
|
|
611
592
|
t.raise(Rufus::Scheduler::TimeoutError)
|
|
612
593
|
end
|
|
@@ -638,10 +619,35 @@ module Rufus
|
|
|
638
619
|
|
|
639
620
|
job = job_class.new(self, t, opts, block || callable)
|
|
640
621
|
|
|
641
|
-
fail ArgumentError.new(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
) if job.respond_to?(:frequency) && job.frequency < @frequency
|
|
622
|
+
#fail ArgumentError.new(
|
|
623
|
+
# "job frequency (#{job.frequency}) is higher than " +
|
|
624
|
+
# "scheduler frequency (#{@frequency})"
|
|
625
|
+
#) if job.respond_to?(:frequency) && job.frequency < @frequency
|
|
626
|
+
#
|
|
627
|
+
# This was expensive
|
|
628
|
+
|
|
629
|
+
if (
|
|
630
|
+
! job.is_a?(Rufus::Scheduler::IntervalJob) &&
|
|
631
|
+
job.methods.include?(:next_time_from)
|
|
632
|
+
) then
|
|
633
|
+
|
|
634
|
+
nts = (1..365)
|
|
635
|
+
.inject([ job.send(:next_time_from, EtOrbi.now) ]) { |a, i|
|
|
636
|
+
a << job.send(:next_time_from, a.last); a }
|
|
637
|
+
deltas = []; prev = nts.shift
|
|
638
|
+
while (nt = nts.shift); deltas << (nt - prev).to_f; prev = nt; end
|
|
639
|
+
|
|
640
|
+
deltas = deltas[1..-1] \
|
|
641
|
+
if opts.keys.find { |k| k.to_s.match(/\Afirst/) }
|
|
642
|
+
#
|
|
643
|
+
# do not consider the first delta if there is a first, first_at,
|
|
644
|
+
# or first_in involved
|
|
645
|
+
|
|
646
|
+
fail ArgumentError.new(
|
|
647
|
+
"job frequency (~max #{deltas.min}s) is higher than " +
|
|
648
|
+
"scheduler frequency (#{@frequency})"
|
|
649
|
+
) if deltas.min < @frequency * 0.9
|
|
650
|
+
end
|
|
645
651
|
|
|
646
652
|
@jobs.push(job)
|
|
647
653
|
|
data/rufus-scheduler.gemspec
CHANGED
|
@@ -16,20 +16,24 @@ Gem::Specification.new do |s|
|
|
|
16
16
|
s.summary = 'job scheduler for Ruby (at, cron, in and every jobs)'
|
|
17
17
|
|
|
18
18
|
s.description = %{
|
|
19
|
-
|
|
19
|
+
Job scheduler for Ruby (at, cron, in and every jobs). Not a replacement for crond.
|
|
20
20
|
}.strip
|
|
21
21
|
|
|
22
22
|
#s.files = `git ls-files`.split("\n")
|
|
23
23
|
s.files = Dir[
|
|
24
|
+
'README.{md,txt}',
|
|
25
|
+
'CHANGELOG.{md,txt}', 'CREDITS.{md,txt}', 'LICENSE.{md,txt}',
|
|
24
26
|
'Makefile',
|
|
25
27
|
'lib/**/*.rb', #'spec/**/*.rb', 'test/**/*.rb',
|
|
26
|
-
|
|
28
|
+
"#{s.name}.gemspec",
|
|
27
29
|
]
|
|
28
30
|
|
|
29
|
-
s.
|
|
31
|
+
s.required_ruby_version = '>= 1.9'
|
|
30
32
|
|
|
31
|
-
s.
|
|
32
|
-
|
|
33
|
+
s.add_runtime_dependency 'fugit', '~> 1.1', '>= 1.1.1'
|
|
34
|
+
|
|
35
|
+
s.add_development_dependency 'rspec', '~> 3.7'
|
|
36
|
+
s.add_development_dependency 'chronic', '~> 0.10'
|
|
33
37
|
|
|
34
38
|
s.require_path = 'lib'
|
|
35
39
|
end
|