rufus-scheduler 3.3.4 → 3.5.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,281 +1,192 @@
1
- #--
2
- # Copyright (c) 2006-2017, John Mettraux, jmettraux@gmail.com
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
- # Made in Japan.
23
- #++
24
-
25
1
 
26
2
  module Rufus
27
3
 
28
4
  class Scheduler
29
5
 
30
- #--
31
- # time and string methods
32
- #++
33
-
34
- def self.parse(o, opts={})
35
-
36
- opts[:no_error] = true
37
-
38
- parse_cron(o, opts) ||
39
- parse_in(o, opts) || # covers 'every' schedule strings
40
- parse_at(o, opts) ||
41
- fail(ArgumentError.new("couldn't parse #{o.inspect} (#{o.class})"))
42
- end
43
-
44
- def self.parse_cron(o, opts)
45
-
46
- CronLine.new(o)
47
-
48
- rescue ArgumentError => ae
49
-
50
- return nil if opts[:no_error]
51
- fail ae
52
- end
53
-
54
- def self.parse_in(o, opts={})
55
-
56
- #o.is_a?(String) ? parse_duration(o, opts) : o
57
-
58
- return parse_duration(o, opts) if o.is_a?(String)
59
- return o if o.is_a?(Numeric)
6
+ class << self
60
7
 
61
- fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
8
+ #--
9
+ # time and string methods
10
+ #++
62
11
 
63
- rescue ArgumentError => ae
12
+ def parse(o, opts={})
64
13
 
65
- return nil if opts[:no_error]
66
- fail ae
67
- end
14
+ opts[:no_error] = true
68
15
 
69
- def self.parse_at(o, opts={})
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
70
21
 
71
- return o if o.is_a?(Rufus::Scheduler::ZoTime)
72
- return Rufus::Scheduler::ZoTime.make(o) if o.is_a?(Time)
73
- Rufus::Scheduler::ZoTime.parse(o, opts)
22
+ def parse_cron(o, opts)
74
23
 
75
- rescue StandardError => se
24
+ Fugit.parse_cron(o)
25
+ end
76
26
 
77
- return nil if opts[:no_error]
78
- fail se
79
- end
27
+ def parse_in(o, opts={})
80
28
 
81
- DURATIONS2M = [
82
- [ 'y', 365 * 24 * 3600 ],
83
- [ 'M', 30 * 24 * 3600 ],
84
- [ 'w', 7 * 24 * 3600 ],
85
- [ 'd', 24 * 3600 ],
86
- [ 'h', 3600 ],
87
- [ 'm', 60 ],
88
- [ 's', 1 ]
89
- ]
90
- DURATIONS2 = DURATIONS2M.dup
91
- DURATIONS2.delete_at(1)
92
-
93
- DURATIONS = DURATIONS2M.inject({}) { |r, (k, v)| r[k] = v; r }
94
- DURATION_LETTERS = DURATIONS.keys.join
95
-
96
- DU_KEYS = DURATIONS2M.collect { |k, v| k.to_sym }
97
-
98
- # Turns a string like '1m10s' into a float like '70.0', more formally,
99
- # turns a time duration expressed as a string into a Float instance
100
- # (millisecond count).
101
- #
102
- # w -> week
103
- # d -> day
104
- # h -> hour
105
- # m -> minute
106
- # s -> second
107
- # M -> month
108
- # y -> year
109
- # 'nada' -> millisecond
110
- #
111
- # Some examples:
112
- #
113
- # Rufus::Scheduler.parse_duration "0.5" # => 0.5
114
- # Rufus::Scheduler.parse_duration "500" # => 0.5
115
- # Rufus::Scheduler.parse_duration "1000" # => 1.0
116
- # Rufus::Scheduler.parse_duration "1h" # => 3600.0
117
- # Rufus::Scheduler.parse_duration "1h10s" # => 3610.0
118
- # Rufus::Scheduler.parse_duration "1w2d" # => 777600.0
119
- #
120
- # Negative time strings are OK (Thanks Danny Fullerton):
121
- #
122
- # Rufus::Scheduler.parse_duration "-0.5" # => -0.5
123
- # Rufus::Scheduler.parse_duration "-1h" # => -3600.0
124
- #
125
- def self.parse_duration(string, opts={})
29
+ #o.is_a?(String) ? parse_duration(o, opts) : o
126
30
 
