timers 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --default_path spec
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.3
4
+ - ree
5
+ - ruby-head
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - jruby-head
9
+
10
+ # Sporadically failing :(
11
+ # - rbx-18mode
12
+ # - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in timers.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tony Arcieri
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ Timers
2
+ ======
3
+ [![Build Status](https://secure.travis-ci.org/tarcieri/timers.png?branch=master)](http://travis-ci.org/tarcieri/timers)
4
+
5
+ Pure Ruby timer collections. Schedule several procs to fire after configurable
6
+ delays or at periodic intervals.
7
+
8
+ This gem is especially useful when you are faced with an API that accepts a
9
+ single timeout but you want to run multiple timers on top of it. An example of
10
+ such a library is [nio4r](https://github.com/tarcieri/nio4r), a cross-platform
11
+ Ruby library for using system calls like epoll and kqueue.
12
+
13
+ Usage
14
+ -----
15
+
16
+ Create a new timer group with `Timers.new`:
17
+
18
+ ```ruby
19
+ require 'timers'
20
+
21
+ timers = Timers.new
22
+ ```
23
+
24
+ Schedule a proc to run after 5 seconds with `Timers#after`:
25
+
26
+ ```ruby
27
+ five_second_timer = timers.after(5) { puts "Take five" }
28
+ ```
29
+
30
+ The `five_second_timer` variable is now bound to a Timers::Timer object. To
31
+ cancel a timer, use `Timers::Timer#cancel`
32
+
33
+ Once you've scheduled a timer, you can wait until the next timer fires with `Timers#wait`:
34
+
35
+ ```ruby
36
+ # Waits 5 seconds
37
+ timers.wait
38
+
39
+ # The script will now print "Take five"
40
+ ```
41
+
42
+ You can schedule a block to run periodically with `Timers#every`:
43
+
44
+ ```ruby
45
+ every_five_seconds = timers.every(5) { puts "Another 5 seconds" }
46
+
47
+ loop { timers.wait }
48
+ ```
49
+
50
+ If you'd like another method to do the waiting for you, e.g. `Kernel.select`,
51
+ you can use `Timers#wait_interval` to obtain the amount of time to wait. When
52
+ a timeout is encountered, you can fire all pending timers with `Timers#fire`:
53
+
54
+ ```ruby
55
+ loop do
56
+ interval = timers.wait_interval
57
+ ready_readers, ready_writers = select readers, writers, nil, interval
58
+
59
+ if ready_readers || ready_writers
60
+ # Handle IO
61
+ ...
62
+ else
63
+ # Timeout!
64
+ timers.fire
65
+ end
66
+ end
67
+ ```
68
+
69
+ License
70
+ -------
71
+
72
+ Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See
73
+ LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
data/lib/timers.rb ADDED
@@ -0,0 +1,115 @@
1
+ require "timers/version"
2
+
3
+ # Low precision timers implemented in pure Ruby
4
+ class Timers
5
+ def initialize
6
+ @timers = []
7
+ end
8
+
9
+ # Call the given block after the given interval
10
+ def after(interval, &block)
11
+ Timer.new(self, interval, false, block)
12
+ end
13
+
14
+ # Call the given block periodically at the given interval
15
+ def every(interval, &block)
16
+ Timer.new(self, interval, true, block)
17
+ end
18
+
19
+ # Wait for the next timer and fire it
20
+ def wait
21
+ return if @timers.empty?
22
+
23
+ interval = wait_interval
24
+ sleep interval if interval >= Timer::QUANTUM
25
+ fire
26
+ end
27
+
28
+ # Interval to wait until when the next timer will fire
29
+ def wait_interval
30
+ @timers.first.time - Time.now unless empty?
31
+ end
32
+
33
+ # Fire all timers that are ready
34
+ def fire
35
+ return if @timers.empty?
36
+
37
+ time = Time.now + Timer::QUANTUM
38
+ while not empty? and time > @timers.first.time
39
+ timer = @timers.shift
40
+ timer.call
41
+ end
42
+ end
43
+
44
+ # Insert a timer into the active timers
45
+ def insert(timer)
46
+ @timers.insert(index(timer), timer)
47
+ end
48
+
49
+ # Remove a given timer from the set we're monitoring
50
+ def cancel(timer)
51
+ @timers.delete timer
52
+ end
53
+
54
+ # Are there any timers pending?
55
+ def empty?
56
+ @timers.empty?
57
+ end
58
+
59
+ # Index where a timer would be located in the sorted timers array
60
+ def index(timer)
61
+ l, r = 0, @timers.size - 1
62
+
63
+ while l <= r
64
+ m = (r + l) / 2
65
+ if timer < @timers.at(m)
66
+ r = m - 1
67
+ else
68
+ l = m + 1
69
+ end
70
+ end
71
+ l
72
+ end
73
+
74
+ # An individual timer set to fire a given proc at a given time
75
+ class Timer
76
+ include Comparable
77
+
78
+ # The timer system is guaranteed (at least by the specs) to be this precise
79
+ # during normal operation. Long blocking calls within actors will delay the
80
+ # firing of timers
81
+ QUANTUM = 0.02
82
+
83
+ attr_reader :interval, :time, :recurring
84
+
85
+ def initialize(timers, interval, recurring, block)
86
+ @timers, @interval, @recurring = timers, interval, recurring
87
+ @block = block
88
+
89
+ reset
90
+ end
91
+
92
+ def <=>(other)
93
+ @time <=> other.time
94
+ end
95
+
96
+ # Cancel this timer
97
+ def cancel
98
+ @timers.cancel self
99
+ end
100
+
101
+ # Reset this timer
102
+ def reset
103
+ @timers.cancel self if defined?(@time)
104
+ @time = Time.now + @interval
105
+ @timers.insert self
106
+ end
107
+
108
+ # Fire the block
109
+ def fire
110
+ reset if recurring
111
+ @block.call
112
+ end
113
+ alias_method :call, :fire
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ class Timers
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler/setup'
2
+ require 'timers'
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Timers do
4
+ Q = Timers::Timer::QUANTUM
5
+
6
+ it "sleeps until the next timer" do
7
+ interval = 0.1
8
+ started_at = Time.now
9
+
10
+ fired = false
11
+ subject.after(interval) { fired = true }
12
+ subject.wait
13
+
14
+ fired.should be_true
15
+ (Time.now - started_at).should be_within(Q).of interval
16
+ end
17
+
18
+ it "it calculates the interval until the next timer should fire" do
19
+ interval = 0.1
20
+
21
+ subject.after(interval)
22
+ subject.wait_interval.should be_within(Q).of interval
23
+ end
24
+
25
+ it "fires timers in the correct order" do
26
+ result = []
27
+
28
+ subject.after(Q * 2) { result << :two }
29
+ subject.after(Q * 3) { result << :three }
30
+ subject.after(Q * 1) { result << :one }
31
+
32
+ sleep Q * 4
33
+ subject.fire
34
+
35
+ result.should == [:one, :two, :three]
36
+ end
37
+
38
+ describe "recurring timers" do
39
+ it "should continue to fire the timers at each interval" do
40
+ result = []
41
+
42
+ subject.every(Q * 3) { result << :foo }
43
+
44
+ sleep Q * 3
45
+ subject.fire
46
+ result.should == [:foo]
47
+
48
+ sleep Q * 3
49
+ subject.fire
50
+ subject.fire
51
+ result.should == [:foo, :foo]
52
+ end
53
+ end
54
+ end
data/timers.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/timers/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tony Arcieri"]
6
+ gem.email = ["tony.arcieri@gmail.com"]
7
+ gem.description = "Pure Ruby one-shot and periodic timers"
8
+ gem.summary = "Schedule procs to run after a certain time, or at periodic intervals, using any API that accepts a timeout"
9
+ gem.homepage = "https://github.com/tarcieri/timers"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "timers"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Timers::VERSION
17
+
18
+ gem.add_development_dependency 'rake'
19
+ gem.add_development_dependency 'rspec'
20
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: timers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tony Arcieri
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70212434293540 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70212434293540
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70212434292840 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70212434292840
36
+ description: Pure Ruby one-shot and periodic timers
37
+ email:
38
+ - tony.arcieri@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .rspec
45
+ - .travis.yml
46
+ - Gemfile
47
+ - LICENSE
48
+ - README.md
49
+ - Rakefile
50
+ - lib/timers.rb
51
+ - lib/timers/version.rb
52
+ - spec/spec_helper.rb
53
+ - spec/timers_spec.rb
54
+ - timers.gemspec
55
+ homepage: https://github.com/tarcieri/timers
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.10
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Schedule procs to run after a certain time, or at periodic intervals, using
79
+ any API that accepts a timeout
80
+ test_files:
81
+ - spec/spec_helper.rb
82
+ - spec/timers_spec.rb
83
+ has_rdoc: