pax_global_header00006660000000000000000000000064120774676610014532gustar00rootroot0000000000000052 comment=e1435caf67a79622afab6f497be5f37550fa3942 ruby-timers-1.1.0/000077500000000000000000000000001207746766100140135ustar00rootroot00000000000000ruby-timers-1.1.0/.rspec000066400000000000000000000000771207746766100151340ustar00rootroot00000000000000--color --format documentation --backtrace --default_path spec ruby-timers-1.1.0/CHANGES.md000066400000000000000000000003321207746766100154030ustar00rootroot000000000000001.1.0 ----- * Timers#after_milliseconds and #after_ms for waiting in milliseconds 1.0.2 ----- * Handle overdue timers correctly 1.0.1 ----- * Explicitly require Forwardable from stdlib 1.0.0 ----- * Initial release ruby-timers-1.1.0/Gemfile000066400000000000000000000001331207746766100153030ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in timers.gemspec gemspec ruby-timers-1.1.0/LICENSE000066400000000000000000000020541207746766100150210ustar00rootroot00000000000000Copyright (c) 2012 Tony Arcieri MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.ruby-timers-1.1.0/README.md000066400000000000000000000034531207746766100152770ustar00rootroot00000000000000Timers ====== [![Build Status](https://secure.travis-ci.org/tarcieri/timers.png?branch=master)](http://travis-ci.org/tarcieri/timers) Pure Ruby timer collections. Schedule several procs to fire after configurable delays or at periodic intervals. This gem is especially useful when you are faced with an API that accepts a single timeout but you want to run multiple timers on top of it. An example of such a library is [nio4r](https://github.com/tarcieri/nio4r), a cross-platform Ruby library for using system calls like epoll and kqueue. Usage ----- Create a new timer group with `Timers.new`: ```ruby require 'timers' timers = Timers.new ``` Schedule a proc to run after 5 seconds with `Timers#after`: ```ruby five_second_timer = timers.after(5) { puts "Take five" } ``` The `five_second_timer` variable is now bound to a Timers::Timer object. To cancel a timer, use `Timers::Timer#cancel` Once you've scheduled a timer, you can wait until the next timer fires with `Timers#wait`: ```ruby # Waits 5 seconds timers.wait # The script will now print "Take five" ``` You can schedule a block to run periodically with `Timers#every`: ```ruby every_five_seconds = timers.every(5) { puts "Another 5 seconds" } loop { timers.wait } ``` If you'd like another method to do the waiting for you, e.g. `Kernel.select`, you can use `Timers#wait_interval` to obtain the amount of time to wait. When a timeout is encountered, you can fire all pending timers with `Timers#fire`: ```ruby loop do interval = timers.wait_interval ready_readers, ready_writers = select readers, writers, nil, interval if ready_readers || ready_writers # Handle IO ... else # Timeout! timers.fire end end ``` License ------- Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See LICENSE for further details. ruby-timers-1.1.0/Rakefile000066400000000000000000000002021207746766100154520ustar00rootroot00000000000000#!/usr/bin/env rake require "bundler/gem_tasks" require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task :default => :spec ruby-timers-1.1.0/lib/000077500000000000000000000000001207746766100145615ustar00rootroot00000000000000ruby-timers-1.1.0/lib/timers.rb000066400000000000000000000052731207746766100164200ustar00rootroot00000000000000require 'set' require 'forwardable' require 'timers/version' # Low precision timers implemented in pure Ruby class Timers include Enumerable extend Forwardable def_delegators :@timers, :delete, :each, :empty? def initialize @timers = SortedSet.new end # Call the given block after the given interval def after(interval, &block) Timer.new(self, interval, false, &block) end # Call the given block after the given interval has expired. +interval+ # is measured in milliseconds. # # Timer.new.after_milliseconds(25) { puts "fired!" } # def after_milliseconds(interval, &block) after(interval / 1000.0, &block) end alias_method :after_ms, :after_milliseconds # Call the given block periodically at the given interval def every(interval, &block) Timer.new(self, interval, true, &block) end # Wait for the next timer and fire it def wait i = wait_interval sleep i if i fire end # Interval to wait until when the next timer will fire def wait_interval(now = Time.now) timer = @timers.first return unless timer interval = timer.time - now interval > 0 ? interval : 0 end # Fire all timers that are ready def fire(now = Time.now) time = now + 0.001 # Fudge 1ms in case of clock imprecision while (timer = @timers.first) && (time >= timer.time) @timers.delete timer timer.fire(now) end end def add(timer) raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer @timers.add(timer) end alias_method :cancel, :delete # An individual timer set to fire a given proc at a given time class Timer include Comparable attr_reader :interval, :time, :recurring def initialize(timers, interval, recurring = false, &block) @timers, @interval, @recurring = timers, interval, recurring @block = block @time = nil reset end def <=>(other) @time <=> other.time end # Cancel this timer def cancel @timers.cancel self end # Reset this timer def reset(now = Time.now) @timers.cancel self if @time @time = now + @interval @timers.add self end # Fire the block def fire(now = Time.now) reset(now) if recurring @block.call end alias_method :call, :fire # Inspect a timer def inspect str = "#= now str << "fires in #{@time - now} seconds" else str << "fired #{now - @time} seconds ago" end str << ", recurs every #{interval}" if recurring else str << "dead" end str << ">" end end end ruby-timers-1.1.0/lib/timers/000077500000000000000000000000001207746766100160645ustar00rootroot00000000000000ruby-timers-1.1.0/lib/timers/version.rb000066400000000000000000000000451207746766100200750ustar00rootroot00000000000000class Timers VERSION = "1.1.0" end ruby-timers-1.1.0/spec/000077500000000000000000000000001207746766100147455ustar00rootroot00000000000000ruby-timers-1.1.0/spec/spec_helper.rb000066400000000000000000000000501207746766100175560ustar00rootroot00000000000000require 'bundler/setup' require 'timers'ruby-timers-1.1.0/spec/timers_spec.rb000066400000000000000000000032311207746766100176060ustar00rootroot00000000000000require 'spec_helper' describe Timers do # Level of accuracy enforced by most tests (50ms) Q = 0.05 it "sleeps until the next timer" do interval = Q * 2 started_at = Time.now fired = false subject.after(interval) { fired = true } subject.wait fired.should be_true (Time.now - started_at).should be_within(Q).of interval end it "fires instantly when next timer is in the past" do fired = false subject.after(Q) { fired = true } sleep(Q * 2) subject.wait fired.should be_true end it "calculates the interval until the next timer should fire" do interval = 0.1 subject.after(interval) subject.wait_interval.should be_within(Q).of interval sleep(interval) subject.wait_interval.should be(0) end it "fires timers in the correct order" do result = [] subject.after(Q * 2) { result << :two } subject.after(Q * 3) { result << :three } subject.after(Q * 1) { result << :one } sleep Q * 4 subject.fire result.should == [:one, :two, :three] end describe "recurring timers" do it "continues to fire the timers at each interval" do result = [] subject.every(Q * 2) { result << :foo } sleep Q * 3 subject.fire result.should == [:foo] sleep Q * 5 subject.fire result.should == [:foo, :foo] end end describe "millisecond timers" do it "calculates the proper interval to wait until firing" do interval_ms = 25 subject.after_milliseconds(interval_ms) expected_elapse = subject.wait_interval subject.wait_interval.should be_within(Q).of (interval_ms / 1000.0) end end end ruby-timers-1.1.0/timers.gemspec000066400000000000000000000015021207746766100166610ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/timers/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Tony Arcieri"] gem.email = ["tony.arcieri@gmail.com"] gem.description = "Pure Ruby one-shot and periodic timers" gem.summary = "Schedule procs to run after a certain time, or at periodic intervals, using any API that accepts a timeout" gem.homepage = "https://github.com/tarcieri/timers" gem.files = `git ls-files`.split($\) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "timers" gem.require_paths = ["lib"] gem.version = Timers::VERSION gem.add_development_dependency 'rake' gem.add_development_dependency 'rspec' end