class PeriodicScheduler

Public Class Methods

new(quantum = 5.0, options = {}) click to toggle source
# File lib/periodic-scheduler.rb, line 82
def initialize(quantum = 5.0, options = {})
  time_source = (options[:time_source] or lambda {Time.now.to_f})
  wait_function = (options[:wait_function] or lambda{|t| sleep t})

  @quantized_space = RealTimeToQuantizedSpaceProjection.new(
    quantum,
    lambda {|v| v.ceil} # behave like sleep - never execute too early
  )
  @time_source = time_source
  @wait_function = wait_function

  @events = {}
end

Public Instance Methods

after(period, &callback) click to toggle source
# File lib/periodic-scheduler.rb, line 96
def after(period, &callback)
        schedule_event Event.new(@quantized_space, real_now, period, false, &callback)
end
empty?() click to toggle source
# File lib/periodic-scheduler.rb, line 156
def empty?
              @events.empty?
end
every(period, &callback) click to toggle source
# File lib/periodic-scheduler.rb, line 100
def every(period, &callback)
        schedule_event Event.new(@quantized_space, real_now, period, true, &callback)
end
run() { |error| ... } click to toggle source
# File lib/periodic-scheduler.rb, line 113
def run
              earliest_quant = @events.keys.sort.first
              raise EmptyScheduleError, "no events scheduled" unless earliest_quant

  wait_time = @quantized_space.revers_project(earliest_quant) - real_now
              wait_time = 0 if wait_time < 0
  wait(wait_time)

              objects = []

  qnow = quantized_now

              # move quants to be run away to separate array
  quants = @events.keys.select{|k| k <= qnow}.sort.map{|q| @events.delete(q)}

              # we have missed one or more scheduled quants
              if quants.length > 1
    begin
      # we raise it so it has proper backtrace
      raise MissedScheduleError.new("missed schedule by #{-wait_time} seconds")
    rescue StandardError => error
                              yield error if block_given?
    end
  end

              # Call callback for every quant and reschedule if needed
  quants.each do |events|
                      # get all events for quantum that are not stopped
    events.each do |e|
      begin
        objects << e.call
      rescue StandardError => error
                                      # Yield errors to block
                                      yield error if block_given?
      end
      e.reschedule(quantized_now) if e.keep? and not e.stopped?
    end
  end

              # return collected callabck return objects
              objects
end
run!(&block) click to toggle source
# File lib/periodic-scheduler.rb, line 104
def run!(&block)
  begin
    loop do
      run(&block)
    end
  rescue EmptyScheduleError
  end
end

Private Instance Methods

quantized_now() click to toggle source
# File lib/periodic-scheduler.rb, line 193
def quantized_now
  @quantized_space.project(real_now)
end
real_now() click to toggle source
# File lib/periodic-scheduler.rb, line 189
def real_now
  @time_source.call
end
schedule_event(event) click to toggle source
# File lib/periodic-scheduler.rb, line 162
def schedule_event(event)
        quant = event.quantum_period
        (@events[quant] ||= []) << event

        event.reschedule_hook do |event|
                unschedule_event(event, quant)
                schedule_event(event)
        end

        event.stop_hook do |event|
                unschedule_event(event, quant)
        end

        event
end
unschedule_event(event, quant) click to toggle source
# File lib/periodic-scheduler.rb, line 178
def unschedule_event(event, quant)
        return unless @events[quant]
        @events[quant].delete(event)
        @events.delete(quant) if @events[quant].empty?
end
wait(time) click to toggle source
# File lib/periodic-scheduler.rb, line 184
def wait(time)
  fail "time must be a positive number" if time < 0
  @wait_function.call(time)
end