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.
@@ -1,200 +1,216 @@
1
1
 
2
- module Rufus
2
+ class Rufus::Scheduler
3
3
 
4
- class Scheduler
4
+ class << self
5
5
 
6
- class << self
6
+ #--
7
+ # time and string methods
8
+ #++
7
9
 
8
- #--
9
- # time and string methods
10
- #++
10
+ def parse(o, opts={})
11
11
 
12
- def parse(o, opts={})
12
+ opts[:no_error] = true
13
13
 
14
- opts[:no_error] = true
14
+ parse_cron(o, opts) ||
15
+ parse_in(o, opts) || # covers 'every' schedule strings
16
+ parse_at(o, opts) ||
17
+ fail(ArgumentError.new("couldn't parse #{o.inspect} (#{o.class})"))
18
+ end
15
19
 
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
20
+ def parse_cron(o, opts={})
21
21
 
22
- def parse_cron(o, opts)
22
+ opts[:no_error] ?
23
+ Fugit.parse_cron(o) :
24
+ Fugit.do_parse_cron(o)
25
+ end
23
26
 
24
- Fugit.parse_cron(o)
25
- end
27
+ def parse_in(o, opts={})
26
28
 
27
- def parse_in(o, opts={})
29
+ #o.is_a?(String) ? parse_duration(o, opts) : o
28
30
 
29
- #o.is_a?(String) ? parse_duration(o, opts) : o
31
+ return parse_duration(o, opts) if o.is_a?(String)
32
+ return o if o.is_a?(Numeric)
30
33
 
31
- return parse_duration(o, opts) if o.is_a?(String)
32
- return o if o.is_a?(Numeric)
34
+ fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
33
35
 
34
- fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
36
+ rescue ArgumentError => ae
35
37
 
36
- rescue ArgumentError => ae
38
+ return nil if opts[:no_error]
39
+ fail ae
40
+ end
37
41
 
38
- return nil if opts[:no_error]
39
- fail ae
40
- end
42
+ def parse_at(o, opts={})
41
43
 
42
- def parse_at(o, opts={})
44
+ return o if o.is_a?(EoTime)
45
+ return EoTime.make(o) if o.is_a?(Time)
46
+ EoTime.parse(o, opts)
43
47
 
44
- return o if o.is_a?(EoTime)
45
- return EoTime.make(o) if o.is_a?(Time)
46
- EoTime.parse(o, opts)
48
+ rescue StandardError => se
47
49
 
48
- rescue StandardError => se
50
+ return nil if opts[:no_error]
51
+ fail se
52
+ end
49
53
 
50
- return nil if opts[:no_error]
51
- fail se
52
- end
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
53
91
 
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
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={})
91
125
 
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={})
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
125
130
 
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
131
+ to_fugit_duration(seconds, options).to_rufus_s
132
+ end
130
133
 
131
- to_fugit_duration(seconds, options).to_rufus_s
132
- end
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={})
133
153
 
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={})
154
+ to_fugit_duration(seconds, options).to_rufus_h
155
+ end
153
156
 
154
- to_fugit_duration(seconds, options).to_rufus_h
155
- end
157
+ # Used by both .to_duration and .to_duration_hash
158
+ #
159
+ def to_fugit_duration(seconds, options={})
156
160
 
157
- # Used by both .to_duration and .to_duration_hash
158
- #
159
- def to_fugit_duration(seconds, options={})
161
+ d = Fugit::Duration
162
+ .parse(seconds, options)
163
+ .deflate
160
164
 
161
- d = Fugit::Duration
162
- .parse(seconds, options)
163
- .deflate
165
+ d = d.drop_seconds if options[:drop_seconds]
166
+ d = d.deflate(:month => options[:months]) if options[:months]
164
167
 
165
- d = d.drop_seconds if options[:drop_seconds]
166
- d = d.deflate(:month => options[:months]) if options[:months]
168
+ d
169
+ end
167
170
 
