safe_timeout 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 784f00b2fc676fe36b98359e8eebdf9177f2e9dd
4
- data.tar.gz: 7ffbf4ed29ad028e4b29b79d24bb9d9feb8970e2
2
+ SHA256:
3
+ metadata.gz: 85774cf93e4a1a308a3f7bfe0d52b8659ab7eed9bd27ac6574ecf4f9fee948bb
4
+ data.tar.gz: e9c71448c0d4975bf580e273e659a7b25da4c6814956adc820f4e3235a0f810a
5
5
  SHA512:
6
- metadata.gz: 13b8a264e3449535013dda188788fe3828d76426e342a9742623fec65f4f7bfc39dd62b2611199f3b9203417e45b16a00968f97706d53cf79c5362efd560c8a8
7
- data.tar.gz: d0789e190f7ebcff48514fbae29670f8b73f87e74803d5e28508cb8e5cdcc88f938ad5327d362b45cec4ab754c461b973dff47e22892c7b0d6cd0fbbac357323
6
+ metadata.gz: b45ddb641fb75815a74504e52554cae94eeee7b9e88590097756df1e283ab9116f2f35097b5f478ca38e55f27407734b43f4a0d52fa9487387f4818768188390
7
+ data.tar.gz: 88ab46e0059f2b78bd9d76d063d791557e28a2ffc27b4ac6a6ca181ca11675c11aa099d47a87a261ad30b34105db8c8f26b6011e55d8d590f912cb22591e31bb
data/.codeclimate.yml ADDED
@@ -0,0 +1,29 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - c
8
+ - ruby
9
+ checks:
10
+ Similar code:
11
+ enabled: false
12
+ fixme:
13
+ enabled: true
14
+ flog:
15
+ enabled: true
16
+ config:
17
+ score_threshold: 20.0
18
+ rubocop:
19
+ enabled: true
20
+ exclude_fingerprints:
21
+ - 4218049e28199ed950d3cd721df86dce
22
+ - c8179d0de3a9df18a2c45750d3f8647e
23
+ - 03f6eee11d86507da564695007106721
24
+ channel: rubocop-1-23-0
25
+ ratings:
26
+ paths:
27
+ - "**.rb"
28
+ exclude_paths:
29
+ - spec/
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ ruby-version: ['2.6', '2.7', '3.0']
16
+
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - name: Set up Ruby
20
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
21
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
22
+ # uses: ruby/setup-ruby@v1
23
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
24
+ with:
25
+ ruby-version: ${{ matrix.ruby-version }}
26
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
27
+ - name: Run tests
28
+ run: bundle exec rake spec
29
+ - name: Rubocop
30
+ run: bundle exec rubocop
31
+ - name: Send coverage to Code Climate
32
+ if: ${{ matrix.ruby-version == '3.0' }}
33
+ uses: paambaati/codeclimate-action@v3.0.0
34
+ env:
35
+ CC_TEST_REPORTER_ID: 79c39bac6840a990e7dfd0950f282b08c1be9fb15e17718af730e524cf631855
36
+ with:
37
+ coverageLocations: ${{github.workspace}}/coverage/coverage.json:simplecov
38
+ coverageCommand: bundle exec rake coverage
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
+ --format documentation
1
2
  --color
