rufus-scheduler 3.5.2 → 3.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,335 @@
1
+
2
+ class Rufus::Scheduler::RepeatJob < Rufus::Scheduler::Job
3
+
4
+ attr_reader :paused_at
5
+
6
+ attr_reader :first_at
7
+ attr_reader :last_at
8
+ attr_accessor :times
9
+
10
+ def initialize(scheduler, duration, opts, block)
11
+
12
+ super
13
+
14
+ @paused_at = nil
15
+
16
+ @times = opts[:times]
17
+
18
+ fail ArgumentError.new(
19
+ "cannot accept :times => #{@times.inspect}, not nil or an int"
20
+ ) unless @times == nil || @times.is_a?(Integer)
21
+
22
+ self.first_at =
23
+ opts[:first] || opts[:first_time] ||
24
+ opts[:first_at] || opts[:first_in] ||
25
+ nil
26
+ self.last_at =
27
+ opts[:last] || opts[:last_at] || opts[:last_in]
28
+
29
+ @resume_discard_past = nil
30
+ end
31
+
32
+ FIRSTS = [ :now, :immediately, 0 ].freeze
33
+
34
+ def first_at=(first)
35
+
36
+ return (@first_at = nil) if first == nil
37
+
38
+ n0 = EoTime.now
39
+ n1 = n0 + 0.003
40
+
41
+ first = n0 if FIRSTS.include?(first)
42
+ fdur = Rufus::Scheduler.parse_duration(first, no_error: true)
43
+
44
+ @first_at = (fdur && (EoTime.now + fdur)) || EoTime.make(first)
45
+ @first_at = n1 if @first_at >= n0 && @first_at < n1
46
+
47
+ fail ArgumentError.new(
48
+ "cannot set first[_at|_in] in the past: " +
49
+ "#{first.inspect} -> #{@first_at.inspect}"
50
+ ) if @first_at < n0
51
+
52
+ @first_at
53
+ end
54
+
55
+ def last_at=(last)
56
+
57
+ @last_at =
58
+ if last
59
+ ldur = Rufus::Scheduler.parse_duration(last, no_error: true)
60
+ (ldur && (EoTime.now + ldur)) || EoTime.make(last)
61
+ else
62
+ nil
63
+ end
64
+
65
+ fail ArgumentError.new(
66
+ "cannot set last[_at|_in] in the past: " +
67
+ "#{last.inspect} -> #{@last_at.inspect}"
68
+ ) if last && @last_at < EoTime.now
69
+
70
+ @last_at
71
+ end
72
+
73
+ def trigger(time)
74
+
75
+ return if @paused_at
76
+ #return set_next_time(time) if @paused_at
77
+
78
+ return (@next_time = nil) if @times && @times < 1
79
+ return (@next_time = nil) if @last_at && time >= @last_at
80
+ #
81
+ # It keeps jobs one step too much in @jobs, but it's OK
82
+
83
+ super
84
+
85
+ @times -= 1 if @times
86
+ end
87
+
88
+ def pause
89
+
90
+ @paused_at = EoTime.now
91
+ end
92
+
93
+ def resume(opts={})
94
+
95
+ @resume_discard_past = opts[:discard_past]
96
+ #p [ :@resume_discard_past, @resume_discard_past ]
97
+ @paused_at = nil
98
+ end
99
+
100
+ def paused?
101
+
102
+ !! @paused_at
103
+ end
104
+
105
+ def determine_id
106
+
107
+ [
108
+ self.class.name.split(':').last.downcase[0..-4],
109
+ @scheduled_at.to_f,
110
+ (self.object_id < 0 ? 'm' : '') + self.object_id.to_s
111
+ ].map(&:to_s).join('_')
112
+ end
113
+
114
+ def occurrences(time0, time1)
115
+
116
+ a = []
117
+
118
+ nt = @next_time
119
+ ts = @times
120
+
121
+ loop do
122
+
123
+ break if nt > time1
124
+ break if ts && ts <= 0
125
+
126
+ a << nt if nt >= time0
127
+
128
+ nt = next_time_from(nt)
129
+ ts = ts - 1 if ts
130
+ end
131
+
132
+ a
133
+ end
134
+
135
+ # Starting from now, returns the {count} next occurences
136
+ # (EtOrbi::EoTime instances) for this job.
137
+ #
138
+ # Warning, for IntervalJob, the @mean_work_time is used since
139
+ # "interval" works from the end of a job to its next trigger
140
+ # (not from one trigger to the next, as for "cron" and "every").
141
+ #
142
+ def next_times(count)
143
+
144
+ (count - 1).times.inject([ next_time ]) { |a|
145
+ a << next_time_from(a.last)
146
+ a }
147
+ end
148
+
149
+ protected
150
+
151
+ def discard_past?
152
+
153
+ dp = @scheduler.discard_past
154
+ dp = @discard_past if @discard_past != nil
155
+ dp = @resume_discard_past if @resume_discard_past != nil
156
+
157
+ dp
158
+ end
159
+ end
160
+
161
+ #
162
+ # A parent class of EveryJob and IntervalJob
163
+ #
164
+ class Rufus::Scheduler::EvInJob < Rufus::Scheduler::RepeatJob
165
+
166
+ def first_at=(first)
167
+
168
+ @next_time = super
169
+ end
170
+ end
171
+
172
+ class Rufus::Scheduler::EveryJob < Rufus::Scheduler::EvInJob
173
+
174
+ attr_reader :frequency
175
+
176
+ attr_accessor :resume_discard_past
177
+
178
+ def initialize(scheduler, duration, opts, block)
179
+
180
+ super(scheduler, duration, opts, block)
181
+
182
+ @frequency = Rufus::Scheduler.parse_in(@original)
183
+ @discard_past = opts[:discard_past]
184
+
185
+ fail ArgumentError.new(
186
+ "cannot schedule #{self.class} with a frequency " +
187
+ "of #{@frequency.inspect} (#{@original.inspect})"
188
+ ) if @frequency <= 0
189
+
190
+ set_next_time(nil)
191
+ end
192
+
193
+ def check_frequency
194
+
195
+ fail ArgumentError.new(
196
+ "job frequency (#{@frequency}s) is higher than " +
197
+ "scheduler frequency (#{@scheduler.frequency}s)"
198
+ ) if @frequency < @scheduler.frequency
199
+ end
200
+
201
+ def next_time_from(time)
202
+
203
+ time + @frequency
204
+ end
205
+
206
+ protected
207
+
208
+ def set_next_time(trigger_time, is_post=false, now=nil)
209
+
210
+ return if is_post
211
+
212
+ n = now || EoTime.now
213
+
214
+ return @next_time = @first_at \
215
+ if @first_at && (trigger_time == nil || @first_at > n)
216
+
217
+ dp = discard_past?
218
+
219
+ loop do
220
+
221
+ @next_time = (@next_time || n) + @frequency
222
+
223
+ break if dp == false
224
+ break if @next_time > n
225
+ end
226
+ end
227
+ end
228
+
229
+ class Rufus::Scheduler::IntervalJob < Rufus::Scheduler::EvInJob
230
+
231
+ attr_reader :interval
232
+
233
+ def initialize(scheduler, interval, opts, block)
234
+
235
+ super(scheduler, interval, opts, block)
236
+
237
+ @interval = Rufus::Scheduler.parse_in(@original)
238
+
239
+ fail ArgumentError.new(
240
+ "cannot schedule #{self.class} with an interval " +
241
+ "of #{@interval.inspect} (#{@original.inspect})"
242
+ ) if @interval <= 0
243
+
244
+ set_next_time(nil)
245
+ end
246
+
247
+ def next_time_from(time)
248
+
249
+ time + @mean_work_time + @interval
250
+ end
251
+
252
+ protected
253
+
254
+ def set_next_time(trigger_time, is_post=false, now=nil)
255
+
256
+ n = now || EoTime.now
257
+
258
+ @next_time =
259
+ if is_post
260
+ n + @interval
261
+ elsif trigger_time.nil?
262
+ if @first_at == nil || @first_at < n
263
+ n + @interval
264
+ else
265
+ @first_at
266
+ end
267
+ else
268
+ false
269
+ end
270
+ end
271
+ end
272
+
273
+ class Rufus::Scheduler::CronJob < Rufus::Scheduler::RepeatJob
274
+
275
+ attr_reader :cron_line
276
+
277
+ def initialize(scheduler, cronline, opts, block)
278
+
279
+ super(scheduler, cronline, opts, block)
280
+
281
+ @cron_line = opts[:_t] || ::Fugit::Cron.do_parse(cronline)
282
+
283
+ set_next_time(nil)
284
+ end
285
+
286
+ def check_frequency
287
+
288
+ return if @scheduler.frequency <= 1
289
+ #
290
+ # The minimum time delta in a cron job is 1 second, so if the
291
+ # scheduler frequency is less than that, no worries.
292
+
293
+ f = @cron_line.rough_frequency
294
+
295
+ fail ArgumentError.new(
296
+ "job frequency (min ~#{f}s) is higher than " +
297
+ "scheduler frequency (#{@scheduler.frequency}s)"
298
+ ) if f < @scheduler.frequency
299
+ end
300
+
301
+ def brute_frequency
302
+
303
+ @cron_line.brute_frequency
304
+ end
305
+
306
+ def rough_frequency
307
+
308
+ @cron_line.rough_frequency
309
+ end
310
+
311
+ def next_time_from(time)
312
+
313
+ @cron_line.next_time(time)
314
+ end
315
+
316
+ protected
317
+
318
+ def set_next_time(trigger_time, is_post=false, now=nil)
319
+
320
+ return if is_post
321
+
322
+ t = trigger_time || now || EoTime.now
323
+
324
+ previous = @previous_time || @scheduled_at
325
+ t = previous if ! discard_past? && t > previous
326
+
327
+ @next_time =
328
+ if @first_at && @first_at > t
329
+ @first_at
330
+ else
331
+ @cron_line.next_time(t)
332
+ end
333
+ end
334
+ end
335
+
@@ -2,71 +2,68 @@
2
2
  require 'fileutils'
