require 'set' require 'forwardable' require 'hitimes' require 'timers/timer' require 'timers/events' module Timers class Group include Enumerable extend Forwardable def_delegators :@timers, :each, :empty? def initialize @events = Events.new @timers = Set.new @paused_timers = Set.new @interval = Hitimes::Interval.new @interval.start end # Scheduled events: attr :events # Active timers: attr :timers # Paused timers: attr :paused_timers # Call the given block after the given interval. The first argument will be # the time at which the group was asked to fire timers for. def after(interval, &block) Timer.new(self, interval, false, &block) end # Call the given block immediately, and then after the given interval. The first # argument will be the time at which the group was asked to fire timers for. def now_and_after(interval, &block) block.call after(interval, &block) end # Call the given block periodically at the given interval. The first # argument will be the time at which the group was asked to fire timers for. def every(interval, recur = true, &block) Timer.new(self, interval, recur, &block) end # Call the given block immediately, and then periodically at the given interval. The first # argument will be the time at which the group was asked to fire timers for. def now_and_every(interval, recur = true, &block) block.call every(interval, recur, &block) end # Wait for the next timer and fire it. Can take a block, which should behave # like sleep(n), except that n may be nil (sleep forever) or a negative # number (fire immediately after return). def wait(&block) if block_given? yield wait_interval while interval = wait_interval and interval > 0 yield interval end else while interval = wait_interval and interval > 0 # We cannot assume that sleep will wait for the specified time, it might be +/- a bit. sleep interval end end return fire end # Interval to wait until when the next timer will fire. # - nil: no timers # - -ve: timers expired already # - 0: timers ready to fire # - +ve: timers waiting to fire def wait_interval(offset = self.current_offset) if handle = @events.first return handle.time - Float(offset) end end # Fire all timers that are ready. def fire(offset = self.current_offset) @events.fire(offset) end # Pause all timers. def pause @timers.dup.each do |timer| timer.pause end end # Resume all timers. def resume @paused_timers.dup.each do |timer| timer.resume end end alias_method :continue, :resume # Delay all timers. def delay(seconds) @timers.each do |timer| timer.delay(seconds) end end # Cancel all timers. def cancel @timers.dup.each do |timer| timer.cancel end end # The group's current time. def current_offset @interval.to_f end end end