rufus-scheduler 3.5.2 → 3.8.1

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.
@@ -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