rufus-scheduler 3.6.0 → 3.7.0

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