127
- s = string.to_s.strip
128
- mod = s[0, 1] == '-' ? -1 : 1
129
- s = s[1..-1] if mod == -1
31
+ return parse_duration(o, opts) if o.is_a?(String)
32
+ return o if o.is_a?(Numeric)
130
33
 
131
- ss = mod < 0 ? '-' : ''
132
- r = 0.0
34
+ fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
133
35
 
134
- s.scan(/(\d*\.\d+|\d+\.?)([#{DURATION_LETTERS}]?)/) do |f, d|
135
- ss += "#{f}#{d}"
136
- r += f.to_f * (DURATIONS[d] || 1.0)
137
- end
36
+ rescue ArgumentError => ae
138
37
 
139
- if ss == '-' || ss != string.to_s.strip
140
38
  return nil if opts[:no_error]
141
- fail ArgumentError.new("invalid time duration #{string.inspect}")
39
+ fail ae
142
40
  end
143
41
 
144
- mod * r
145
- end
146
-
147
- class << self
148
- #-
149
- # for compatibility with rufus-scheduler 2.x
150
- #+
151
- alias parse_duration_string parse_duration
152
- alias parse_time_string parse_duration
153
- end
154
-
155
-
156
- # Turns a number of seconds into a a time string
157
- #
158
- # Rufus.to_duration 0 # => '0s'
159
- # Rufus.to_duration 60 # => '1m'
160
- # Rufus.to_duration 3661 # => '1h1m1s'
161
- # Rufus.to_duration 7 * 24 * 3600 # => '1w'
162
- # Rufus.to_duration 30 * 24 * 3600 + 1 # => "4w2d1s"
163
- #
164
- # It goes from seconds to the year. Months are not counted (as they
165
- # are of variable length). Weeks are counted.
166
- #
167
- # For 30 days months to be counted, the second parameter of this
168
- # method can be set to true.
169
- #
170
- # Rufus.to_duration 30 * 24 * 3600 + 1, true # => "1M1s"
171
- #
172
- # If a Float value is passed, milliseconds will be displayed without
173
- # 'marker'
174
- #
175
- # Rufus.to_duration 0.051 # => "51"
176
- # Rufus.to_duration 7.051 # => "7s51"
177
- # Rufus.to_duration 0.120 + 30 * 24 * 3600 + 1 # => "4w2d1s120"
178
- #
179
- # (this behaviour mirrors the one found for parse_time_string()).
180
- #
181
- # Options are :
182
- #
183
- # * :months, if set to true, months (M) of 30 days will be taken into
184
- # account when building up the result
185
- # * :drop_seconds, if set to true, seconds and milliseconds will be trimmed
186
- # from the result
187
- #
188
- def self.to_duration(seconds, options={})
189
-
190
- h = to_duration_hash(seconds, options)
191
-
192
- return (options[:drop_seconds] ? '0m' : '0s') if h.empty?
193
-
194
- s =
195
- DU_KEYS.inject('') { |r, key|
196
- count = h[key]
197
- count = nil if count == 0
198
- r << "#{count}#{key}" if count
199
- r
200
- }
42
+ def parse_at(o, opts={})
201
43
 
202
- ms = h[:ms]
203
- s << ms.to_s if ms
44
+ return o if o.is_a?(EoTime)
45
+ return EoTime.make(o) if o.is_a?(Time)
46
+ EoTime.parse(o, opts)
204
47
 
205
- s
206
- end
207
-
208
- class << self
209
- #-
210
- # for compatibility with rufus-scheduler 2.x
211
- #+
212
- alias to_duration_string to_duration
213
- alias to_time_string to_duration
214
- end
48
+ rescue StandardError => se
215
49
 
216
- # Turns a number of seconds (integer or Float) into a hash like in :
217
- #
218
- # Rufus.to_duration_hash 0.051
219
- # # => { :ms => "51" }
220
- # Rufus.to_duration_hash 7.051
221
- # # => { :s => 7, :ms => "51" }
222
- # Rufus.to_duration_hash 0.120 + 30 * 24 * 3600 + 1
223
- # # => { :w => 4, :d => 2, :s => 1, :ms => "120" }
224
- #
225
- # This method is used by to_duration behind the scenes.
226
- #
227
- # Options are :
228
- #
229
- # * :months, if set to true, months (M) of 30 days will be taken into
230
- # account when building up the result
231
- # * :drop_seconds, if set to true, seconds and milliseconds will be trimmed
232
- # from the result
233
- #
234
- def self.to_duration_hash(seconds, options={})
50
+ return nil if opts[:no_error]
51
+ fail se
52
+ end
235
53
 
236
- h = {}
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
237
91
 
238
- if seconds.is_a?(Float)
239
- h[:ms] = (seconds % 1 * 1000).to_i
240
- seconds = seconds.to_i
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={})
125
+
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
130
+
131
+ to_fugit_duration(seconds, options).to_rufus_s
241
132
  end
242
133
 
243
- if options[:drop_seconds]
244
- h.delete(:ms)
245
- seconds = (seconds - seconds % 60)
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={})
153
+
154
+ to_fugit_duration(seconds, options).to_rufus_h
246
155
  end
247
156
 
248
- durations = options[:months] ? DURATIONS2M : DURATIONS2
157
+ # Used by both .to_duration and .to_duration_hash
158
+ #
159
+ def to_fugit_duration(seconds, options={})
249
160
 
250
- durations.each do |key, duration|
161
+ d = Fugit::Duration
162
+ .parse(seconds, options)
163
+ .deflate
251
164
 
252
- count = seconds / duration
253
- seconds = seconds % duration
165
+ d = d.drop_seconds if options[:drop_seconds]
166
+ d = d.deflate(:month => options[:months]) if options[:months]
254
167
 
255
- h[key.to_sym] = count if count > 0
168
+ d
256
169
  end
257
170
 
258
- h
259
- end
260
-
261
- #--
262
- # misc
263
- #++
171
+ #--
172
+ # misc
173
+ #++
264
174
 
265
- # Produces the UTC string representation of a Time instance
266
- #
267
- # like "2009/11/23 11:11:50.947109 UTC"
268
- #
269
- def self.utc_to_s(t=Time.now)
175
+ # Produces the UTC string representation of a Time instance
176
+ #
177
+ # like "2009/11/23 11:11:50.947109 UTC"
178
+ #
179
+ def utc_to_s(t=Time.now)
270
180
 
271
- "#{t.utc.strftime('%Y-%m-%d %H:%M:%S')}.#{sprintf('%06d', t.usec)} UTC"
272
- end
181
+ "#{t.utc.strftime('%Y-%m-%d %H:%M:%S')}.#{sprintf('%06d', t.usec)} UTC"
182
+ end
273
183
 
274
- # Produces a hour/min/sec/milli string representation of Time instance
275
- #
276
- def self.h_to_s(t=Time.now)
184
+ # Produces a hour/min/sec/milli string representation of Time instance
185
+ #
186
+ def h_to_s(t=Time.now)
277
187
 
278
- "#{t.strftime('%H:%M:%S')}.#{sprintf('%06d', t.usec)}"
188
+ "#{t.strftime('%H:%M:%S')}.#{sprintf('%06d', t.usec)}"
189
+ end
279
190
  end
280
191
 
281
192
  # Debugging tools...
@@ -1,46 +1,25 @@
1
- #--
2
- # Copyright (c) 2006-2017, John Mettraux, jmettraux@gmail.com
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
- # Made in Japan.
23
- #++
24
1
 
2
+ require 'set'
25
3
  require 'date' if RUBY_VERSION < '1.9.0'
26
4
  require 'time'
27
5
  require 'thread'
28
- require 'tzinfo'
6
+
7
+ require 'fugit'
29
8
 
30
9
 
31
10
  module Rufus
32
11
 
33
12
  class Scheduler
34
13
 
14
+ VERSION = '3.5.0'
15
+
16
+ EoTime = ::EtOrbi::EoTime
17
+
35
18
  require 'rufus/scheduler/util'
36
- require 'rufus/scheduler/zotime'
37
19
  require 'rufus/scheduler/jobs'
38
- require 'rufus/scheduler/cronline'
39
20
  require 'rufus/scheduler/job_array'
40
21
  require 'rufus/scheduler/locks'
41
22
 
42
- VERSION = '3.3.4'
43
-
44
23
  #
45
24
  # A common error class for rufus-scheduler
46
25
  #
@@ -104,7 +83,7 @@ module Rufus
104
83
  @trigger_lock = opts[:trigger_lock] || Rufus::Scheduler::NullLock.new
105
84
 
106
85
  # If we can't grab the @scheduler_lock, don't run.
107
- @scheduler_lock.lock || return
86
+ lock || return
108
87
 
109
88
  start
110
89
  end
@@ -154,12 +133,12 @@ module Rufus
154
133
 
155
134
  def uptime
156
135
 
157
- @started_at ? Rufus::Scheduler::ZoTime.now - @started_at : nil
136
+ @started_at ? EoTime.now - @started_at : nil
158
137
  end
159
138
 
160
139
  def uptime_s
161
140
 
162
- self.class.to_duration(uptime)
141
+ uptime ? self.class.to_duration(uptime) : ''
163
142
  end
164
143
 
165
144
  def join
@@ -258,9 +237,9 @@ module Rufus
258
237
  opts[:_t] = Scheduler.parse(arg, opts)
259
238
 
260
239
  case opts[:_t]
261
- when CronLine then schedule_cron(arg, callable, opts, &block)
262
- when Time then schedule_at(arg, callable, opts, &block)
263
- else schedule_in(arg, callable, opts, &block)
240
+ when ::Fugit::Cron then schedule_cron(arg, callable, opts, &block)
241
+ when ::EtOrbi::EoTime, Time then schedule_at(arg, callable, opts, &block)
242
+ else schedule_in(arg, callable, opts, &block)
264
243
  end
265
244
  end
266
245
 
@@ -272,8 +251,8 @@ module Rufus
272
251
  opts[:_t] = Scheduler.parse(arg, opts)
273
252
 
274
253
  case opts[:_t]
275
- when CronLine then schedule_cron(arg, callable, opts, &block)
276
- else schedule_every(arg, callable, opts, &block)
254
+ when ::Fugit::Cron then schedule_cron(arg, callable, opts, &block)
255
+ else schedule_every(arg, callable, opts, &block)
277
256
  end
278
257
  end
279
258
 
@@ -409,7 +388,7 @@ module Rufus
409
388
  # Accepts a query option, which can be set to:
410
389
  # * :all (default), returns all the threads that are work threads
411
390
  # or are currently running a job
412
- # * :active, returns all threads that are currenly running a job
391
+ # * :active, returns all threads that are currently running a job
413
392
  # * :vacant, returns the threads that are not running a job
414
393
  #
415
394
  # If, thanks to :blocking => true, a job is scheduled to monopolize the
@@ -478,7 +457,9 @@ module Rufus
478
457
  stderr.puts(" #{pre} tz:")
479
458
  stderr.puts(" #{pre} ENV['TZ']: #{ENV['TZ']}")
480
459
  stderr.puts(" #{pre} Time.now: #{Time.now}")
481
- stderr.puts(" #{pre} local_tzone: #{Rufus::Scheduler::ZoTime.local_tzone.inspect}")
460
+ stderr.puts(" #{pre} local_tzone: #{EoTime.local_tzone.inspect}")
461
+ stderr.puts(" #{pre} et-orbi:")
462
+ stderr.puts(" #{pre} #{EoTime.platform_info}")
482
463
  stderr.puts(" #{pre} scheduler:")
483
464
  stderr.puts(" #{pre} object_id: #{object_id}")
484
465
  stderr.puts(" #{pre} opts:")
@@ -558,7 +539,7 @@ module Rufus
558
539
 
559
540
  def start
560
541
 
561
- @started_at = Rufus::Scheduler::ZoTime.now
542
+ @started_at = EoTime.now
562
543
 
563
544
  @thread =
564
545
  Thread.new do
@@ -585,7 +566,7 @@ module Rufus
585
566
 
586
567
  def trigger_jobs
587
568
 
588
- now = Rufus::Scheduler::ZoTime.now
569
+ now = EoTime.now
589
570
 
590
571
  @jobs.each(now) do |job|
591
572
 
@@ -604,9 +585,9 @@ module Rufus
604
585
  next unless job && to && ts
605
586
  # thread might just have become inactive (job -> nil)
606
587
 
607
- to = ts + to unless to.is_a?(Rufus::Scheduler::ZoTime)
588
+ to = ts + to unless to.is_a?(EoTime)
608
589
 
609
- next if to > Rufus::Scheduler::ZoTime.now
590
+ next if to > EoTime.now
610
591
 
611
592
  t.raise(Rufus::Scheduler::TimeoutError)
612
593
  end
@@ -638,10 +619,35 @@ module Rufus
638
619
 
639
620
  job = job_class.new(self, t, opts, block || callable)
640
621
 
641
- fail ArgumentError.new(
642
- "job frequency (#{job.frequency}) is higher than " +
643
- "scheduler frequency (#{@frequency})"
644
- ) if job.respond_to?(:frequency) && job.frequency < @frequency
622
+ #fail ArgumentError.new(
623
+ # "job frequency (#{job.frequency}) is higher than " +
624
+ # "scheduler frequency (#{@frequency})"
625
+ #) if job.respond_to?(:frequency) && job.frequency < @frequency
626
+ #
627
+ # This was expensive
628
+
629
+ if (
630
+ ! job.is_a?(Rufus::Scheduler::IntervalJob) &&
631
+ job.methods.include?(:next_time_from)
632
+ ) then
633
+
634
+ nts = (1..365)
635
+ .inject([ job.send(:next_time_from, EtOrbi.now) ]) { |a, i|
636
+ a << job.send(:next_time_from, a.last); a }
637
+ deltas = []; prev = nts.shift
638
+ while (nt = nts.shift); deltas << (nt - prev).to_f; prev = nt; end
639
+
640
+ deltas = deltas[1..-1] \
641
+ if opts.keys.find { |k| k.to_s.match(/\Afirst/) }
642
+ #
643
+ # do not consider the first delta if there is a first, first_at,
644
+ # or first_in involved
645
+
646
+ fail ArgumentError.new(
647
+ "job frequency (~max #{deltas.min}s) is higher than " +
648
+ "scheduler frequency (#{@frequency})"
649
+ ) if deltas.min < @frequency * 0.9
650
+ end
645
651
 
646
652
  @jobs.push(job)
647
653
 
@@ -16,20 +16,24 @@ Gem::Specification.new do |s|
16
16
  s.summary = 'job scheduler for Ruby (at, cron, in and every jobs)'
17
17
 
18
18
  s.description = %{
19
- job scheduler for Ruby (at, cron, in and every jobs).
19
+ Job scheduler for Ruby (at, cron, in and every jobs). Not a replacement for crond.
20
20
  }.strip
21
21
 
22
22
  #s.files = `git ls-files`.split("\n")
23
23
  s.files = Dir[
24
+ 'README.{md,txt}',
25
+ 'CHANGELOG.{md,txt}', 'CREDITS.{md,txt}', 'LICENSE.{md,txt}',
24
26
  'Makefile',
25
27
  'lib/**/*.rb', #'spec/**/*.rb', 'test/**/*.rb',
26
- '*.gemspec', '*.txt', '*.rdoc', '*.md'
28
+ "#{s.name}.gemspec",
27
29
  ]
28
30
 
29
- s.add_runtime_dependency 'tzinfo'
31
+ s.required_ruby_version = '>= 1.9'
30
32
 
31
- s.add_development_dependency 'rspec', '>= 2.13.0'
32
- s.add_development_dependency 'chronic'
33
+ s.add_runtime_dependency 'fugit', '~> 1.1', '>= 1.1.1'
34
+
35
+ s.add_development_dependency 'rspec', '~> 3.7'
36
+ s.add_development_dependency 'chronic', '~> 0.10'
33
37
 
34
38
  s.require_path = 'lib'
35
39
  end