168
- d
169
- end
171
+ #--
172
+ # misc
173
+ #++
170
174
 
171
- #--
172
- # misc
173
- #++
175
+ if RUBY_VERSION > '1.9.9'
174
176
 
175
177
  # Produces the UTC string representation of a Time instance
176
178
  #
177
179
  # like "2009/11/23 11:11:50.947109 UTC"
178
180
  #
179
181
  def utc_to_s(t=Time.now)
180
-
181
- "#{t.utc.strftime('%Y-%m-%d %H:%M:%S')}.#{sprintf('%06d', t.usec)} UTC"
182
+ "#{t.dup.utc.strftime('%F %T.%6N')} UTC"
182
183
  end
183
184
 
184
185
  # Produces a hour/min/sec/milli string representation of Time instance
185
186
  #
186
187
  def h_to_s(t=Time.now)
188
+ t.strftime('%T.%6N')
189
+ end
190
+ else
187
191
 
192
+ def utc_to_s(t=Time.now)
193
+ "#{t.utc.strftime('%Y-%m-%d %H:%M:%S')}.#{sprintf('%06d', t.usec)} UTC"
194
+ end
195
+ def h_to_s(t=Time.now)
188
196
  "#{t.strftime('%H:%M:%S')}.#{sprintf('%06d', t.usec)}"
189
197
  end
190
198
  end
191
199
 
192
- # Debugging tools...
193
- #
194
- class D
195
-
196
- def self.h_to_s(t=Time.now); Rufus::Scheduler.h_to_s(t); end
200
+ if defined?(Process::CLOCK_MONOTONIC)
201
+ def monow; Process.clock_gettime(Process::CLOCK_MONOTONIC); end
202
+ else
203
+ def monow; Time.now.to_f; end
197
204
  end
205
+
206
+ def ltstamp; Time.now.strftime('%FT%T.%3N'); end
207
+ end
208
+
209
+ # Debugging tools...
210
+ #
211
+ class D
212
+
213
+ def self.h_to_s(t=Time.now); Rufus::Scheduler.h_to_s(t); end
198
214
  end
199
215
  end
200
216
 
@@ -10,8 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.authors = [ 'John Mettraux' ]
12
12
  s.email = [ 'jmettraux@gmail.com' ]
13
- s.homepage = 'http://github.com/jmettraux/rufus-scheduler'
14
- s.rubyforge_project = 'rufus'
13
+ s.homepage = 'https://github.com/jmettraux/rufus-scheduler'
15
14
  s.license = 'MIT'
16
15
  s.summary = 'job scheduler for Ruby (at, cron, in and every jobs)'
17
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufus-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-22 00:00:00.000000000 Z
11
+ date: 2020-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fugit
@@ -74,18 +74,20 @@ files:
74
74
  - lib/rufus-scheduler.rb
75
75
  - lib/rufus/scheduler.rb
76
76
  - lib/rufus/scheduler/job_array.rb
77
- - lib/rufus/scheduler/jobs.rb
77
+ - lib/rufus/scheduler/jobs_core.rb
78
+ - lib/rufus/scheduler/jobs_one_time.rb
79
+ - lib/rufus/scheduler/jobs_repeat.rb
78
80
  - lib/rufus/scheduler/locks.rb
79
81
  - lib/rufus/scheduler/util.rb
80
82
  - rufus-scheduler.gemspec
81
- homepage: http://github.com/jmettraux/rufus-scheduler
83
+ homepage: https://github.com/jmettraux/rufus-scheduler
82
84
  licenses:
83
85
  - MIT
84
86
  metadata:
85
- changelog_uri: http://github.com/jmettraux/rufus-scheduler/blob/master/CHANGELOG.md
86
- bug_tracker_uri: http://github.com/jmettraux/rufus-scheduler/issues
87
- homepage_uri: http://github.com/jmettraux/rufus-scheduler
88
- source_code_uri: http://github.com/jmettraux/rufus-scheduler
87
+ changelog_uri: https://github.com/jmettraux/rufus-scheduler/blob/master/CHANGELOG.md
88
+ bug_tracker_uri: https://github.com/jmettraux/rufus-scheduler/issues
89
+ homepage_uri: https://github.com/jmettraux/rufus-scheduler
90
+ source_code_uri: https://github.com/jmettraux/rufus-scheduler
89
91
  post_install_message:
