lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.17 vs lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.18

- old
+ new

@@ -1,7 +1,7 @@ #-- -# Copyright (c) 2006-2012, John Mettraux, jmettraux@gmail.com +# Copyright (c) 2006-2013, John Mettraux, jmettraux@gmail.com # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell @@ -67,23 +67,30 @@ @minutes = parse_item(items[0 + offset], 0, 59) @hours = parse_item(items[1 + offset], 0, 24) @days = parse_item(items[2 + offset], 1, 31) @months = parse_item(items[3 + offset], 1, 12) @weekdays, @monthdays = parse_weekdays(items[4 + offset]) + + [ @seconds, @minutes, @hours, @months ].each do |es| + + raise ArgumentError.new( + "invalid cronline: '#{line}'" + ) if es && es.find { |e| ! e.is_a?(Integer) } + end end # Returns true if the given time matches this cron line. # def matches?(time) time = Time.at(time) unless time.kind_of?(Time) time = @timezone.utc_to_local(time.getutc) if @timezone - return false unless sub_match?(time.sec, @seconds) - return false unless sub_match?(time.min, @minutes) - return false unless sub_match?(time.hour, @hours) + return false unless sub_match?(time, :sec, @seconds) + return false unless sub_match?(time, :min, @minutes) + return false unless sub_match?(time, :hour, @hours) return false unless date_match?(time) true end # Returns the next time that this cron line is supposed to 'fire' @@ -124,19 +131,19 @@ unless date_match?(time) time += (24 - time.hour) * 3600 - time.min * 60 - time.sec next end - unless sub_match?(time.hour, @hours) + unless sub_match?(time, :hour, @hours) time += (60 - time.min) * 60 - time.sec next end - unless sub_match?(time.min, @minutes) + unless sub_match?(time, :min, @minutes) time += 60 - time.sec next end - unless sub_match?(time.sec, @seconds) + unless sub_match?(time, :sec, @seconds) time += 1 next end break @@ -190,14 +197,18 @@ ) if it.index('-') (monthdays ||= []) << it else + expr = it.dup + WEEKDAYS.each_with_index { |a, i| expr.gsub!(/#{a}/, i.to_s) } - WEEKDAYS.each_with_index { |a, i| it.gsub!(/#{a}/, i.to_s) } + raise ArgumentError.new( + "invalid weekday expression (#{it})" + ) if expr !~ /^0*[0-7](-0*[0-7])?$/ - its = it.index('-') ? parse_range(it, 0, 7) : [ Integer(it) ] + its = expr.index('-') ? parse_range(expr, 0, 7) : [ Integer(expr) ] its = its.collect { |i| i == 7 ? 0 : i } (weekdays ||= []).concat(its) end end @@ -208,12 +219,13 @@ end def parse_item(item, min, max) return nil if item == '*' + return [ 'L' ] if item == 'L' return parse_list(item, min, max) if item.index(',') - return parse_range(item, min, max) if item.index('*') or item.index('-') + return parse_range(item, min, max) if item.match(/[*-\/]/) i = item.to_i i = min if i < min i = max if i > max @@ -221,37 +233,41 @@ [ i ] end def parse_list(item, min, max) - item.split(',').inject([]) { |r, i| - r.push(parse_range(i, min, max)) - }.flatten + l = item.split(',').collect { |i| parse_range(i, min, max) }.flatten + + raise ArgumentError.new( + "found duplicates in #{item.inspect}" + ) if l.uniq.size < l.size + + l end def parse_range(item, min, max) - i = item.index('-') - j = item.index('/') + dash = item.index('-') + slash = item.index('/') - return item.to_i if (not i and not j) + return parse_item(item, min, max) if (not slash) and (not dash) - inc = j ? item[j + 1..-1].to_i : 1 + raise ArgumentError.new( + "'L' (end of month) is not accepted in ranges, " + + "#{item.inspect} is not valid" + ) if item.index('L') + inc = slash ? item[slash + 1..-1].to_i : 1 + istart = -1 iend = -1 - if i + if dash - istart = item[0..i - 1].to_i + istart = item[0..dash - 1].to_i + iend = (slash ? item[dash + 1..slash - 1] : item[dash + 1..-1]).to_i - iend = if j - item[i + 1..j - 1].to_i - else - item[i + 1..-1].to_i - end - else # case */x istart = min iend = max end @@ -269,25 +285,29 @@ end result end - def sub_match?(value, values) + def sub_match?(time, accessor, values=:none) - values.nil? || values.include?(value) - end + value, values = + if values == :none + [ time, accessor ] + else + [ time.send(accessor), values ] + end - def monthday_match(monthday, monthdays) + return true if values.nil? + return true if values.include?('L') && (time + 24 * 3600).day == 1 - return true if monthdays == nil - return true if monthdays.include?(monthday) + values.include?(value) end def date_match?(date) - return false unless sub_match?(date.day, @days) - return false unless sub_match?(date.month, @months) - return false unless sub_match?(date.wday, @weekdays) + return false unless sub_match?(date, :day, @days) + return false unless sub_match?(date, :month, @months) + return false unless sub_match?(date, :wday, @weekdays) return false unless sub_match?(CronLine.monthday(date), @monthdays) true end DAY_IN_SECONDS = 7 * 24 * 3600