3
3
 
4
4
 
5
- class Rufus::Scheduler
5
+ #
6
+ # A lock that can always be acquired
7
+ #
8
+ class Rufus::Scheduler::NullLock
6
9
 
10
+ # Locking is always successful.
7
11
  #
8
- # A lock that can always be acquired
9
- #
10
- class NullLock
11
-
12
- # Locking is always successful.
13
- #
14
- def lock; true; end
12
+ def lock; true; end
15
13
 
16
- def locked?; true; end
17
- def unlock; true; end
18
- end
14
+ def locked?; true; end
15
+ def unlock; true; end
16
+ end
19
17
 
20
- #
21
- # The standard flock mechanism, with its own class thanks to @ecin
22
- #
23
- class FileLock
18
+ #
19
+ # The standard flock mechanism, with its own class thanks to @ecin
20
+ #
21
+ class Rufus::Scheduler::FileLock
24
22
 
25
- attr_reader :path
23
+ attr_reader :path
26
24
 
27
- def initialize(path)
25
+ def initialize(path)
28
26
 
29
- @path = path.to_s
30
- end
27
+ @path = path.to_s
28
+ end
31
29
 
32
- # Locking is successful if this Ruby process can create and lock
33
- # its lockfile (at the given path).
34
- #
35
- def lock
30
+ # Locking is successful if this Ruby process can create and lock
31
+ # its lockfile (at the given path).
32
+ #
33
+ def lock
36
34
 
