Class: RocketJob::Plugins::Rufus::CronLine
- Inherits:
-
Object
- Object
- RocketJob::Plugins::Rufus::CronLine
- Defined in:
- lib/rocket_job/plugins/rufus/cron_line.rb
Overview
A ‘cron line’ is a line in the sense of a crontab (man 5 crontab) file line.
Constant Summary collapse
- NEXT_TIME_MAX_YEARS =
The max number of years in the future or the past before giving up searching for #next_time or #previous_time respectively
14
Class Attribute Summary collapse
-
.cache ⇒ Object
readonly
Returns the value of attribute cache.
Instance Attribute Summary collapse
-
#days ⇒ Object
readonly
Returns the value of attribute days.
-
#hours ⇒ Object
readonly
Returns the value of attribute hours.
-
#minutes ⇒ Object
readonly
Returns the value of attribute minutes.
-
#months ⇒ Object
readonly
Returns the value of attribute months.
-
#original ⇒ Object
readonly
The string used for creating this cronline instance.
-
#original_timezone ⇒ Object
readonly
Returns the value of attribute original_timezone.
-
#seconds ⇒ Object
readonly
Returns the value of attribute seconds.
-
#timezone ⇒ Object
readonly
Returns the value of attribute timezone.
-
#weekdays ⇒ Object
readonly
attr_reader :monthdays # reader defined below.
Instance Method Summary collapse
-
#brute_frequency ⇒ Object
Returns the shortest delta between two potential occurences of the schedule described by this cronline.
-
#frequency ⇒ Object
Returns a quickly computed approximation of the frequency for this cron line.
-
#initialize(line) ⇒ CronLine
constructor
A new instance of CronLine.
-
#matches?(time) ⇒ Boolean
Returns true if the given time matches this cron line.
- #next_second(time) ⇒ Object
-
#next_time(from = ZoTime.now) ⇒ Object
Returns the next time that this cron line is supposed to ‘fire’.
- #prev_second(time) ⇒ Object
-
#previous_time(from = ZoTime.now) ⇒ Object
Returns the previous time the cronline matched.
-
#to_a ⇒ Object
(also: #to_array)
Returns an array of 6 arrays (seconds, minutes, hours, days, months, weekdays).
Constructor Details
#initialize(line) ⇒ CronLine
Returns a new instance of CronLine.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 55 def initialize(line) fail ArgumentError.new( "not a string: #{line.inspect}" ) unless line.is_a?(String) @original = line @original_timezone = nil items = line.split if @timezone = RocketJob::Plugins::Rufus::ZoTime.get_tzone(items.last) @original_timezone = items.pop else @timezone = RocketJob::Plugins::Rufus::ZoTime.get_tzone(:current) end fail ArgumentError.new( "not a valid cronline : '#{line}'" ) unless items.length == 5 or items.length == 6 offset = items.length - 5 @seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ] @minutes = parse_item(items[0 + offset], 0, 59) @hours = parse_item(items[1 + offset], 0, 24) @days = parse_item(items[2 + offset], -30, 31) @months = parse_item(items[3 + offset], 1, 12) @weekdays, @monthdays = parse_weekdays(items[4 + offset]) [ @seconds, @minutes, @hours, @months ].each do |es| fail ArgumentError.new( "invalid cronline: '#{line}'" ) if es && es.find { |e| ! e.is_a?(Integer) } end if @days && @days.include?(0) # gh-221 fail ArgumentError.new('invalid day 0 in cronline') end end |
Class Attribute Details
.cache ⇒ Object (readonly)
Returns the value of attribute cache.
260 261 262 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 260 def cache @cache end |
Instance Attribute Details
#days ⇒ Object (readonly)
Returns the value of attribute days.
49 50 51 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 49 def days @days end |
#hours ⇒ Object (readonly)
Returns the value of attribute hours.
48 49 50 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 48 def hours @hours end |
#minutes ⇒ Object (readonly)
Returns the value of attribute minutes.
47 48 49 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 47 def minutes @minutes end |
#months ⇒ Object (readonly)
Returns the value of attribute months.
50 51 52 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 50 def months @months end |
#original ⇒ Object (readonly)
The string used for creating this cronline instance.
43 44 45 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 43 def original @original end |
#original_timezone ⇒ Object (readonly)
Returns the value of attribute original_timezone.
44 45 46 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 44 def original_timezone @original_timezone end |
#seconds ⇒ Object (readonly)
Returns the value of attribute seconds.
46 47 48 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 46 def seconds @seconds end |
#timezone ⇒ Object (readonly)
Returns the value of attribute timezone.
53 54 55 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 53 def timezone @timezone end |
#weekdays ⇒ Object (readonly)
attr_reader :monthdays # reader defined below
52 53 54 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 52 def weekdays @weekdays end |
Instance Method Details
#brute_frequency ⇒ Object
Returns the shortest delta between two potential occurences of the schedule described by this cronline.
.
For a simple cronline like “*/5 * * * *”, obviously the frequency is five minutes. Why does this method look at a whole year of #next_time ?
Consider “* * * * sun#2,sun#3”, the computed frequency is 1 week (the shortest delta is the one between the second sunday and the third sunday). This method takes no chance and runs next_time for the span of a whole year and keeps the shortest.
Of course, this method can get VERY slow if you call on it a second- based cronline…
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 278 def brute_frequency key = "brute_frequency:#{@original}" delta = self.class.cache[key] return delta if delta delta = 366 * DAY_S t0 = previous_time(Time.local(2000, 1, 1)) loop do break if delta <= 1 break if delta <= 60 && @seconds && @seconds.size == 1 t1 = next_time(t0) d = t1 - t0 delta = d if d < delta break if @months.nil? && t1.month == 2 break if @months.nil? && @days.nil? && t1.day == 2 break if @months.nil? && @days.nil? && @hours.nil? && t1.hour == 1 break if @months.nil? && @days.nil? && @hours.nil? && @minutes.nil? && t1.min == 1 break if t1.year >= 2001 t0 = t1 end self.class.cache[key] = delta end |
#frequency ⇒ Object
Returns a quickly computed approximation of the frequency for this cron line.
#brute_frequency, on the other hand, will compute the frequency by examining a whole year, that can take more than seconds for a seconds level cron…
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 246 def frequency return brute_frequency unless @seconds && @seconds.length > 1 secs = toa(@seconds) secs[1..-1].inject([ secs[0], 60 ]) { |(prev, delta), sec| d = sec - prev [ sec, d < delta ? d : delta ] }[1] end |
#matches?(time) ⇒ Boolean
Returns true if the given time matches this cron line.
100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 100 def matches?(time) # FIXME Don't create a new ZoTime if time is already a ZoTime in same # zone ... # Wait, this seems only used in specs... t = ZoTime.new(time.to_f, @timezone) return false unless sub_match?(t, :sec, @seconds) return false unless sub_match?(t, :min, @minutes) return false unless sub_match?(t, :hour, @hours) return false unless date_match?(t) true end |
#next_second(time) ⇒ Object
309 310 311 312 313 314 315 316 317 318 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 309 def next_second(time) secs = toa(@seconds) return secs.first + 60 - time.sec if time.sec > secs.last secs.shift while secs.first < time.sec secs.first - time.sec end |
#next_time(from = ZoTime.now) ⇒ Object
Returns the next time that this cron line is supposed to ‘fire’
This is raw, 3 secs to iterate over 1 year on my macbook :( brutal. (Well, I was wrong, takes 0.001 sec on 1.8.7 and 1.9.1)
This method accepts an optional Time parameter. It’s the starting point for the ‘search’. By default, it’s Time.now
Note that the time instance returned will be in the same time zone that the given start point Time (thus a result in the local time zone will be passed if no start time is specified (search start time set to Time.now))
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.mktime(2008, 10, 24, 7, 29))
#=> Fri Oct 24 07:30:00 -0500 2008
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.utc(2008, 10, 24, 7, 29))
#=> Fri Oct 24 07:30:00 UTC 2008
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.utc(2008, 10, 24, 7, 29)).localtime
#=> Fri Oct 24 02:30:00 -0500 2008
(Thanks to K Liu for the note and the examples)
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 141 def next_time(from=ZoTime.now) nt = nil zt = ZoTime.new(from.to_i + 1, @timezone) maxy = from.year + NEXT_TIME_MAX_YEARS loop do nt = zt.dup fail RangeError.new( "failed to reach occurrence within " + "#{NEXT_TIME_MAX_YEARS} years for '#{original}'" ) if nt.year > maxy unless date_match?(nt) zt.add((24 - nt.hour) * 3600 - nt.min * 60 - nt.sec) next end unless sub_match?(nt, :hour, @hours) zt.add((60 - nt.min) * 60 - nt.sec) next end unless sub_match?(nt, :min, @minutes) zt.add(60 - nt.sec) next end unless sub_match?(nt, :sec, @seconds) zt.add(next_second(nt)) next end break end nt end |
#prev_second(time) ⇒ Object
320 321 322 323 324 325 326 327 328 329 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 320 def prev_second(time) secs = toa(@seconds) return time.sec + 60 - secs.last if time.sec < secs.first secs.pop while time.sec < secs.last time.sec - secs.last end |
#previous_time(from = ZoTime.now) ⇒ Object
Returns the previous time the cronline matched. It’s like next_time, but for the past.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 182 def previous_time(from=ZoTime.now) pt = nil zt = ZoTime.new(from.to_i - 1, @timezone) miny = from.year - NEXT_TIME_MAX_YEARS loop do pt = zt.dup fail RangeError.new( "failed to reach occurrence within " + "#{NEXT_TIME_MAX_YEARS} years for '#{original}'" ) if pt.year < miny unless date_match?(pt) zt.substract(pt.hour * 3600 + pt.min * 60 + pt.sec + 1) next end unless sub_match?(pt, :hour, @hours) zt.substract(pt.min * 60 + pt.sec + 1) next end unless sub_match?(pt, :min, @minutes) zt.substract(pt.sec + 1) next end unless sub_match?(pt, :sec, @seconds) zt.substract(prev_second(pt)) next end break end pt end |
#to_a ⇒ Object Also known as: to_array
Returns an array of 6 arrays (seconds, minutes, hours, days, months, weekdays). This method is mostly used by the cronline specs.
224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/rocket_job/plugins/rufus/cron_line.rb', line 224 def to_a [ toa(@seconds), toa(@minutes), toa(@hours), toa(@days), toa(@months), toa(@weekdays), toa(@monthdays), @timezone.name ] end |