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
#--