37
- return true if locked?
35
+ return true if locked?
38
36
 
39
- @lockfile = nil
37
+ @lockfile = nil
40
38
 
41
- FileUtils.mkdir_p(::File.dirname(@path))
39
+ FileUtils.mkdir_p(::File.dirname(@path))
42
40
 
43
- file = File.new(@path, File::RDWR | File::CREAT)
44
- locked = file.flock(File::LOCK_NB | File::LOCK_EX)
41
+ file = File.new(@path, File::RDWR | File::CREAT)
42
+ locked = file.flock(File::LOCK_NB | File::LOCK_EX)
45
43
 
46
- return false unless locked
44
+ return false unless locked
47
45
 
48
- now = Time.now
46
+ now = Time.now
49
47
 
50
- file.print("pid: #{$$}, ")
51
- file.print("scheduler.object_id: #{self.object_id}, ")
52
- file.print("time: #{now}, ")
53
- file.print("timestamp: #{now.to_f}")
54
- file.flush
48
+ file.print("pid: #{$$}, ")
49
+ file.print("scheduler.object_id: #{self.object_id}, ")
50
+ file.print("time: #{now}, ")
51
+ file.print("timestamp: #{now.to_f}")
52
+ file.flush
55
53
 
56
- @lockfile = file
54
+ @lockfile = file
57
55
 
58
- true
59
- end
56
+ true
57
+ end
60
58
 
61
- def unlock
59
+ def unlock
62
60
 
63
- !! (@lockfile && @lockfile.flock(File::LOCK_UN))
64
- end
61
+ !! (@lockfile && @lockfile.flock(File::LOCK_UN))
62
+ end
65
63
 
66
- def locked?
64
+ def locked?
67
65
 
68
- !! (@lockfile && @lockfile.flock(File::LOCK_NB | File::LOCK_EX))
69
- end
66
+ !! (@lockfile && @lockfile.flock(File::LOCK_NB | File::LOCK_EX))
70
67
  end
71
68
  end
72
69