sane_timeout 0.1
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/LICENSE.txt +22 -0
- data/Rakefile +1 -0
- data/lib/sane_timeout.rb +110 -0
- data/readme.md +46 -0
- data/sane_timeout.gemspec +18 -0
- data/test/test_sane_timeout.rb +136 -0
- data/test/test_timeout.rb +177 -0
- metadata +55 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 John Joseph Bachir
|
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/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/sane_timeout.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Timeout long-running blocks
|
2
|
+
#
|
3
|
+
# == Synopsis
|
4
|
+
#
|
5
|
+
# require 'timeout'
|
6
|
+
# status = Timeout::timeout(5) {
|
7
|
+
# # Something that should be interrupted if it takes more than 5 seconds...
|
8
|
+
# }
|
9
|
+
#
|
10
|
+
# == Description
|
11
|
+
#
|
12
|
+
# Timeout provides a way to auto-terminate a potentially long-running
|
13
|
+
# operation if it hasn't finished in a fixed amount of time.
|
14
|
+
#
|
15
|
+
# Previous versions didn't use a module for namespacing, however
|
16
|
+
# #timeout is provided for backwards compatibility. You
|
17
|
+
# should prefer Timeout#timeout instead.
|
18
|
+
#
|
19
|
+
# == Copyright
|
20
|
+
#
|
21
|
+
# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
|
22
|
+
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
|
23
|
+
|
24
|
+
module Timeout
|
25
|
+
# Raised by Timeout#timeout when the block times out.
|
26
|
+
class Error < RuntimeError
|
27
|
+
end
|
28
|
+
class ExitException < ::Exception # :nodoc:
|
29
|
+
end
|
30
|
+
|
31
|
+
# :stopdoc:
|
32
|
+
THIS_FILE = /\A#{Regexp.quote(__FILE__)}:/o
|
33
|
+
CALLER_OFFSET = ((c = caller[0]) && THIS_FILE =~ c) ? 1 : 0
|
34
|
+
# :startdoc:
|
35
|
+
|
36
|
+
# Perform an operation in a block, timing it out if it takes longer
|
37
|
+
# than +sec+ seconds to complete.
|
38
|
+
#
|
39
|
+
# +sec+:: Number of seconds to wait for the block to terminate. Any number
|
40
|
+
# may be used, including Floats to specify fractional seconds.
|
41
|
+
# +klass+:: Exception Class to raise if the block fails to terminate
|
42
|
+
# in +sec+ seconds. Omitting will use the default, Timeout::Error
|
43
|
+
#
|
44
|
+
# The block will be executed on another thread and will be given one
|
45
|
+
# argument: +sec+.
|
46
|
+
#
|
47
|
+
# Returns the result of the block *if* the block completed before
|
48
|
+
# +sec+ seconds, otherwise throws an exception, based on the value of +klass+.
|
49
|
+
#
|
50
|
+
# Note that this is both a method of module Timeout, so you can <tt>include
|
51
|
+
# Timeout</tt> into your classes so they have a #timeout method, as well as
|
52
|
+
# a module method, so you can call it directly as Timeout.timeout().
|
53
|
+
def timeout(sec, klass = nil) #:yield: +sec+
|
54
|
+
return yield(sec) if sec == nil or sec.zero?
|
55
|
+
exception = klass || Class.new(ExitException)
|
56
|
+
begin
|
57
|
+
begin
|
58
|
+
current_thread = Thread.current
|
59
|
+
|
60
|
+
x = Thread.start{ yield(sec) }
|
61
|
+
|
62
|
+
y = Thread.start {
|
63
|
+
begin
|
64
|
+
sleep sec
|
65
|
+
rescue => e
|
66
|
+
x.raise e
|
67
|
+
else
|
68
|
+
x.kill
|
69
|
+
# x.join
|
70
|
+
current_thread.raise exception, "execution expired"
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
x.value
|
75
|
+
ensure
|
76
|
+
if y
|
77
|
+
y.kill
|
78
|
+
y.join # make sure y is dead.
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue exception => e
|
82
|
+
rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
|
83
|
+
(bt = e.backtrace).reject! {|m| rej =~ m}
|
84
|
+
level = -caller(CALLER_OFFSET).size
|
85
|
+
while THIS_FILE =~ bt[level]
|
86
|
+
bt.delete_at(level)
|
87
|
+
level += 1
|
88
|
+
end
|
89
|
+
raise if klass # if exception class is specified, it
|
90
|
+
# would be expected outside.
|
91
|
+
raise Error, e.message, e.backtrace
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
module_function :timeout
|
96
|
+
end
|
97
|
+
|
98
|
+
# Identical to:
|
99
|
+
#
|
100
|
+
# Timeout::timeout(n, e, &block).
|
101
|
+
#
|
102
|
+
# This method is deprecated and provided only for backwards compatibility.
|
103
|
+
# You should use Timeout#timeout instead.
|
104
|
+
def timeout(n, e = nil, &block)
|
105
|
+
Timeout::timeout(n, e, &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Another name for Timeout::Error, defined for backwards compatibility with
|
109
|
+
# earlier versions of timeout.rb.
|
110
|
+
TimeoutError = Timeout::Error
|
data/readme.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# SaneTimeout
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'sane_timeout'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install sane_timeout
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
## Open Questions
|
26
|
+
* on line 69, I had tried doing x.join based on the standard lib doing the
|
27
|
+
same thing with the y thread. I assumed that kill is not synchromous so it's
|
28
|
+
necessary to wait for x to die. However, doing this causes many things to break.
|
29
|
+
I haven't been able to figure out why.
|
30
|
+
* related, is there any reason to kill y before returning x.value?
|
31
|
+
|
32
|
+
## To Do
|
33
|
+
* determine best way to install sane_timeout so that it
|
34
|
+
replaces Timeout. possibly offer to modes, one where
|
35
|
+
Timeout is overwritten, another where sane_timeout is
|
36
|
+
invoked in its own namespace (SaneTimeout)
|
37
|
+
|
38
|
+
## Project Improvement
|
39
|
+
* better name?
|
40
|
+
* There is redundant code between test_timeout.rb and test_sane_timeout.rb
|
41
|
+
because I wanted to annotate in the former and be DRY in the latter. Maybe this
|
42
|
+
can somehow be improved.
|
43
|
+
|
44
|
+
## odd design choice in standard lib:
|
45
|
+
* same type of error is raised inside thread and outside when specified
|
46
|
+
* when not specified, Exception is raised inside, StandardError is raised outside
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "sane_timeout"
|
7
|
+
gem.version = '0.1'
|
8
|
+
gem.authors = ["John Joseph Bachir"]
|
9
|
+
gem.email = ["john@ganxy.com"]
|
10
|
+
gem.description = %q{Sane Timeout fixes Ruby standard lib Timeout}
|
11
|
+
gem.summary = %q{Sane Timeout fixes Ruby standard lib Timeout}
|
12
|
+
gem.homepage = "https://github.com/jjb/sane_timeout"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
# gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require '../sane-timeout'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
class TestTimeout < Test::Unit::TestCase
|
6
|
+
|
7
|
+
### Tests that come with standard lib
|
8
|
+
def test_queue
|
9
|
+
q = Queue.new
|
10
|
+
assert_raise(Timeout::Error, "[ruby-dev:32935]") {
|
11
|
+
timeout(0.1) { q.pop }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_timeout
|
16
|
+
@flag = true
|
17
|
+
Thread.start {
|
18
|
+
sleep 0.1
|
19
|
+
@flag = false
|
20
|
+
}
|
21
|
+
assert_nothing_raised("[ruby-dev:38319]") do
|
22
|
+
Timeout.timeout(1) {
|
23
|
+
nil while @flag
|
24
|
+
}
|
25
|
+
end
|
26
|
+
assert !@flag, "[ruby-dev:38319]"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_cannot_convert_into_time_interval
|
30
|
+
bug3168 = '[ruby-dev:41010]'
|
31
|
+
def (n = Object.new).zero?; false; end
|
32
|
+
assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
### tests not included in standard lib, but which standard lib does pass
|
38
|
+
|
39
|
+
def test_non_timing_out_code_is_successful
|
40
|
+
assert_nothing_raised do
|
41
|
+
Timeout.timeout(2) {
|
42
|
+
true
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_code_that_takes_too_long_is_stopped_and_raises
|
48
|
+
assert_raise(Timeout::Error) do
|
49
|
+
Timeout.timeout(0.1) {
|
50
|
+
sleep 10
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_returns_block_value_when_not_timing_out
|
56
|
+
retval = Timeout.timeout(1){ "foobar" }
|
57
|
+
assert_equal "foobar", retval
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
### Tests that sane_timeout passes
|
62
|
+
|
63
|
+
def subject(throws, catches)
|
64
|
+
$inner_attempted=nil
|
65
|
+
$inner_succeeded=nil
|
66
|
+
$caught_in_inner=nil
|
67
|
+
|
68
|
+
$raised_in_outer=nil
|
69
|
+
$not_raised_in_outer=nil
|
70
|
+
begin
|
71
|
+
Timeout.timeout(0.1, throws){
|
72
|
+
begin
|
73
|
+
$inner_attempted=true
|
74
|
+
sleep 10
|
75
|
+
rescue catches
|
76
|
+
$caught_in_inner=true
|
77
|
+
else
|
78
|
+
$inner_succeeded=true
|
79
|
+
end
|
80
|
+
}
|
81
|
+
rescue Exception
|
82
|
+
$raised_in_outer = true
|
83
|
+
else
|
84
|
+
$not_raised_in_outer = true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def expectations
|
89
|
+
assert $inner_attempted, "Inner was not attempted"
|
90
|
+
assert !$inner_succeeded, "Inner did not succeed"
|
91
|
+
assert !$caught_in_inner, "Exception was caught in inner"
|
92
|
+
assert $raised_in_outer, "Exception was not raised in outer"
|
93
|
+
assert !$not_raised_in_outer, "Exception was not raised in outer(2)"
|
94
|
+
end
|
95
|
+
|
96
|
+
puts "when an exception to raise is not specified and the inner code does not catch Exception"
|
97
|
+
def test_1
|
98
|
+
subject(nil, StandardError)
|
99
|
+
expectations
|
100
|
+
end
|
101
|
+
|
102
|
+
puts "when an exception to raise is not specified and the inner code does catch Exception"
|
103
|
+
def test_2
|
104
|
+
subject(nil, Exception)
|
105
|
+
expectations
|
106
|
+
end
|
107
|
+
|
108
|
+
puts "when an exception to raise is StandardError and the inner code does not catch Exception"
|
109
|
+
class MyError < StandardError; end
|
110
|
+
def test_3
|
111
|
+
subject(MyError, StandardError)
|
112
|
+
expectations
|
113
|
+
end
|
114
|
+
|
115
|
+
puts "when an exception to raise is StandardError and the inner code does catch Exception"
|
116
|
+
class MyError2 < StandardError; end
|
117
|
+
def test_4
|
118
|
+
subject(MyError2, Exception)
|
119
|
+
expectations
|
120
|
+
end
|
121
|
+
|
122
|
+
puts "when an exception to raise is Exception and the inner code does not catch Exception"
|
123
|
+
class MyError3 < Exception; end
|
124
|
+
def test_5
|
125
|
+
subject(MyError3, StandardError)
|
126
|
+
expectations
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "when an exception to raise is Exception and the inner code does catch Exception"
|
130
|
+
class MyError4 < Exception; end
|
131
|
+
def test_6
|
132
|
+
subject(MyError4, Exception)
|
133
|
+
expectations
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'timeout'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
class TestTimeout < Test::Unit::TestCase
|
6
|
+
|
7
|
+
### Tests that come with standard lib
|
8
|
+
|
9
|
+
def test_queue
|
10
|
+
q = Queue.new
|
11
|
+
assert_raise(Timeout::Error, "[ruby-dev:32935]") {
|
12
|
+
timeout(0.1) { q.pop }
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_timeout
|
17
|
+
@flag = true
|
18
|
+
Thread.start {
|
19
|
+
sleep 0.1
|
20
|
+
@flag = false
|
21
|
+
}
|
22
|
+
assert_nothing_raised("[ruby-dev:38319]") do
|
23
|
+
Timeout.timeout(1) {
|
24
|
+
nil while @flag
|
25
|
+
}
|
26
|
+
end
|
27
|
+
assert !@flag, "[ruby-dev:38319]"
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_cannot_convert_into_time_interval
|
31
|
+
bug3168 = '[ruby-dev:41010]'
|
32
|
+
def (n = Object.new).zero?; false; end
|
33
|
+
assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
### tests not included in standard lib, but which standard lib does pass
|
38
|
+
|
39
|
+
def test_non_timing_out_code_is_successful
|
40
|
+
assert_nothing_raised do
|
41
|
+
Timeout.timeout(2) {
|
42
|
+
true
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_code_that_takes_too_long_is_stopped_and_raises
|
48
|
+
assert_raise(Timeout::Error) do
|
49
|
+
Timeout.timeout(0.1) {
|
50
|
+
sleep 10
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_returns_block_value_when_not_timing_out
|
56
|
+
retval = Timeout.timeout(1){ "foobar" }
|
57
|
+
assert_equal "foobar", retval
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
### Tests demonstrating problems with standard lib
|
62
|
+
# NOTE: this demonstration was done such that all of the assertions pass,
|
63
|
+
# The ones marked weird, bad, and very bad should not, and their
|
64
|
+
# passing is demonstrating the brokenness.
|
65
|
+
|
66
|
+
def subject(throws, catches)
|
67
|
+
$inner_attempted=nil
|
68
|
+
$inner_succeeded=nil
|
69
|
+
$caught_in_inner=nil
|
70
|
+
|
71
|
+
$raised_in_outer=nil
|
72
|
+
$not_raised_in_outer=nil
|
73
|
+
begin
|
74
|
+
Timeout.timeout(0.1, throws){
|
75
|
+
begin
|
76
|
+
$inner_attempted=true
|
77
|
+
sleep 10
|
78
|
+
rescue catches
|
79
|
+
$caught_in_inner=true
|
80
|
+
else
|
81
|
+
$inner_succeeded=true
|
82
|
+
end
|
83
|
+
}
|
84
|
+
rescue Exception
|
85
|
+
$raised_in_outer = true
|
86
|
+
else
|
87
|
+
$not_raised_in_outer = true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
puts "when an exception to raise is not specified and the inner code does not catch Exception"
|
92
|
+
def test_1
|
93
|
+
subject(nil, StandardError)
|
94
|
+
|
95
|
+
# EXPECTED
|
96
|
+
assert $inner_attempted
|
97
|
+
assert !$inner_succeeded
|
98
|
+
assert !$caught_in_inner
|
99
|
+
assert $raised_in_outer && !$not_raised_in_outer
|
100
|
+
end
|
101
|
+
|
102
|
+
puts "when an exception to raise is not specified and the inner code does catch Exception"
|
103
|
+
def test_2
|
104
|
+
subject(nil, Exception)
|
105
|
+
|
106
|
+
# EXPECTED
|
107
|
+
assert $inner_attempted
|
108
|
+
assert !$inner_succeeded
|
109
|
+
|
110
|
+
# WEIRD
|
111
|
+
assert $caught_in_inner
|
112
|
+
|
113
|
+
# BAD
|
114
|
+
assert !$raised_in_outer && $not_raised_in_outer
|
115
|
+
end
|
116
|
+
|
117
|
+
puts "when an exception to raise is StandardError and the inner code does not catch Exception"
|
118
|
+
class MyError < StandardError; end
|
119
|
+
def test_3
|
120
|
+
subject(MyError, StandardError)
|
121
|
+
|
122
|
+
# EXPECTED
|
123
|
+
assert $inner_attempted
|
124
|
+
assert !$inner_succeeded
|
125
|
+
|
126
|
+
# WEIRD
|
127
|
+
assert $caught_in_inner
|
128
|
+
|
129
|
+
# BAD
|
130
|
+
assert !$raised_in_outer && $not_raised_in_outer
|
131
|
+
end
|
132
|
+
|
133
|
+
puts "when an exception to raise is StandardError and the inner code does catch Exception"
|
134
|
+
class MyError2 < StandardError; end
|
135
|
+
def test_4
|
136
|
+
subject(MyError2, Exception)
|
137
|
+
|
138
|
+
# EXPECTED
|
139
|
+
assert $inner_attempted
|
140
|
+
assert !$inner_succeeded
|
141
|
+
|
142
|
+
# WEIRD
|
143
|
+
assert $caught_in_inner
|
144
|
+
|
145
|
+
# BAD
|
146
|
+
assert !$raised_in_outer && $not_raised_in_outer
|
147
|
+
end
|
148
|
+
|
149
|
+
puts "when an exception to raise is Exception and the inner code does not catch Exception"
|
150
|
+
class MyError3 < Exception; end
|
151
|
+
def test_5
|
152
|
+
subject(MyError3, StandardError)
|
153
|
+
|
154
|
+
# EXPECTED
|
155
|
+
assert $inner_attempted
|
156
|
+
assert !$inner_succeeded
|
157
|
+
assert !$caught_in_inner
|
158
|
+
assert $raised_in_outer && !$not_raised_in_outer
|
159
|
+
end
|
160
|
+
|
161
|
+
puts "when an exception to raise is Exception and the inner code does catch Exception"
|
162
|
+
class MyError4 < Exception; end
|
163
|
+
def test_6
|
164
|
+
subject(MyError4, Exception)
|
165
|
+
|
166
|
+
# EXPECTED
|
167
|
+
assert $inner_attempted
|
168
|
+
assert !$inner_succeeded
|
169
|
+
|
170
|
+
# WEIRD
|
171
|
+
assert $caught_in_inner
|
172
|
+
|
173
|
+
# VERY BAD
|
174
|
+
assert !$raised_in_outer && $not_raised_in_outer
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sane_timeout
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John Joseph Bachir
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-15 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Sane Timeout fixes Ruby standard lib Timeout
|
15
|
+
email:
|
16
|
+
- john@ganxy.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- LICENSE.txt
|
22
|
+
- Rakefile
|
23
|
+
- lib/sane_timeout.rb
|
24
|
+
- readme.md
|
25
|
+
- sane_timeout.gemspec
|
26
|
+
- test/test_sane_timeout.rb
|
27
|
+
- test/test_timeout.rb
|
28
|
+
homepage: https://github.com/jjb/sane_timeout
|
29
|
+
licenses: []
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.23
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: Sane Timeout fixes Ruby standard lib Timeout
|
52
|
+
test_files:
|
53
|
+
- test/test_sane_timeout.rb
|
54
|
+
- test/test_timeout.rb
|
55
|
+
has_rdoc:
|