class CronR::Cron

This is an array that is modified to represent a cron table along with the necessary machinery to run it.

cron = Cron.new
# Or
cron = Cron.new(job1,job2,...)

To start cron:

cron.start

Jobs are instances of CronJob (although these are just glorified hashes). In fact, Cron expects job instances to respond to:

runnable?(time) => if we can run now
once? => this job is a one-off

@see CronJob

Cron uses ruby's Time object to establish what the curren time is.

cron.time => current time

You can however override cron's idea of the current time. eg using ActiveSupport time zones:

cron.time {
  Time.use_zone("Sydney") {
    Time.zone.now  
  }
}

cron.time => <the time in new timezone>

To alter cron after it's started (remember, it's just an array):

cron.suspend {|arr|
  ... do something with cron ...
  ... arr == this cron instance ...
  arr << cron_job  # for example
}

suspend will wait for the thread dispatching jobs to be safely sleeping.

To gracefully stop cron:

cron.stop {
   ... do something now we've stopped ...
}

RUNNABLE ITEMS

If a runnable item carries a proc, we don't run it - the thread used in Cron is just for doing cron. So runnable items are basically enqueued to @queue. You can create an thread and have it dequeue this queue.

Thread.new {
  loop {
    job = cron.queue.deq
    job.run  # Using CronJob#run .
    ...
  }
}

You can also replace queue with something. It should respond to enq and deq and be thread-safe. One example might be to have enq insert a record into a table.

Attributes

debug[RW]
mutex[R]
queue[RW]
stopped[R]
suspended[R]
thread[R]
timezone[RW]

Public Class Methods

new(*jobs) click to toggle source

*items should consist of 0 or more items of form:

[job_id(string),CronJob.new(...),thing]
Calls superclass method
# File lib/CronR/Cron.rb, line 92
def initialize *jobs
  super()
  @stopped = true
  @suspended = false
  @queue = Queue.new
  jobs.each{|job|
    self.push(job)
  }
  @mutex = Mutex.new
  @timezone = nil
end

Public Instance Methods

run(time=nil) click to toggle source

Check each item in this array, if runnable push it on a queue.

We assume that this item is or simlar to CronJob. We don't call cron_job#job. That is the work for another thread esp. if job is a Proc.

# File lib/CronR/Cron.rb, line 124
def run time=nil

  puts "[cron] run called #{Time.now}" if @debug
  time = self.time if time.nil?
  self.each { |cron_job|
    ok,details = cron_job.runnable?(time)
    if ok then
      @queue.enq(cron_job)
      if cron_job.once? then
        cron_job[:delete] = true
      end
    end
  }
  self.reject! { |cron_job|
    cron_job[:delete]
  }
end
start(debug=false,method=:every_minute,*args) click to toggle source

Start cron.

Will wake up every minute and perform run.

# File lib/CronR/Cron.rb, line 146
def start debug=false,method=:every_minute,*args
  @stopped = false
  @suspended = false
  @dead = Queue.new
  @thread = CronR::Utils.send(method,debug,*args) {
    time = self.time
    @mutex.synchronize {
      if @stopped then
        # It's important we put something on this queue ONLY AFTER
        # we've acquired the mutex...
        @dead.enq(true)
        true
      elsif @suspended then
      else
        self.run(time)
      end
    }
  }
end
stop(&block) click to toggle source

Gracefully stop the thread.

If block is given, it will be called once the thread has stopped.

# File lib/CronR/Cron.rb, line 190
def stop &block
  if block_given? then
    @stopped = true
    @suspended = false
    # Wait till something is put on the dead queue...
    # This stops us from acquiring the mutex until after @thread
    # has processed @stopped set to true.
    sig = @dead.deq
    # The cron thread should be dead now, or wrapping up (with the
    # acquired mutex)...
    @mutex.synchronize {
      while @thread.alive?
        sleep 0.2
      end
      block.call(self)
    }
  end
end
suspend(&block) click to toggle source

Suspend the thread.

If block is given, it will only be called once the thread is sleeping again. You can use this to safely modify this array.

# File lib/CronR/Cron.rb, line 172
def suspend &block
  if block_given? then
    @mutex.synchronize {
      begin
        @suspended = true
        block.call(self)
      ensure
        @suspended = false
      end
    }
  end
end
time() click to toggle source

Get current time.

If passed a block, the block will be used to get the time.

# File lib/CronR/Cron.rb, line 108
def time
  if @timezone then
    Time.use_zone(@timezone) {
      Time.zone.now
    }
  else
    Time.now
  end
end