90
92
  rdoc_options: []
91
93
  require_paths:
@@ -101,8 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
103
  - !ruby/object:Gem::Version
102
104
  version: '0'
103
105
  requirements: []
104
- rubyforge_project: rufus
105
- rubygems_version: 2.6.14.3
106
+ rubygems_version: 3.0.3
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: job scheduler for Ruby (at, cron, in and every jobs)
@@ -1,701 +0,0 @@
1
-
2
- module Rufus
3
-
4
- class Scheduler
5
-
6
- #--
7
- # job classes
8
- #++
9
-
10
- class Job
11
-
12
- #
13
- # Used by Job#kill
14
- #
15
- class KillSignal < StandardError; end
16
-
17
- attr_reader :id
18
- attr_reader :opts
19
- attr_reader :original
20
- attr_reader :scheduled_at
21
- attr_reader :last_time
22
- attr_reader :unscheduled_at
23
- attr_reader :tags
24
- attr_reader :count
25
- attr_reader :last_work_time
26
- attr_reader :mean_work_time
27
-
28
- # next trigger time
29
- #
30
- attr_accessor :next_time
31
-
32
- # previous "next trigger time"
33
- #
34
- attr_accessor :previous_time
35
-
36
- # anything with a #call(job[, timet]) method,
37
- # what gets actually triggered
38
- #
39
- attr_reader :callable
40
-
41
- # a reference to the instance whose call method is the @callable
42
- #
43
- attr_reader :handler
44
-
45
- def initialize(scheduler, original, opts, block)
46
-
47
- @scheduler = scheduler
48
- @original = original
49
- @opts = opts
50
-
51
- @handler = block
52
-
53
- @callable =
54
- if block.respond_to?(:arity)
55
- block
56
- elsif block.respond_to?(:call)
57
- block.method(:call)
58
- elsif block.is_a?(Class)
59
- @handler = block.new
60
- @handler.method(:call) rescue nil
61
- else
62
- nil
63
- end
64
-
65
- @scheduled_at = EoTime.now
66
- @unscheduled_at = nil
67
- @last_time = nil
68
-
69
- @locals = {}
70
- @local_mutex = Mutex.new
71
-
72
- @id = determine_id
73
-
74
- fail(
75
- ArgumentError,
76
- 'missing block or callable to schedule',
77
- caller[2..-1]
78
- ) unless @callable
79
-
80
- @tags = Array(opts[:tag] || opts[:tags]).collect { |t| t.to_s }
81
-
82
- @count = 0
83
- @last_work_time = 0.0
84
- @mean_work_time = 0.0
85
-
86
- # tidy up options
87
-
88
- if @opts[:allow_overlap] == false || @opts[:allow_overlapping] == false
89
- @opts[:overlap] = false
90
- end
91
- if m = @opts[:mutex]
92
- @opts[:mutex] = Array(m)
93
- end
94
- end
95
-
96
- alias job_id id
97
-
98
- # Will fail with an ArgumentError if the job frequency is higher than
99
- # the scheduler frequency.
100
- #
101
- def check_frequency
102
-
103
- # this parent implementation never fails
104
- end
105
-
106
- def trigger(time)
107
-
108
- @previous_time = @next_time
109
- set_next_time(time)
110
-
111
- do_trigger(time)
112
- end
113
-
114
- # Trigger the job right now, off of its schedule.
115
- #
116
- # Done in collaboration with Piavka in
117
- # https://github.com/jmettraux/rufus-scheduler/issues/214
118
- #
119
- def trigger_off_schedule(time=EoTime.now)
120
-
121
- do_trigger(time)
122
- end
123
-
124
- def unschedule
125
-
126
- @unscheduled_at = EoTime.now
127
- end
128
-
129
- def threads
130
-
131
- Thread.list.select { |t| t[:rufus_scheduler_job] == self }
132
- end
133
-
134
- # Kills all the threads this Job currently has going on.
135
- #
136
- def kill
137
-
138
- threads.each { |t| t.raise(KillSignal) }
139
- end
140
-
141
- def running?
142
-
143
- threads.any?
144
- end
145
-
146
- def scheduled?
147
-
148
- @scheduler.scheduled?(self)
149
- end
150
-
151
- def []=(key, value)
152
-
153
- @local_mutex.synchronize { @locals[key] = value }
154
- end
155
-
156
- def [](key)
157
-
158
- @local_mutex.synchronize { @locals[key] }
159
- end
160
-
161
- def key?(key)
162
-
163
- @local_mutex.synchronize { @locals.key?(key) }
164
- end
165
-
166
- def keys
167
-
168
- @local_mutex.synchronize { @locals.keys }
169
- end
170
-
171
- #def hash
172
- # self.object_id
173
- #end
174
- #def eql?(o)
175
- # o.class == self.class && o.hash == self.hash
176
- #end
177
- #
178
- # might be necessary at some point
179
-
180
- def next_times(count)
181
-
182
- next_time ? [ next_time ] : []
183
- end
184
-
185
- # Calls the callable (usually a block) wrapped in this Job instance.
186
- #
187
- # Warning: error rescueing is the responsibity of the caller.
188
- #
189
- def call(do_rescue=false)
190
-
191
- do_call(EoTime.now, do_rescue)
192
- end
193
-
194
- protected
195
-
196
- def callback(meth, time)
197
-
198
- return true unless @scheduler.respond_to?(meth)
199
-
200
- arity = @scheduler.method(meth).arity
201
- args = [ self, time ][0, (arity < 0 ? 2 : arity)]
202
-
203
- @scheduler.send(meth, *args)
204
- end
205
-
206
- def compute_timeout
207
-
208
- if to = @opts[:timeout]
209
- Rufus::Scheduler.parse(to)
210
- else
211
- nil
212
- end
213
- end
214
-
215
- def mutex(m)
216
-
217
- m.is_a?(Mutex) ? m : (@scheduler.mutexes[m.to_s] ||= Mutex.new)
218
- end
219
-
220
- def do_call(time, do_rescue)
221
-
222
- args = [ self, time ][0, @callable.arity]
223
- @callable.call(*args)
224
-
225
- rescue StandardError => se
226
-
227
- fail se unless do_rescue
228
-
229
- return if se.is_a?(KillSignal) # discard
230
-
231
- @scheduler.on_error(self, se)
232
-
233
- # exceptions above StandardError do pass through
234
- end
235
-
236
- def do_trigger(time)
237
-
238
- return if (
239
- opts[:overlap] == false &&
240
- running?
241
- )
242
- return if (
243
- callback(:confirm_lock, time) &&
244
- callback(:on_pre_trigger, time)
245
- ) == false
246
-
247
- @count += 1
248
-
249
- if opts[:blocking]
250
- trigger_now(time)
251
- else
252
- trigger_queue(time)
253
- end
254
- end
255
-
256
- def trigger_now(time)
257
-
258
- t = EoTime.now
259
- # if there are mutexes, t might be really bigger than time
260
-
261
- Thread.current[:rufus_scheduler_job] = self
262
- Thread.current[:rufus_scheduler_time] = t
263
- Thread.current[:rufus_scheduler_timeout] = compute_timeout
264
-
265
- @last_time = t
266
-
267
- do_call(time, true)
268
-
269
- ensure
270
-
271
- @last_work_time =
272
- EoTime.now - Thread.current[:rufus_scheduler_time]
273
- @mean_work_time =
274
- ((@count - 1) * @mean_work_time + @last_work_time) / @count
275
-
276
- post_trigger(time)
277
-
278
- Thread.current[:rufus_scheduler_job] = nil
279
- Thread.current[:rufus_scheduler_time] = nil
280
- Thread.current[:rufus_scheduler_timeout] = nil
281
- end
282
-
283
- def post_trigger(time)
284
-
285
- set_next_time(time, true)
286
-
287
- callback(:on_post_trigger, time)
288
- end
289
-
290
- def start_work_thread
291
-
292
- thread =
293
- Thread.new do
294
-
295
- Thread.current[:rufus_scheduler_job] = true
296
- # indicates that the thread is going to be assigned immediately
297
-
298
- Thread.current[@scheduler.thread_key] = true
299
- Thread.current[:rufus_scheduler_work_thread] = true
300
-
301
- loop do
302
-
303
- job, time = @scheduler.work_queue.pop
304
-
305
- break if @scheduler.started_at == nil
306
-
307
- next if job.unscheduled_at
308
-
309
- begin
310
-
311
- (job.opts[:mutex] || []).reduce(
312
- lambda { job.trigger_now(time) }
313
- ) do |b, m|
314
- lambda { mutex(m).synchronize { b.call } }
315
- end.call
316
-
317
- rescue KillSignal
318
-
319
- # simply go on looping
320
- end
321
- end
322
- end
323
-
324
- thread[@scheduler.thread_key] = true
325
- thread[:rufus_scheduler_work_thread] = true
326
- #
327
- # same as above (in the thead block),
328
- # but since it has to be done as quickly as possible.
329
- # So, whoever is running first (scheduler thread vs job thread)
330
- # sets this information
331
-
332
- thread
333
- end
334
-
335
- def trigger_queue(time)
336
-
337
- threads = @scheduler.work_threads
338
-
339
- vac = threads.select { |t| t[:rufus_scheduler_job] == nil }.size
340
- que = @scheduler.work_queue.size
341
-
342
- cur = threads.size
343
- max = @scheduler.max_work_threads
344
-
345
- start_work_thread if vac - que < 1 && cur < max
346
-
347
- @scheduler.work_queue << [ self, time ]
348
- end
349
- end
350
-
351
- class OneTimeJob < Job
352
-
353
- alias time next_time
354
-
355
- def occurrences(time0, time1)
356
-
357
- (time >= time0 && time <= time1) ? [ time ] : []
358
- end
359
-
360
- protected
361
-
362
- def determine_id
363
-
364
- [
365
- self.class.name.split(':').last.downcase[0..-4],
366
- @scheduled_at.to_f,
367
- @next_time.to_f,
368
- (self.object_id < 0 ? 'm' : '') + self.object_id.to_s
369
- ].map(&:to_s).join('_')
370
- end
371
-
372
- # There is no next_time for one time jobs, hence the false.
373
- #
374
- def set_next_time(trigger_time, is_post=false)
375
-
376
- @next_time = is_post ? nil : false
377
- end
378
- end
379
-
380
- class AtJob < OneTimeJob
381
-
382
- def initialize(scheduler, time, opts, block)
383
-
384
- super(scheduler, time, opts, block)
385
-
386
- @next_time =
387
- opts[:_t] || Rufus::Scheduler.parse_at(time, opts)
388
- end
389
- end
390
-
391
- class InJob < OneTimeJob
392
-
393
- def initialize(scheduler, duration, opts, block)
394
-
395
- super(scheduler, duration, opts, block)
396
-
397
- @next_time =
398
- @scheduled_at +
399
- opts[:_t] || Rufus::Scheduler.parse_in(duration, opts)
400
- end
401
- end
402
-
403
- class RepeatJob < Job
404
-
405
- attr_reader :paused_at
406
-
407
- attr_reader :first_at
408
- attr_reader :last_at
409
- attr_accessor :times
410
-
411
- def initialize(scheduler, duration, opts, block)
412
-
413
- super
414
-
415
- @paused_at = nil
416
-
417
- @times = opts[:times]
418
-
419
- fail ArgumentError.new(
420
- "cannot accept :times => #{@times.inspect}, not nil or an int"
421
- ) unless @times == nil || @times.is_a?(Integer)
422
-
423
- self.first_at =
424
- opts[:first] || opts[:first_time] ||
425
- opts[:first_at] || opts[:first_in] ||
426
- nil
427
- self.last_at =
428
- opts[:last] || opts[:last_at] || opts[:last_in]
429
- end
430
-
431
- def first_at=(first)
432
-
433
- return (@first_at = nil) if first == nil
434
-
435
- n0 = EoTime.now
436
- n1 = n0 + 0.003
437
-
438
- first = n0 if first == :now || first == :immediately || first == 0
439
- fdur = Rufus::Scheduler.parse_duration(first, no_error: true)
440
-
441
- @first_at = (fdur && (EoTime.now + fdur)) || EoTime.make(first)
442
- @first_at = n1 if @first_at >= n0 && @first_at < n1
443
-
444
- fail ArgumentError.new(
445
- "cannot set first[_at|_in] in the past: " +
446
- "#{first.inspect} -> #{@first_at.inspect}"
447
- ) if @first_at < n0
448
-
449
- @first_at
450
- end
451
-
452
- def last_at=(last)
453
-
454
- @last_at =
455
- if last
456
- ldur = Rufus::Scheduler.parse_duration(last, no_error: true)
457
- (ldur && (EoTime.now + ldur)) || EoTime.make(last)
458
- else
459
- nil
460
- end
461
-
462
- fail ArgumentError.new(
463
- "cannot set last[_at|_in] in the past: " +
464
- "#{last.inspect} -> #{@last_at.inspect}"
465
- ) if last && @last_at < EoTime.now
466
-
467
- @last_at
468
- end
469
-
470
- def trigger(time)
471
-
472
- return if @paused_at
473
-
474
- return (@next_time = nil) if @times && @times < 1
475
- return (@next_time = nil) if @last_at && time >= @last_at
476
- #
477
- # It keeps jobs one step too much in @jobs, but it's OK
478
-
479
- super
480
-
481
- @times -= 1 if @times
482
- end
483
-
484
- def pause
485
-
486
- @paused_at = EoTime.now
487
- end
488
-
489
- def resume
490
-
491
- @paused_at = nil
492
- end
493
-
494
- def paused?
495
-
496
- @paused_at != nil
497
- end
498
-
499
- def determine_id
500
-
501
- [
502
- self.class.name.split(':').last.downcase[0..-4],
503
- @scheduled_at.to_f,
504
- (self.object_id < 0 ? 'm' : '') + self.object_id.to_s
505
- ].map(&:to_s).join('_')
506
- end
507
-
508
- def occurrences(time0, time1)
509
-
510
- a = []
511
-
512
- nt = @next_time
513
- ts = @times
514
-
515
- loop do
516
-
517
- break if nt > time1
518
- break if ts && ts <= 0
519
-
520
- a << nt if nt >= time0
521
-
522
- nt = next_time_from(nt)
523
- ts = ts - 1 if ts
524
- end
525
-
526
- a
527
- end
528
-
529
- # Starting from now, returns the {count} next occurences
530
- # (EtOrbi::EoTime instances) for this job.
531
- #
532
- # Warning, for IntervalJob, the @mean_work_time is used since
533
- # "interval" works from the end of a job to its next trigger
534
- # (not from one trigger to the next, as for "cron" and "every").
535
- #
536
- def next_times(count)
537
-
538
- (count - 1).times.inject([ next_time ]) { |a|
539
- a << next_time_from(a.last)
540
- a }
541
- end
542
- end
543
-
544
- #
545
- # A parent class of EveryJob and IntervalJob
546
- #
547
- class EvInJob < RepeatJob
548
-
549
- def first_at=(first)
550
-
551
- @next_time = super
552
- end
553
- end
554
-
555
- class EveryJob < EvInJob
556
-
557
- attr_reader :frequency
558
-
559
- def initialize(scheduler, duration, opts, block)
560
-
561
- super(scheduler, duration, opts, block)
562
-
563
- @frequency = Rufus::Scheduler.parse_in(@original)
564
-
565
- fail ArgumentError.new(
566
- "cannot schedule #{self.class} with a frequency " +
567
- "of #{@frequency.inspect} (#{@original.inspect})"
568
- ) if @frequency <= 0
569
-
570
- set_next_time(nil)
571
- end
572
-
573
- def check_frequency
574
-
575
- fail ArgumentError.new(
576
- "job frequency (#{@frequency}s) is higher than " +
577
- "scheduler frequency (#{@scheduler.frequency}s)"
578
- ) if @frequency < @scheduler.frequency
579
- end
580
-
581
- def next_time_from(time)
582
-
583
- time + @frequency
584
- end
585
-
586
- protected
587
-
588
- def set_next_time(trigger_time, is_post=false)
589
-
590
- return if is_post
591
-
592
- n = EoTime.now
593
-
594
- @next_time =
595
- if @first_at && (trigger_time == nil || @first_at > n)
596
- @first_at
597
- else
598
- (@next_time || n) + @frequency
599
- end
600
- end
601
- end
602
-
603
- class IntervalJob < EvInJob
604
-
605
- attr_reader :interval
606
-
607
- def initialize(scheduler, interval, opts, block)
608
-
609
- super(scheduler, interval, opts, block)
610
-
611
- @interval = Rufus::Scheduler.parse_in(@original)
612
-
613
- fail ArgumentError.new(
614
- "cannot schedule #{self.class} with an interval " +
615
- "of #{@interval.inspect} (#{@original.inspect})"
616
- ) if @interval <= 0
617
-
618
- set_next_time(nil)
619
- end
620
-
621
- def next_time_from(time)
622
-
623
- time + @mean_work_time + @interval
624
- end
625
-
626
- protected
627
-
628
- def set_next_time(trigger_time, is_post=false)
629
-
630
- @next_time =
631
- if is_post
632
- EoTime.now + @interval
633
- elsif trigger_time.nil?
634
- if @first_at == nil || @first_at < Time.now
635
- EoTime.now + @interval
636
- else
637
- @first_at
638
- end
639
- else
640
- false
641
- end
642
- end
643
- end
644
-
645
- class CronJob < RepeatJob
646
-
647
- attr_reader :cron_line
648
-
649
- def initialize(scheduler, cronline, opts, block)
650
-
651
- super(scheduler, cronline, opts, block)
652
-
653
- @cron_line = opts[:_t] || ::Fugit::Cron.do_parse(cronline)
654
-
655
- set_next_time(nil)
656
- end
657
-
658
- def check_frequency
659
-
660
- return if @scheduler.frequency <= 1
661
- #
662
- # The minimum time delta in a cron job is 1 second, so if the
663
- # scheduler frequency is less than that, no worries.
664
-
665
- f = @cron_line.rough_frequency
666
-
667
- fail ArgumentError.new(
668
- "job frequency (min ~#{f}s) is higher than " +
669
- "scheduler frequency (#{@scheduler.frequency}s)"
670
- ) if f < @scheduler.frequency
671
- end
672
-
673
- def brute_frequency
674
-
675
- @cron_line.brute_frequency
676
- end
677
-
678
- def rough_frequency
679
-
680
- @cron_line.rough_frequency
681
- end
682
-
683
- def next_time_from(time)
684
-
685
- if @first_at == nil || @first_at <= time
686
- @cron_line.next_time(time)
687
- else
688
- @first_at
689
- end
690
- end
691
-
692
- protected
693
-
694
- def set_next_time(trigger_time, is_post=false)
695
-
696
- @next_time = next_time_from(trigger_time || Time.now)
697
- end
698
- end
699
- end
700
- end
701
-