lib/rufus/scheduler.rb in rufus-scheduler-1.0.6 vs lib/rufus/scheduler.rb in rufus-scheduler-1.0.7

- old
+ new

@@ -62,10 +62,11 @@ # == Examples # # require 'rubygems' # require 'rufus/scheduler' # + # scheduler = Rufus::Scheduler.start_new # # scheduler.schedule_in("3d") do # regenerate_monthly_report() # end # # @@ -178,10 +179,30 @@ # # The rufus scheduler recognizes an optional first column for second # scheduling. This column can, like for the other columns, specify a # value ("7"), a list of values ("7,8,9,27") or a range ("7-12"). # + # + # == information passed to schedule blocks + # + # When calling schedule_every(), schedule_in() or schedule_at(), the block + # expects zero or 3 parameters like in + # + # scheduler.schedule_every("1h20m") do |job_id, at, params| + # puts "my job_id is #{job_id}" + # end + # + # For schedule(), zero or two parameters can get passed + # + # scheduler.schedule "7 * * * * *" do |job_id, cron_line, params| + # puts "my job_id is #{job_id}" + # end + # + # In both cases, params corresponds to the params passed to the schedule + # method (:tags, :first_at, :first_in, :dont_reschedule, ...) + # + # # == Exceptions # # The rufus scheduler will output a stacktrace to the STDOUT in # case of exception. There are two ways to change that behaviour. # @@ -240,10 +261,27 @@ # # scheduler.schedule_every "2d", :first_at => "5h" do # # schedule something every two days, start in 5 hours... # end # + # == job.next_time() + # + # Jobs, be they at, every or cron have a next_time() method, which tells + # when the job will be fired next time (for at and in jobs, this is also the + # last time). + # + # For cron jobs, the current implementation is quite brutal. It takes three + # seconds on my 2006 macbook to reach a cron schedule 1 year away. + # + # When is the next friday 13th ? + # + # require 'rubygems' + # require 'rufus/scheduler' + # + # puts Rufus::CronLine.new("* * 13 * fri").next_time + # + # # == :thread_name option # # You can specify the name of the scheduler's thread. Should make # it easier in some debugging situations. # @@ -347,10 +385,20 @@ end end end # + # Instantiates a new Rufus::Scheduler instance, starts it and returns it + # + def self.start_new + + s = self.new + s.start + s + end + + # # The scheduler is stoppable via sstop() # def stop @stopped = true @@ -610,16 +658,11 @@ # Returns the job corresponding to job_id, an instance of AtJob # or CronJob will be returned. # def get_job (job_id) - job = @cron_jobs[job_id] - return job if job - - @pending_jobs.find do |job| - job.job_id == job_id - end + @cron_jobs[job_id] || @pending_jobs.find { |job| job.job_id == job_id } end # # Finds a job (via get_job()) and then returns the wrapped # schedulable if any. @@ -638,39 +681,23 @@ # # Returns an array of jobs that have the given tag. # def find_jobs (tag) - result = @cron_jobs.values.find_all do |job| - job.has_tag?(tag) - end - - result + @pending_jobs.find_all do |job| - job.has_tag?(tag) - end + @cron_jobs.values.find_all { |job| job.has_tag?(tag) } + + @pending_jobs.find_all { |job| job.has_tag?(tag) } end # # Finds the jobs with the given tag and then returns an array of # the wrapped Schedulable objects. # Jobs that haven't a wrapped Schedulable won't be included in the # result. # def find_schedulables (tag) - #jobs = find_jobs(tag) - #result = [] - #jobs.each do |job| - # result.push(job.schedulable) if job.respond_to?(:schedulable) - #end - #result - - find_jobs(tags).inject([]) do |result, job| - - result.push(job.schedulable) if job.respond_to?(:schedulable) - result - end + find_jobs(tag).find_all { |job| job.respond_to?(:schedulable) } end # # Returns the number of currently pending jobs in this scheduler # ('at' jobs and 'every' jobs). @@ -707,14 +734,13 @@ # # Returns true if the given string seems to be a cron string. # def Scheduler.is_cron_string (s) - s.match ".+ .+ .+ .+ .+" + s.match ".+ .+ .+ .+ .+" # well... end - #protected private def do_unschedule (job_id) for i in 0...@pending_jobs.length @@ -1156,10 +1182,19 @@ # def schedule_info Time.at(@at) end + + # + # next_time is last_time (except for EveryJob instances). Returns + # a Time instance. + # + def next_time + + schedule_info + end end # # An 'every' job is simply an extension of an 'at' job. # @@ -1220,21 +1255,33 @@ # # As the name implies. # def trigger - @block.call @job_id, @cron_line + @block.call @job_id, @cron_line, @params end # # Returns the original cron tab string used to schedule this # Job. Like for example "60/3 * * * Sun". # def schedule_info @cron_line.original end + + # + # Returns a Time instance : the next time this cron job is + # supposed to "fire". + # + # 'from' is used to specify the starting point for determining + # what will be the next time. Defaults to now. + # + def next_time (from=Time.now) + + @cron_line.next_time(from) + end end # # A 'cron line' is a line in the sense of a crontab # (man 5 crontab) file line. @@ -1291,12 +1338,11 @@ # worth checking seconds and minutes) # def matches? (time) #def matches? (time, precision) - time = Time.at(time) \ - if time.kind_of?(Float) or time.kind_of?(Integer) + time = Time.at(time) unless time.kind_of?(Time) return false \ if no_match?(time.sec, @seconds) #if precision <= 1 and no_match?(time.sec, @seconds) return false \ @@ -1318,10 +1364,71 @@ # Returns an array of 6 arrays (seconds, minutes, hours, days, # months, weekdays). # This method is used by the cronline unit tests. # def to_array + [ @seconds, @minutes, @hours, @days, @months, @weekdays ] + end + + # + # 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. + # + def next_time (now = Time.now) + + # + # position now to the next cron second + + if @seconds + next_sec = @seconds.find { |s| s > now.sec } || 60 + @seconds.first + now += next_sec - now.sec + else + now += 1 + end + + # + # prepare sec jump array + + sjarray = nil + + if @seconds + + sjarray = [] + + i = @seconds.index(now.sec) + ii = i + + loop do + cur = @seconds[ii] + ii += 1 + ii = 0 if ii == @seconds.size + nxt = @seconds[ii] + nxt += 60 if ii == 0 + sjarray << (nxt - cur) + break if ii == i + end + + else + + sjarray = [ 1 ] + end + + # + # ok, seek... + + i = 0 + + loop do + return now if matches?(now) + now += sjarray[i] + i += 1 + i = 0 if i == sjarray.size + # danger... potentially no exit... + end + + nil end private #--