2
- --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,204 @@
1
+ AllCops:
2
+ Exclude:
3
+ - ext/**/*
4
+ - tmp/**/*
5
+ - vendor/**/*
6
+ Gemspec/DateAssignment:
7
+ Enabled: true
8
+ Gemspec/RequireMFA:
9
+ Enabled: true
10
+ Gemspec/RequiredRubyVersion:
11
+ Enabled: false
12
+ Layout/EmptyLinesAroundAttributeAccessor:
13
+ Enabled: true
14
+ Layout/EmptyLinesAroundClassBody:
15
+ EnforcedStyle: empty_lines_except_namespace
16
+ Layout/EmptyLinesAroundModuleBody:
17
+ EnforcedStyle: empty_lines_except_namespace
18
+ Layout/ExtraSpacing:
19
+ Enabled: false
20
+ Layout/HashAlignment:
21
+ EnforcedHashRocketStyle: table
22
+ EnforcedColonStyle: table
23
+ Layout/LineEndStringConcatenationIndentation:
24
+ Enabled: true
25
+ Layout/LineLength:
26
+ Max: 120
27
+ Enabled: false
28
+ Layout/SpaceAroundMethodCallOperator:
29
+ Enabled: true
30
+ Layout/SpaceBeforeBrackets:
31
+ Enabled: true
32
+ Lint/AmbiguousAssignment:
33
+ Enabled: true
34
+ Lint/AmbiguousOperatorPrecedence:
35
+ Enabled: true
36
+ Lint/AmbiguousRange:
37
+ Enabled: true
38
+ Lint/DeprecatedConstants:
39
+ Enabled: true
40
+ Lint/DeprecatedOpenSSLConstant:
41
+ Enabled: true
42
+ Lint/DuplicateBranch:
43
+ Enabled: true
44
+ Lint/DuplicateRegexpCharacterClassElement:
45
+ Enabled: true
46
+ Lint/EmptyBlock:
47
+ Enabled: true
48
+ Lint/EmptyClass:
49
+ Enabled: true
50
+ Lint/EmptyInPattern:
51
+ Enabled: true
52
+ Lint/IncompatibleIoSelectWithFiberScheduler:
53
+ Enabled: true
54
+ Lint/LambdaWithoutLiteralBlock:
55
+ Enabled: true
56
+ Lint/MixedRegexpCaptureTypes:
57
+ Enabled: true
58
+ Lint/NoReturnInBeginEndBlocks:
59
+ Enabled: true
60
+ Lint/NumberedParameterAssignment:
61
+ Enabled: true
62
+ Lint/OrAssignmentToConstant:
63
+ Enabled: true
64
+ Lint/RaiseException:
65
+ Enabled: true
66
+ Lint/RedundantDirGlobSort:
67
+ Enabled: true
68
+ Lint/RequireRelativeSelfPath:
69
+ Enabled: true
70
+ Lint/StructNewOverride:
71
+ Enabled: true
72
+ Lint/SymbolConversion:
73
+ Enabled: true
74
+ Lint/ToEnumArguments:
75
+ Enabled: true
76
+ Lint/TripleQuotes:
77
+ Enabled: true
78
+ Lint/UnexpectedBlockArity:
79
+ Enabled: true
80
+ Lint/UnmodifiedReduceAccumulator:
81
+ Enabled: true
82
+ Lint/UselessRuby2Keywords:
83
+ Enabled: true
84
+ Metrics/AbcSize:
85
+ Max: 50
86
+ Enabled: false
87
+ Metrics/BlockLength:
88
+ Max: 50
89
+ Enabled: false
90
+ Metrics/ClassLength:
91
+ Max: 50
92
+ Enabled: false
93
+ Metrics/CyclomaticComplexity:
94
+ Max: 30
95
+ Enabled: false
96
+ Metrics/MethodLength:
97
+ Max: 20
98
+ Enabled: false
99
+ Metrics/ModuleLength:
100
+ Max: 1000
101
+ Enabled: false
102
+ Metrics/PerceivedComplexity:
103
+ Max: 30
104
+ Enabled: false
105
+ Naming/BlockForwarding:
106
+ Enabled: true
107
+ Security/IoMethods:
108
+ Enabled: true
109
+ Security/MarshalLoad:
110
+ Enabled: false
111
+ Style/AndOr:
112
+ Enabled: false
113
+ Style/ArgumentsForwarding:
114
+ Enabled: true
115
+ Style/CaseEquality:
116
+ Enabled: false
117
+ Style/CollectionCompact:
118
+ Enabled: true
119
+ Style/DocumentDynamicEvalDefinition:
120
+ Enabled: true
121
+ Style/Documentation:
122
+ Enabled: false
123
+ Style/DoubleNegation:
124
+ Enabled: false
125
+ Style/EndlessMethod:
126
+ Enabled: true
127
+ Style/ExponentialNotation:
128
+ Enabled: true
129
+ Style/FileRead:
130
+ Enabled: true
131
+ Style/FileWrite:
132
+ Enabled: true
133
+ Style/FrozenStringLiteralComment:
134
+ Enabled: false
135
+ Style/GuardClause:
136
+ Enabled: false
137
+ Style/HashConversion:
138
+ Enabled: true
139
+ Style/HashEachMethods:
140
+ Enabled: true
141
+ Style/HashExcept:
142
+ Enabled: true
143
+ Style/HashSyntax:
144
+ Enabled: true
145
+ Style/HashTransformKeys:
146
+ Enabled: true
147
+ Style/HashTransformValues:
148
+ Enabled: true
149
+ Style/IfUnlessModifier:
150
+ Enabled: false
151
+ Style/IfWithBooleanLiteralBranches:
152
+ Enabled: true
153
+ Style/InPatternThen:
154
+ Enabled: true
155
+ Style/MapToHash:
156
+ Enabled: true
157
+ Style/MultilineBlockChain:
158
+ Enabled: false
159
+ Style/MultilineIfModifier:
160
+ Enabled: false
161
+ Style/MultilineInPatternThen:
162
+ Enabled: true
163
+ Style/MutableConstant:
164
+ Enabled: false
165
+ Style/NegatedIfElseCondition:
166
+ Enabled: true
167
+ Style/NilLambda:
168
+ Enabled: true
169
+ Style/NumberedParameters:
170
+ Enabled: true
171
+ Style/NumberedParametersLimit:
172
+ Enabled: true
173
+ Style/OpenStructUse:
174
+ Enabled: true
175
+ Style/QuotedSymbols:
176
+ Enabled: true
177
+ Style/RedundantArgument:
178
+ Enabled: true
179
+ Style/RedundantRegexpCharacterClass:
180
+ Enabled: true
181
+ Style/RedundantRegexpEscape:
182
+ Enabled: true
183
+ Style/RedundantSelfAssignmentBranch:
184
+ Enabled: true
185
+ Style/RescueModifier:
186
+ Enabled: false
187
+ Style/RescueStandardError:
188
+ Enabled: false
189
+ Style/SelectByRegexp:
190
+ Enabled: true
191
+ Style/SlicingWithRange:
192
+ Enabled: true
193
+ Style/StringChars:
194
+ Enabled: true
195
+ Style/SwapValues:
196
+ Enabled: true
197
+ Style/TrailingCommaInArguments:
198
+ EnforcedStyleForMultiline: comma
199
+ Style/TrailingCommaInArrayLiteral:
200
+ EnforcedStyleForMultiline: consistent_comma
201
+ Style/TrailingCommaInHashLiteral:
202
+ EnforcedStyleForMultiline: consistent_comma
203
+ Style/ZeroLengthPredicate:
204
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --readme README.md
2
+ --title 'Safe Timeout Documentation'
3
+ --charset utf-8
4
+ --markup-provider=redcarpet
5
+ --markup markdown
6
+ 'lib/**/*.rb' - '*.md'
@@ -0,0 +1,133 @@
1
+
2
+ # Contributor Covenant Code of Conduct
3
+
4
+ ## Our Pledge
5
+
6
+ We as members, contributors, and leaders pledge to make participation in our
7
+ community a harassment-free experience for everyone, regardless of age, body
8
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
9
+ identity and expression, level of experience, education, socio-economic status,
10
+ nationality, personal appearance, race, caste, color, religion, or sexual
11
+ identity and orientation.
12
+
13
+ We pledge to act and interact in ways that contribute to an open, welcoming,
14
+ diverse, inclusive, and healthy community.
15
+
16
+ ## Our Standards
17
+
18
+ Examples of behavior that contributes to a positive environment for our
19
+ community include:
20
+
21
+ * Demonstrating empathy and kindness toward other people
22
+ * Being respectful of differing opinions, viewpoints, and experiences
23
+ * Giving and gracefully accepting constructive feedback
24
+ * Accepting responsibility and apologizing to those affected by our mistakes,
25
+ and learning from the experience
26
+ * Focusing on what is best not just for us as individuals, but for the overall
27
+ community
28
+
29
+ Examples of unacceptable behavior include:
30
+
31
+ * The use of sexualized language or imagery, and sexual attention or advances of
32
+ any kind
33
+ * Trolling, insulting or derogatory comments, and personal or political attacks
34
+ * Public or private harassment
35
+ * Publishing others' private information, such as a physical or email address,
36
+ without their explicit permission
37
+ * Other conduct which could reasonably be considered inappropriate in a
38
+ professional setting
39
+
40
+ ## Enforcement Responsibilities
41
+
42
+ Community leaders are responsible for clarifying and enforcing our standards of
43
+ acceptable behavior and will take appropriate and fair corrective action in
44
+ response to any behavior that they deem inappropriate, threatening, offensive,
45
+ or harmful.
46
+
47
+ Community leaders have the right and responsibility to remove, edit, or reject
48
+ comments, commits, code, wiki edits, issues, and other contributions that are
49
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
50
+ decisions when appropriate.
51
+
52
+ ## Scope
53
+
54
+ This Code of Conduct applies within all community spaces, and also applies when
55
+ an individual is officially representing the community in public spaces.
56
+ Examples of representing our community include using an official e-mail address,
57
+ posting via an official social media account, or acting as an appointed
58
+ representative at an online or offline event.
59
+
60
+ ## Enforcement
61
+
62
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
63
+ reported to the community leaders responsible for enforcement at
64
+ [INSERT CONTACT METHOD].
65
+ All complaints will be reviewed and investigated promptly and fairly.
66
+
67
+ All community leaders are obligated to respect the privacy and security of the
68
+ reporter of any incident.
69
+
70
+ ## Enforcement Guidelines
71
+
72
+ Community leaders will follow these Community Impact Guidelines in determining
73
+ the consequences for any action they deem in violation of this Code of Conduct:
74
+
75
+ ### 1. Correction
76
+
77
+ **Community Impact**: Use of inappropriate language or other behavior deemed
78
+ unprofessional or unwelcome in the community.
79
+
80
+ **Consequence**: A private, written warning from community leaders, providing
81
+ clarity around the nature of the violation and an explanation of why the
82
+ behavior was inappropriate. A public apology may be requested.
83
+
84
+ ### 2. Warning
85
+
86
+ **Community Impact**: A violation through a single incident or series of
87
+ actions.
88
+
89
+ **Consequence**: A warning with consequences for continued behavior. No
90
+ interaction with the people involved, including unsolicited interaction with
91
+ those enforcing the Code of Conduct, for a specified period of time. This
92
+ includes avoiding interactions in community spaces as well as external channels
93
+ like social media. Violating these terms may lead to a temporary or permanent
94
+ ban.
95
+
96
+ ### 3. Temporary Ban
97
+
98
+ **Community Impact**: A serious violation of community standards, including
99
+ sustained inappropriate behavior.
100
+
101
+ **Consequence**: A temporary ban from any sort of interaction or public
102
+ communication with the community for a specified period of time. No public or
103
+ private interaction with the people involved, including unsolicited interaction
104
+ with those enforcing the Code of Conduct, is allowed during this period.
105
+ Violating these terms may lead to a permanent ban.
106
+
107
+ ### 4. Permanent Ban
108
+
109
+ **Community Impact**: Demonstrating a pattern of violation of community
110
+ standards, including sustained inappropriate behavior, harassment of an
111
+ individual, or aggression toward or disparagement of classes of individuals.
112
+
113
+ **Consequence**: A permanent ban from any sort of public interaction within the
114
+ community.
115
+
116
+ ## Attribution
117
+
118
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119
+ version 2.1, available at
120
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121
+
122
+ Community Impact Guidelines were inspired by
123
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124
+
125
+ For answers to common questions about this code of conduct, see the FAQ at
126
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
127
+ [https://www.contributor-covenant.org/translations][translations].
128
+
129
+ [homepage]: https://www.contributor-covenant.org
130
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131
+ [Mozilla CoC]: https://github.com/mozilla/diversity
132
+ [FAQ]: https://www.contributor-covenant.org/faq
133
+ [translations]: https://www.contributor-covenant.org/translations
@@ -1,4 +1,4 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2015 David McCullars
4
4
 
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
data/README.md CHANGED
@@ -1,8 +1,23 @@
1
1
  # SafeTimeout
2
2
 
3
+ * README: https://github.com/david-mccullars/safe_timeout
4
+ * Documentation: http://www.rubydoc.info/github/david-mccullars/safe_timeout
5
+ * Bug Reports: https://github.com/david-mccullars/safe_timeout/issues
6
+
7
+
8
+ ## Status
9
+
10
+ [![Gem Version](https://badge.fury.io/rb/safe_timeout.svg)](https://badge.fury.io/rb/safe_timeout)
11
+ [![Build Status](https://github.com/david-mccullars/safe_timeout/workflows/CI/badge.svg)](https://github.com/david-mccullars/safe_timeout/actions?workflow=CI)
12
+ [![Code Climate](https://codeclimate.com/github/david-mccullars/safe_timeout/badges/gpa.svg)](https://codeclimate.com/github/david-mccullars/safe_timeout)
13
+ [![Test Coverage](https://codeclimate.com/github/david-mccullars/safe_timeout/badges/coverage.svg)](https://codeclimate.com/github/david-mccullars/safe_timeout/coverage)
14
+ [![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
15
+
16
+
17
+ ## Description
18
+
3
19
  A safer alternative to Ruby's Timeout that uses unix processes instead of threads
4
20
 
5
- [![Circle CI](https://circleci.com/gh/david-mccullars/safe_timeout/tree/master.svg?style=svg)](https://circleci.com/gh/david-mccullars/safe_timeout/tree/master)
6
21
 
7
22
  ## Installation
8
23
 
@@ -41,10 +56,7 @@ could add the following to the top of an application:
41
56
 
42
57
  Timeout = SafeTimeout
43
58
 
44
- ## Contributing
45
59
 
46
- 1. Fork it ( https://github.com/[my-github-username]/safe_timeout/fork )
47
- 2. Create your feature branch (`git checkout -b my-new-feature`)
48
- 3. Commit your changes (`git commit -am 'Add some feature'`)
49
- 4. Push to the branch (`git push origin my-new-feature`)
50
- 5. Create a new Pull Request
60
+ ## License
61
+
62
+ MIT. See the {file:LICENSE} file.
data/Rakefile CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'yard'
3
4
 
4
5
  RSpec::Core::RakeTask.new('spec')
5
- task :default => :spec
6
+ task default: :spec
7
+
8
+ YARD::Rake::YardocTask.new
data/bin/safe_timeout ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ if ENV['COVERAGE']
3
+ require 'simplecov'
4
+ SimpleCov.command_name("#{Process.ppid}:#{Process.pid}")
5
+ SimpleCov.formatter = Class.new(SimpleCov::Formatter::HTMLFormatter) do
6
+ def output_message(_ignored); end
7
+ end
8
+ SimpleCov.start
9
+ end
10
+ require 'safe_timeout'
11
+ SafeTimeout::InterruptingChildProcess.new(*ARGV).wait_for_timeout
@@ -1,40 +1,25 @@
1
1
  module SafeTimeout
2
2
  class InterruptingChildProcess
3
3
 
4
- def initialize(options={})
5
- @expiration = Time.now.to_f + options.fetch(:timeout)
6
- @on_timeout = options.fetch(:on_timeout)
7
- end
4
+ def initialize(expiration)
5
+ @ppid = Process.ppid
6
+ @expiration = expiration.to_f
8
7
 
9
- def start(&block)
10
- Signal.trap("TRAP", &@on_timeout)
11
- Kernel.fork { wait_for_expiration }
12
- yield
13
- ensure
14
- stop rescue nil
8
+ abort "Invalid pid to monitor: #{@ppid}" if @ppid.to_i.zero?
9
+ abort "Invalid expiration: #{@expiration}" unless @expiration > 0.0
15
10
  end
16
11
 
17
- def stop
18
- # Tell that child to stop interrupting!
19
- Process.kill("HUP", @child_pid)
12
+ def notify_parent_of_expiration
13
+ SafeTimeout.send_signal('TRAP', @ppid)
20
14
  end
21
15
 
22
- def wait_for_expiration
23
- Signal.trap("HUP") { exit 0 }
16
+ def wait_for_timeout
17
+ Signal.trap('HUP') { exit 0 }
18
+
19
+ sleep [@expiration - Time.now.to_f, 0.1].max
24
20
 
25
- # If the parent dies unexpectedly and the child is never told to
26
- # stop, it becomes an orphan and is given to the init process (1)
27
- # or worse yet it becomes a zombie with parent 0. In either case,
28
- # stop interrupting!
29
- while Process.ppid > 1
30
- sleep 0.1
31
- if Time.now.to_f > @expiration
32
- Process.kill("TRAP", Process.ppid)
33
- return
34
- end
35
- end
21
+ notify_parent_of_expiration
36
22
  end
37
23
 
38
24
  end
39
-
40
25
  end
@@ -0,0 +1,38 @@
1
+ require 'English'
2
+ module SafeTimeout
3
+ class Spawner
4
+
5
+ def initialize(options = {})
6
+ @expiration = Time.now.to_f + options.fetch(:timeout)
7
+ @on_timeout = lambda do |*args|
8
+ @timed_out = true
9
+ options.fetch(:on_timeout).call(*args)
10
+ end
11
+ @timed_out = false
12
+ end
13
+
14
+ def start
15
+ original = Signal.trap('TRAP', &@on_timeout) || 'DEFAULT'
16
+ spawn_interrupter
17
+ yield
18
+ ensure
19
+ Signal.trap('TRAP', original)
20
+ stop
21
+ end
22
+
23
+ def stop
24
+ # Tell that child to stop interrupting!
25
+ SafeTimeout.send_signal('HUP', @child_pid) unless @timed_out
26
+ Process.wait(@child_pid) rescue nil
27
+ end
28
+
29
+ def spawn_interrupter
30
+ # Create a light-weight child process to notify this process if it is
31
+ # taking too long
32
+ bin = Gem.bin_path('safe_timeout', 'safe_timeout')
33
+ @child_pid = Process.spawn({ 'COVERAGE' => defined?(SimpleCov) }, bin, @expiration.to_s)
34
+ Process.detach(@child_pid)
35
+ end
36
+
37
+ end
38
+ end
@@ -1,3 +1,5 @@
1
1
  module SafeTimeout
2
- VERSION = "0.0.1"
2
+
3
+ VERSION = '1.0.0'
4
+
3
5
  end
data/lib/safe_timeout.rb CHANGED
@@ -1,15 +1,26 @@
1
- # Ruby's Timeout is broken and highly dangerous. To avoid this risk we
2
- # instead use a child process to handle the timeout. We fork it and let
3
- # it issue a SIGINT (Ctrl-C) to the parent if the timeout is reached.
1
+ require 'timeout'
2
+
3
+ #
4
+ # Ruby's Timeout is broken and highly dangerous. To avoid this risk we instead
5
+ # use a child process to handle the timeout. We fork it and let it issue a SIGINT
6
+ # (Ctrl-C) to the parent if the timeout is reached.
7
+ #
4
8
  module SafeTimeout
5
9
 
6
10
  autoload :InterruptingChildProcess, 'safe_timeout/interrupting_child_process'
11
+ autoload :Spawner, 'safe_timeout/spawner'
7
12
 
8
- def self.timeout(sec, klass=nil, &block)
9
- InterruptingChildProcess.new(
10
- :timeout => sec,
11
- :on_timeout => lambda { |_| raise(klass || Timeout::Error) }
13
+ def self.timeout(sec, klass = nil, &block)
14
+ Spawner.new(
15
+ timeout: sec,
16
+ on_timeout: ->(_) { raise(klass || Timeout::Error) },
12
17
  ).start(&block)
13
18
  end
14
19
 
20
+ def self.send_signal(signal, pid)
21
+ Process.kill(signal, pid) if pid
22
+ rescue Errno::ESRCH
23
+ # do nothing
24
+ end
25
+
15
26
  end
data/safe_timeout.gemspec CHANGED
@@ -1,24 +1,35 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'safe_timeout/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "safe_timeout"
6
+ spec.name = 'safe_timeout'
8
7
  spec.version = SafeTimeout::VERSION
9
- spec.authors = ["David McCullars"]
10
- spec.email = ["david.mccullars@gmail.com"]
11
- spec.summary = %q{A safer alternative to Ruby's Timeout that uses unix processes instead of threads.}
12
- spec.description = %q{A safer alternative to Ruby's Timeout that uses unix processes instead of threads.}
13
- spec.homepage = "https://github.com/david-mccullars/safe_timeout"
14
- spec.license = "MIT"
8
+ spec.authors = ['David McCullars']
9
+ spec.email = ['david.mccullars@gmail.com']
10
+ spec.summary = "A safer alternative to Ruby's Timeout that uses unix processes instead of threads."
11
+ spec.description = "A safer alternative to Ruby's Timeout that uses unix processes instead of threads."
12
+ spec.homepage = 'https://github.com/david-mccullars/safe_timeout'
13
+ spec.license = 'MIT'
15
14
 
16
15
  spec.files = `git ls-files -z`.split("\x0")
17
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
20
19
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency "rspec", "~> 3.2"
20
+ spec.required_ruby_version = '>= 2.6'
21
+
22
+ spec.add_development_dependency 'bundler'
23
+ spec.add_development_dependency 'github-markup'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'redcarpet'
26
+ spec.add_development_dependency 'rspec'
27
+ spec.add_development_dependency 'rubocop'
28
+ spec.add_development_dependency 'rubocop-rake'
29
+ spec.add_development_dependency 'rubocop-rspec'
30
+ spec.add_development_dependency 'simplecov', '~> 0.17.0' # 0.18 not supported by code climate
31
+ spec.add_development_dependency 'yard'
32
+ spec.metadata = {
33
+ 'rubygems_mfa_required' => 'true',
34
+ }
24
35
  end
@@ -2,7 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe SafeTimeout do
4
4
  context '.timeout' do
5
-
6
5
  it 'should process its block' do
7
6
  result = nil
8
7
  expect do
@@ -23,22 +22,21 @@ describe SafeTimeout do
23
22
  child = fork do
24
23
  SafeTimeout.timeout(5) { sleep 5 }
25
24
  end
26
- :loop until grand_child = all_processes[child].first
25
+ :loop until (grand_child = all_processes[child].first)
27
26
  expect(grand_child).to be > child
28
27
 
29
28
  Process.kill('TERM', child)
30
- expect(is_process_still_running? grand_child).to be false
29
+ expect(is_process_still_running?(grand_child)).to be false
31
30
  end
32
31
 
33
32
  private
34
33
 
35
34
  # Returns a hash of all running processes and their children
36
35
  def all_processes
37
- `ps -eo pid,ppid`.lines.reduce(Hash.new []) do |hash, line|
36
+ `ps -eo pid,ppid`.lines.each_with_object(Hash.new([])) do |line, hash|
38
37
  pid, ppid = line.split.map(&:to_i)
39
38
  hash[ppid] = [] unless hash.key?(ppid)
40
39
  hash[ppid] << pid
41
- hash
42
40
  end
43
41
  end
44
42
 
@@ -55,6 +53,5 @@ describe SafeTimeout do
55
53
  end
56
54
  true
57
55
  end
58
-
59
56
  end
60
57
  end
data/spec/spec_helper.rb CHANGED
@@ -1,15 +1,16 @@
1
- require 'bundler'
2
- Bundler.require
1
+ require 'English'
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
3
+ require 'simplecov'
3
4
 
4
- RSpec.configure do |config|
5
+ FileUtils.rm_rf('coverage') # Prevent old runs from merging in
6
+ SimpleCov.command_name(Process.pid.to_s)
5
7
 
6
- config.expect_with :rspec do |expectations|
7
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
- end
9
-
10
- config.mock_with :rspec do |mocks|
11
- mocks.verify_partial_doubles = true
12
- mocks.syntax = :expect
13
- end
8
+ SimpleCov.start do
9
+ add_filter '/spec/'
10
+ end
14
11
 
12
+ require 'safe_timeout'
13
+ # Ensure all files get loaded (for coverage sake)
14
+ Dir[File.expand_path('../lib/**/*.rb', __dir__)].each do |f|
15
+ require f[%r{lib/(.*)\.rb$}, 1]
15
16
  end
metadata CHANGED
@@ -1,74 +1,180 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_timeout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David McCullars
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-12 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: github-markup
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - "~>"
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '10.0'
47
+ version: '0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - "~>"
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
39
60
  - !ruby/object:Gem::Version
40
- version: '10.0'
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
43
127
  requirement: !ruby/object:Gem::Requirement
44
128
  requirements:
45
129
  - - "~>"
46
130
  - !ruby/object:Gem::Version
47
- version: '3.2'
131
+ version: 0.17.0
48
132
  type: :development
49
133
  prerelease: false
50
134
  version_requirements: !ruby/object:Gem::Requirement
51
135
  requirements:
52
136
  - - "~>"
53
137
  - !ruby/object:Gem::Version
54
- version: '3.2'
138
+ version: 0.17.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: yard
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
55
153
  description: A safer alternative to Ruby's Timeout that uses unix processes instead
56
154
  of threads.
57
155
  email:
58
156
  - david.mccullars@gmail.com
59
- executables: []
157
+ executables:
158
+ - safe_timeout
60
159
  extensions: []
61
160
  extra_rdoc_files: []
62
161
  files:
162
+ - ".codeclimate.yml"
163
+ - ".github/workflows/ci.yml"
63
164
  - ".gitignore"
64
165
  - ".rspec"
166
+ - ".rubocop.yml"
167
+ - ".ruby-version"
168
+ - ".yardopts"
169
+ - CODE_OF_CONDUCT.md
65
170
  - Gemfile
66
- - LICENSE.txt
171
+ - LICENSE
67
172
  - README.md
68
173
  - Rakefile
69
- - circle.yml
174
+ - bin/safe_timeout
70
175
  - lib/safe_timeout.rb
71
176
  - lib/safe_timeout/interrupting_child_process.rb
177
+ - lib/safe_timeout/spawner.rb
72
178
  - lib/safe_timeout/version.rb
73
179
  - safe_timeout.gemspec
74
180
  - spec/lib/safe_timeout_spec.rb
@@ -76,8 +182,9 @@ files:
76
182
  homepage: https://github.com/david-mccullars/safe_timeout
77
183
  licenses:
78
184
  - MIT
79
- metadata: {}
80
- post_install_message:
185
+ metadata:
186
+ rubygems_mfa_required: 'true'
187
+ post_install_message:
81
188
  rdoc_options: []
82
189
  require_paths:
83
190
  - lib
@@ -85,16 +192,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
192
  requirements:
86
193
  - - ">="
87
194
  - !ruby/object:Gem::Version
88
- version: '0'
195
+ version: '2.6'
89
196
  required_rubygems_version: !ruby/object:Gem::Requirement
90
197
  requirements:
91
198
  - - ">="
92
199
  - !ruby/object:Gem::Version
93
200
  version: '0'
94
201
  requirements: []
95
- rubyforge_project:
96
- rubygems_version: 2.2.2
97
- signing_key:
202
+ rubygems_version: 3.2.32
203
+ signing_key:
98
204
  specification_version: 4
99
205
  summary: A safer alternative to Ruby's Timeout that uses unix processes instead of
100
206
  threads.
data/circle.yml DELETED
@@ -1,12 +0,0 @@
1
- dependencies:
2
- override:
3
- - 'rvm-exec 1.8.7 bundle install'
4
- - 'rvm-exec 1.9.3 bundle install'
5
- - 'rvm-exec 2.1.2 bundle install'
6
- - 'rvm-exec 2.2.0 bundle install'
7
- test:
8
- override:
9
- - 'rvm-exec 1.8.7 bundle exec rake'
10
- - 'rvm-exec 1.9.3 bundle exec rake'
11
- - 'rvm-exec 2.1.2 bundle exec rake'
12
- - 'rvm-exec 2.2.0 bundle exec rake'