fire_poll 1.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 +5 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +19 -0
- data/fire_poll.gemspec +26 -0
- data/lib/fire_poll.rb +22 -0
- data/lib/fire_poll/version.rb +3 -0
- data/test/test_fire_poll.rb +67 -0
- metadata +137 -0
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --readme README.md
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
(the MIT license)
|
2
|
+
Copyright 2011 by Atomic Object
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
Description
|
2
|
+
===========
|
3
|
+
`FirePoll.poll` is a method for knowing when something is ready. When your block yields true, execution continues. When your block yields false, poll keeps trying until it gives up and raises an error.
|
4
|
+
|
5
|
+
Examples
|
6
|
+
--------
|
7
|
+
I'm writing a system test for a web application. My test simulates uploading a large file, which isn't instantaneous. I need to know when the file has finished uploading so I can start making assertions.
|
8
|
+
def wait_for_file(filename)
|
9
|
+
FirePoll.poll do
|
10
|
+
FileStore.file_ready?(filename)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_files_are_saved
|
15
|
+
upload_file "blue.txt"
|
16
|
+
assert_nothing_raised { wait_for_file "blue.txt" }
|
17
|
+
assert_equal "blue is the best color", read_saved_file("blue.txt")
|
18
|
+
end
|
19
|
+
|
20
|
+
I just fired up a fake web service to respond to my client application. I want to know when the service is online before I start the client tests. I'm willing to wait 10 seconds before giving up.
|
21
|
+
class TestHelper
|
22
|
+
include FirePoll
|
23
|
+
|
24
|
+
def wait_for_server
|
25
|
+
poll("server didn't come online quick enough", 10) do
|
26
|
+
begin
|
27
|
+
TCPSocket.new(SERVER_IP, SERVER_PORT)
|
28
|
+
true
|
29
|
+
rescue Exception
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Usage
|
37
|
+
-----
|
38
|
+
Pass a block to `FirePoll.poll`. Return `true` when your need is met. Return `false` when it isn't. `poll` will raise an exception after too many failed attempts.
|
39
|
+
|
40
|
+
The `poll` method takes two optional parameters: a specific message to raise on failure and the number of seconds to wait. By default, `poll` will try for two seconds. `poll` runs every tenth of a second.
|
41
|
+
FirePoll.poll { ... } # raises an error after two seconds
|
42
|
+
FirePoll.poll("new data hasn't arrived from the device") { ... } # raises a friendlier error message
|
43
|
+
FirePoll.poll("waited for too long!", 7) { ... } # raises an error after seven seconds with a specific error message
|
44
|
+
FirePoll.poll(nil, 88) { ... } # raises an error after eighty-eight seconds with the generic error message
|
45
|
+
|
46
|
+
The `FirePoll` module may be mixed into your class; this makes it a little faster to type the method name.
|
47
|
+
class TestHelper
|
48
|
+
include FirePoll
|
49
|
+
def helper_method
|
50
|
+
poll do
|
51
|
+
...
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Implementation
|
57
|
+
--------------
|
58
|
+
`FirePoll.poll`'s implementation isn't partcilarly accurate with respect to time. The method will run your block (number of seconds * 10) times. It sleeps for a tenth of a second between attempts. Since it doesn't keep track of time, if your timing needs require accuracy, you'll need to look elsewhere.
|
59
|
+
|
60
|
+
Motivation
|
61
|
+
----------
|
62
|
+
We frequently need to wait for something to happen - usually in tests. And we usually don't have any strict time requirements - as long as something happens in _about_ [x] seconds, we're happy. `FirePoll.poll` meets our need nicely.
|
63
|
+
|
64
|
+
On a related note, `Timer::Timeout` is known to be [busted](http://ph7spot.com/musings/system-timer) and [unreliable](http://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html). `FirePoll.poll` doesn't employ any threads or timers, so we don't worry about whether it will work or not.
|
65
|
+
|
66
|
+
TODO
|
67
|
+
----
|
68
|
+
* Nice to have: hook into Test::Unit and RSpec instead of raising a Ruby exception
|
69
|
+
* Nice to have: pass options as a hash instead of two parameters. This will look nice with Ruby 1.9's hash syntax.
|
70
|
+
|
71
|
+
Authors
|
72
|
+
=======
|
73
|
+
* Matt Fletcher (fletcher@atomicobject.com)
|
74
|
+
* David Crosby (crosby@atomicobject.com)
|
75
|
+
* Micah Alles (alles@atomicobject.com)
|
76
|
+
* © 2011 [Atomic Object](http://www.atomicobject.com/)
|
77
|
+
* More Atomic Object [open source](http://www.atomicobject.com/pages/Software+Commons) projects
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "rake/clean"
|
4
|
+
require "yard"
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.test_files = FileList["test/test*.rb"]
|
9
|
+
end
|
10
|
+
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
t.files = %w[ lib/fire_poll.rb ]
|
13
|
+
end
|
14
|
+
|
15
|
+
CLEAN.include "pkg"
|
16
|
+
CLEAN.include "doc"
|
17
|
+
CLEAN.include ".yardoc"
|
18
|
+
|
19
|
+
task :default => :test
|
data/fire_poll.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fire_poll/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fire_poll"
|
7
|
+
s.version = FirePoll::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Matt Fletcher", "David Crosby", "Micah Alles"]
|
10
|
+
s.email = ["fletcher@atomicobject.com", "crosby@atomicobject.com", "alles@atomicobject.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Simple, brute-force method for knowing when something is ready}
|
13
|
+
s.description = %q{Simple, brute-force method for knowing when something is ready}
|
14
|
+
|
15
|
+
s.rubyforge_project = "fire_poll"
|
16
|
+
|
17
|
+
s.add_development_dependency("bundler", ">= 1.0.0")
|
18
|
+
s.add_development_dependency("rake", ">= 0.8.0")
|
19
|
+
s.add_development_dependency("yard", "~> 0.6.4")
|
20
|
+
s.add_development_dependency("bluecloth", "~> 2.0.11")
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
data/lib/fire_poll.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# see the {file:README.md README file} for instruction on how to use this library
|
2
|
+
module FirePoll
|
3
|
+
# @param [String] msg a custom message raised when polling fails
|
4
|
+
# @param [Numeric] seconds number of seconds to poll
|
5
|
+
# @yield a block that determines whether polling should continue
|
6
|
+
# @yieldreturn false if polling should continue
|
7
|
+
# @yieldreturn true if polling is complete
|
8
|
+
# @raise [RuntimeError] when polling fails
|
9
|
+
# @return [NilClass]
|
10
|
+
# @since 1.0.0
|
11
|
+
def poll(msg=nil, seconds=2.0)
|
12
|
+
(seconds * 10).to_i.times do
|
13
|
+
return if yield
|
14
|
+
sleep 0.1
|
15
|
+
end
|
16
|
+
msg ||= "polling failed after #{seconds} seconds"
|
17
|
+
raise msg
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :poll
|
22
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "fire_poll"
|
3
|
+
|
4
|
+
class FirePollTest < Test::Unit::TestCase
|
5
|
+
def test_raises_when_block_does_not_yield_true
|
6
|
+
assert_raise RuntimeError do
|
7
|
+
FirePoll.poll { false }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_does_not_raise_when_block_yields_true
|
12
|
+
assert_nothing_raised do
|
13
|
+
FirePoll.poll { true }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_does_not_raise_when_block_eventually_yields_true
|
18
|
+
assert_nothing_raised do
|
19
|
+
count = 0
|
20
|
+
FirePoll.poll do
|
21
|
+
count += 1
|
22
|
+
count > 5 ? true : false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_can_take_an_optional_number_of_seconds_to_poll
|
28
|
+
# default 2 seconds = 20 calls to the block
|
29
|
+
assert_nothing_raised do
|
30
|
+
count1 = 0
|
31
|
+
FirePoll.poll do
|
32
|
+
count1 += 1
|
33
|
+
count1 > 11 ? true : false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# 1 second = 10 calls to the block
|
38
|
+
assert_raise RuntimeError do
|
39
|
+
count = 0
|
40
|
+
FirePoll.poll(nil, 1) do
|
41
|
+
count += 1
|
42
|
+
count > 11 ? true : false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_can_take_an_optional_message
|
48
|
+
# anyone know a better way to do this with straight-up Test::Unit?
|
49
|
+
begin
|
50
|
+
FirePoll.poll("custom message") { false }
|
51
|
+
rescue RuntimeError => ex
|
52
|
+
assert_equal "custom message", ex.message
|
53
|
+
return
|
54
|
+
end
|
55
|
+
flunk "should have raised"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class TestFirePollMixin < Test::Unit::TestCase
|
60
|
+
include FirePoll
|
61
|
+
|
62
|
+
def test_can_be_mixed_in
|
63
|
+
assert_nothing_raised do
|
64
|
+
poll { true }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fire_poll
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Matt Fletcher
|
13
|
+
- David Crosby
|
14
|
+
- Micah Alles
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-02-23 00:00:00 -05:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: bundler
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rake
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 8
|
48
|
+
- 0
|
49
|
+
version: 0.8.0
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: yard
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
- 6
|
63
|
+
- 4
|
64
|
+
version: 0.6.4
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: bluecloth
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 2
|
77
|
+
- 0
|
78
|
+
- 11
|
79
|
+
version: 2.0.11
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
82
|
+
description: Simple, brute-force method for knowing when something is ready
|
83
|
+
email:
|
84
|
+
- fletcher@atomicobject.com
|
85
|
+
- crosby@atomicobject.com
|
86
|
+
- alles@atomicobject.com
|
87
|
+
executables: []
|
88
|
+
|
89
|
+
extensions: []
|
90
|
+
|
91
|
+
extra_rdoc_files: []
|
92
|
+
|
93
|
+
files:
|
94
|
+
- .gitignore
|
95
|
+
- .yardopts
|
96
|
+
- Gemfile
|
97
|
+
- LICENSE
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- fire_poll.gemspec
|
101
|
+
- lib/fire_poll.rb
|
102
|
+
- lib/fire_poll/version.rb
|
103
|
+
- test/test_fire_poll.rb
|
104
|
+
has_rdoc: true
|
105
|
+
homepage: ""
|
106
|
+
licenses: []
|
107
|
+
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
requirements: []
|
130
|
+
|
131
|
+
rubyforge_project: fire_poll
|
132
|
+
rubygems_version: 1.3.7
|
133
|
+
signing_key:
|
134
|
+
specification_version: 3
|
135
|
+
summary: Simple, brute-force method for knowing when something is ready
|
136
|
+
test_files:
|
137
|
+
- test/test_fire_poll.rb
|