rufus-scheduler 3.6.0 → 3.7.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.
@@ -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