restartable 0.2.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGMyYTJiYTBhNTgxOGIzOWM5NzRlZWQ2YWFhNzNkNGIzOTQ1NTgwYQ==
4
+ OTU0NzIyODRhMDJlMDgzYmViODZmYTA3OGJkMDY4Y2I3YTA4YTgwZQ==
5
5
  data.tar.gz: !binary |-
6
- ZDMwM2MwMDZmMWI5NGU4NmRhYjA5ZjQzZWIwYmI5MDI1ZjY3YjY0Yg==
7
- !binary "U0hBNTEy":
6
+ MWQyMDc2MTM2NWE1OTIwNDE4YjJmZTU3ZGNkYTljOGNmMjYwNWY2NQ==
7
+ SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTMyMTEyODg1YmIzOGY0NWI3NWM3YjBlMThjY2Q5NzU4YjBmZjlmYWNkYzI1
10
- ZDczN2JhZDFmNTRmMTk3OTY5NjE1YTBmMDM3NTljZWJhMDRjNjE4NjY5M2Ux
11
- MjMzYzA3Y2NjYjI3MjgzMTY3MDBhMzMyYzg0YWVhM2E2M2JmMmE=
9
+ OGM0NTE3NWRkY2ViNWNmMzU1MjJmNTg2ZGFlOWE0YTBmMzQ1MDAwZTVkYjlj
10
+ ZDFjMjQxMDdjNTk4ZDllMDM5N2M2MGY1MDkyMGFjNTEyZTdjMzMzMDIyMDQ1
11
+ NGE5NDlkZDg2ZDFkZTdlNzgzNmZhYWVhZmUwYTg5OGNlZTIxZjI=
12
12
  data.tar.gz: !binary |-
13
- NmFiZGFkNGZkYjNiN2Y0OWRlYmVjMThlOGY5YWVjODYxOGRiMDM5ZmRlZGRk
14
- ZDFkMDNkZGI2NTY5MjkyODIxN2E4NGMwYzA0ODg3ODg4NDZlZGUxNzk5MDBl
15
- OGM4ZWJiZGYwOWY3YWQyYmM1ZmQwYjkxN2M5NzBlMDc3Mzc1MmQ=
13
+ NjhjNGM3MjlhNDk0ZWJkMDBiZjJiMWNjNWYyY2I5N2NjYjY4YmZlOTBmNDdm
14
+ NjdhZTEwY2U1YWVjNTk2ZWQyMjg0ZGRjZDY1YzQzMWQyNGRlY2Q0YTc5ZjI2
15
+ ZWQyNjMyYTk0MTVhZmJjNDQ0YWJmN2Y4MDdhMmQ3M2Y5YzI1MDM=
data/.rubocop.yml ADDED
@@ -0,0 +1,59 @@
1
+ AllCops:
2
+ Exclude:
3
+ - '*.gemspec'
4
+
5
+ Lint/EndAlignment:
6
+ AlignWith: variable
7
+
8
+ Lint/Eval:
9
+ Enabled: false
10
+
11
+ Metrics/AbcSize:
12
+ Max: 20
13
+
14
+ Metrics/MethodLength:
15
+ Max: 20
16
+
17
+ Style/AccessModifierIndentation:
18
+ EnforcedStyle: outdent
19
+
20
+ Style/BracesAroundHashParameters:
21
+ Enabled: false
22
+
23
+ Style/CaseIndentation:
24
+ IndentWhenRelativeTo: end
25
+
26
+ Style/DotPosition:
27
+ EnforcedStyle: trailing
28
+
29
+ Style/DoubleNegation:
30
+ Enabled: false
31
+
32
+ Style/Encoding:
33
+ EnforcedStyle: when_needed
34
+
35
+ Style/HashSyntax:
36
+ EnforcedStyle: hash_rockets
37
+
38
+ Style/IfUnlessModifier:
39
+ MaxLineLength: 40
40
+
41
+ Style/IndentHash:
42
+ EnforcedStyle: consistent
43
+
44
+ Style/PercentLiteralDelimiters:
45
+ PreferredDelimiters:
46
+ '%w': '[]'
47
+ '%W': '[]'
48
+
49
+ Style/Semicolon:
50
+ AllowAsExpressionSeparator: true
51
+
52
+ Style/SpaceBeforeBlockBraces:
53
+ EnforcedStyle: no_space
54
+
55
+ Style/SpaceInsideHashLiteralBraces:
56
+ EnforcedStyle: no_space
57
+
58
+ Style/TrailingComma:
59
+ EnforcedStyleForMultiline: comma
data/.travis.yml CHANGED
@@ -1,8 +1,17 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.8.7
4
- - 1.9.2
5
4
  - 1.9.3
6
- - 2.0.0
7
- - ree
8
- script: bundle exec cucumber
5
+ - '2.0'
6
+ - '2.1'
7
+ script:
8
+ if [ -n "$RUBOCOP" ]; then
9
+ bundle exec rubocop
10
+ ; else
11
+ bundle exec cucumber
12
+ ; fi
13
+ matrix:
14
+ fast_finish: true
15
+ include:
16
+ - env: RUBOCOP=true
17
+ rvm: default
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
data/README.markdown CHANGED
@@ -1,9 +1,12 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/restartable.svg?style=flat)](https://rubygems.org/gems/restartable)
2
+ [![Build Status](https://img.shields.io/travis/toy/restartable/master.svg?style=flat)](https://travis-ci.org/toy/restartable)
3
+ [![Code Climate](https://img.shields.io/codeclimate/github/toy/restartable.svg?style=flat)](https://codeclimate.com/github/toy/restartable)
4
+ [![Dependency Status](https://img.shields.io/gemnasium/toy/restartable.svg?style=flat)](https://gemnasium.com/toy/restartable)
5
+
1
6
  # restartable
2
7
 
3
8
  Run code, Ctrl-C to restart, two Ctrl-C to stop.
4
9
 
5
- [![Build Status](https://travis-ci.org/toy/restartable.png?branch=master)](https://travis-ci.org/toy/restartable)
6
-
7
10
  ## Copyright
8
11
 
9
12
  Copyright (c) 2012-2014 Ivan Kuchin. See LICENSE.txt for details.
data/bin/restartable CHANGED
@@ -14,6 +14,12 @@ Usege:
14
14
 
15
15
  TEXT
16
16
 
17
+ op.on('-r', '--on-restart CMD', 'Run CMD on restart') do |cmd|
18
+ (options[:on_restart] ||= []) << proc{ system cmd }
19
+ end
20
+
21
+ op.separator nil
22
+
17
23
  op.on_tail('-h', '--help', 'Show full help') do
18
24
  puts option_parser.help
19
25
  exit
@@ -27,11 +33,11 @@ end
27
33
 
28
34
  begin
29
35
  option_parser.order!
30
- raise OptionParser::ParseError, 'No command to run' if ARGV.empty?
36
+ fail OptionParser::ParseError, 'No command to run' if ARGV.empty?
31
37
  rescue OptionParser::ParseError => e
32
- abort "#{e.to_s}\n\n#{option_parser.help}"
38
+ abort "#{e}\n\n#{option_parser.help}"
33
39
  end
34
40
 
35
41
  Restartable.new(options) do
36
- exec *ARGV
42
+ exec(*ARGV)
37
43
  end
@@ -1,32 +1,37 @@
1
1
  Feature: Restarting
2
2
 
3
3
  Scenario Outline: Restarting and terminating
4
+ Given I have set on restart to `$stdout.puts 'Restart!'`
4
5
  Given I have invoked restartable with `<code>`
5
6
 
6
7
  When I have waited for 1 second
7
8
  Then I should see "^C to restart, double ^C to stop" in stderr
8
9
  And I should see "Hello world!" in stdout
9
- And there should be an inner process
10
+ And there should be a child process
10
11
  When I interrupt restartable
11
- Then I should see "Killing children…" and "Waiting ^C 0.5 second than restart…" in stderr
12
- And inner process should terminate
12
+ Then I should see "Killing children…" in stderr
13
+ And I should see "Waiting ^C 0.5 second than restart…" in stderr within <timeout> seconds
14
+ And child process should terminate
13
15
 
14
16
  When I have waited for 1 second
15
- Then I should see "^C to restart, double ^C to stop" in stderr
17
+ Then I should see "Restart!" in stdout
18
+ And I should see "^C to restart, double ^C to stop" in stderr
16
19
  And I should see "Hello world!" in stdout
17
- And there should be an inner process
20
+ And there should be a child process
18
21
  When I interrupt restartable twice
19
22
  Then I should see "Killing children…" and "Don't restart!" in stderr
20
- And inner process should terminate
23
+ And child process should terminate within <timeout> seconds
21
24
  And restartable should finish
25
+ And I should not see "Waiting ^C 0.5 second than restart…" in stderr
26
+ And I should not see "Restart!" in stdout
22
27
 
23
28
  Examples:
24
- | code |
25
- | $stdout.puts "Hello world!" |
26
- | $stdout.puts "Hello world!"; 100.times{ sleep 1 } |
27
- | exec 'echo "Hello world!"; sleep 100' |
28
- | system 'echo "Hello world!"; sleep 100' |
29
- | fork{ $stdout.puts "Hello world!"; 100.times{ sleep 1 } } |
30
- | fork{ fork{ fork{ $stdout.puts "Hello world!"; 100.times{ sleep 1 } } } } |
31
- | Signal.trap("INT"){}; $stdout.puts "Hello world!"; 100.times{ sleep 1 } |
32
- | Signal.trap("INT"){}; Signal.trap("TERM"){}; $stdout.puts "Hello world!"; 100.times{ sleep 1 } |
29
+ | code | timeout |
30
+ | $stdout.puts "Hello world!" | 5 |
31
+ | $stdout.puts "Hello world!"; 100.times{ sleep 1 } | 5 |
32
+ | exec 'echo "Hello world!"; sleep 100' | 5 |
33
+ | system 'echo "Hello world!"; sleep 100' | 5 |
34
+ | fork{ $stdout.puts "Hello world!"; 100.times{ sleep 1 } } | 5 |
35
+ | fork{ fork{ fork{ $stdout.puts "Hello world!"; 100.times{ sleep 1 } } } } | 5 |
36
+ | Signal.trap("INT"){}; $stdout.puts "Hello world!"; 100.times{ sleep 1 } | 15 |
37
+ | Signal.trap("INT"){}; Signal.trap("TERM"){}; $stdout.puts "Hello world!"; 100.times{ sleep 1 } | 25 |
@@ -1,5 +1,9 @@
1
1
  require 'restartable'
2
2
 
3
+ Given(/^I have set on restart to `(.*?)`$/) do |command|
4
+ (@on_restart ||= []) << proc{ eval(command) }
5
+ end
6
+
3
7
  Given(/^I have invoked restartable with `(.*?)`$/) do |command|
4
8
  @stdout = IO.pipe
5
9
  @stderr = IO.pipe
@@ -13,7 +17,7 @@ Given(/^I have invoked restartable with `(.*?)`$/) do |command|
13
17
  @stderr[0].close
14
18
  STDERR.reopen(@stderr[1])
15
19
 
16
- Restartable.new do
20
+ Restartable.new(:on_restart => @on_restart) do
17
21
  Signal.trap('INT', 'EXIT')
18
22
  eval(command)
19
23
  end
@@ -27,17 +31,16 @@ When(/^I have waited for (\d+) second$/) do |seconds|
27
31
  sleep seconds.to_i
28
32
  end
29
33
 
30
- Then(/^I should see "(.*?)" in stdout$/) do |string|
31
- Timeout::timeout(5) do
32
- @stdout[0].gets.should include(string)
33
- end
34
- end
35
-
36
- Then(/^I should see "(.*?)" in stderr$/) do |arg|
37
- Timeout::timeout(60) do
34
+ Then(/^
35
+ I\ should\ see\ "(.*?)"
36
+ \ in\ std(out|err)
37
+ (?:\ within\ (\d+)\ seconds)?
38
+ $/x) do |arg, io_name, timeout|
39
+ io = (io_name == 'out' ? @stdout : @stderr)[0]
40
+ Timeout.timeout(timeout ? timeout.to_i : 5) do
38
41
  strings = arg.split(/".*?"/)
39
42
  until strings.empty?
40
- line = @stderr[0].gets
43
+ line = io.gets
41
44
  strings.reject! do |string|
42
45
  line.include?(string)
43
46
  end
@@ -45,34 +48,40 @@ Then(/^I should see "(.*?)" in stderr$/) do |arg|
45
48
  end
46
49
  end
47
50
 
51
+ Then(/^I should not see "(.*?)" in std(out|err)$/) do |arg, io_name|
52
+ io = (io_name == 'out' ? @stdout : @stderr)[0]
53
+ strings = arg.split(/".*?"/)
54
+ while (line = io.gets)
55
+ if strings.any?{ |string| line.include?(string) }
56
+ fail "Got #{line}"
57
+ end
58
+ end
59
+ end
60
+
48
61
  When(/^I interrupt restartable$/) do
49
62
  Process.kill('INT', -@pid)
50
63
  end
51
64
 
52
65
  When(/^I interrupt restartable twice$/) do
53
- Process.kill('INT', -@pid)
66
+ step 'I interrupt restartable'
54
67
  sleep 0.1
55
- Process.kill('INT', -@pid)
68
+ step 'I interrupt restartable'
56
69
  end
57
70
 
58
- Then(/^there should be an inner process$/) do
59
- Timeout::timeout(5) do
60
- until Sys::ProcTable.ps.any?{ |pe| pe.ppid == @pid }
61
- sleep 1
62
- end
71
+ Then(/^there should be a child process$/) do
72
+ Timeout.timeout(5) do
73
+ sleep 1 until Sys::ProcTable.ps.any?{ |pe| pe.ppid == @pid }
63
74
  end
64
75
  end
65
76
 
66
- Then(/^inner process should terminate$/) do
67
- Timeout::timeout(100) do
68
- until Sys::ProcTable.ps.none?{ |pe| pe.ppid == @pid }
69
- sleep 1
70
- end
77
+ Then(/^child process should terminate(?: within (\d+) seconds)?$/) do |timeout|
78
+ Timeout.timeout(timeout ? timeout.to_i : 5) do
79
+ sleep 1 until Sys::ProcTable.ps.none?{ |pe| pe.ppid == @pid }
71
80
  end
72
81
  end
73
82
 
74
83
  Then(/^restartable should finish$/) do
75
- Timeout::timeout(5) do
84
+ Timeout.timeout(5) do
76
85
  Process.wait(@pid)
77
86
  end
78
87
  end
data/lib/restartable.rb CHANGED
@@ -3,26 +3,25 @@
3
3
  require 'sys/proctable'
4
4
  require 'colored'
5
5
  require 'thread'
6
+ require 'English'
6
7
 
8
+ # Main interface
7
9
  class Restartable
8
10
  def self.version
9
11
  Gem.loaded_specs['restartable'].version.to_s rescue 'DEV'
10
12
  end
11
13
 
12
14
  def initialize(options = {}, &block)
13
- @options, @block = options, block
15
+ @on_restart = Array(options[:on_restart])
16
+ @block = block
14
17
  run!
15
18
  end
16
19
 
17
20
  private
18
21
 
19
22
  def run!
20
- Signal.trap('INT') do
21
- interrupt!
22
- end
23
- Signal.trap('TERM') do
24
- terminate!
25
- end
23
+ Signal.trap('INT'){ interrupt! }
24
+ Signal.trap('TERM'){ terminate! }
26
25
 
27
26
  until @stop
28
27
  @interrupted = nil
@@ -34,10 +33,11 @@ private
34
33
  end
35
34
  sleep 0.1 until @interrupted
36
35
  kill_children!
37
- unless @stop
38
- $stderr << "Waiting ^C 0.5 second than restart…\n".yellow.bold
39
- sleep 0.5
40
- end
36
+ break if @stop
37
+ $stderr << "Waiting ^C 0.5 second than restart…\n".yellow.bold
38
+ sleep 0.5
39
+ break if @stop
40
+ @on_restart.each(&:call)
41
41
  end
42
42
  end
43
43
 
@@ -58,26 +58,35 @@ private
58
58
  WAIT_SIGNALS = [[5, 'INT'], [3, 'INT'], [1, 'INT'], [3, 'TERM'], [5, 'KILL']]
59
59
 
60
60
  def kill_children!
61
- until (pids = children_pids).empty?
61
+ until children_pids.empty?
62
62
  $stderr << "Killing children…\n".yellow.bold
63
- ripper = Thread.new do
64
- WAIT_SIGNALS.each do |time, signal|
65
- sleep time
66
- $stderr << "…SIG#{signal}…\n".yellow
67
- signal_children!(signal)
63
+
64
+ signal_pair = 0
65
+
66
+ begin
67
+ time, signal = WAIT_SIGNALS[signal_pair]
68
+ Timeout.timeout(time) do
69
+ Process.waitall
70
+ wait_children
68
71
  end
72
+ rescue Timeout::Error
73
+ $stderr << "…SIG#{signal}…\n".yellow
74
+ signal_children!(signal)
75
+ retry if WAIT_SIGNALS[signal_pair += 1]
69
76
  end
70
- Process.waitall
71
- pids.each do |pid|
72
- begin
73
- loop do
74
- Process.kill(0, pid)
75
- sleep 1
76
- end
77
- rescue Errno::ESRCH
77
+ end
78
+ end
79
+
80
+ def wait_children
81
+ children_pids.each do |pid|
82
+ begin
83
+ loop do
84
+ Process.kill(0, pid)
85
+ sleep 1
78
86
  end
87
+ rescue Errno::ESRCH
88
+ next
79
89
  end
80
- ripper.terminate
81
90
  end
82
91
  end
83
92
 
@@ -86,6 +95,7 @@ private
86
95
  begin
87
96
  Process.kill(signal, pid)
88
97
  rescue Errno::ESRCH
98
+ next
89
99
  end
90
100
  end
91
101
  end
@@ -94,15 +104,11 @@ private
94
104
  pgrp = Process.getpgrp
95
105
  Sys::ProcTable.ps.select do |pe|
96
106
  pgrp == case
97
- when pe.respond_to?(:pgid)
98
- pe.pgid
99
- when pe.respond_to?(:pgrp)
100
- pe.pgrp
101
- when pe.respond_to?(:ppid)
102
- pe.ppid
103
- else
104
- raise 'Can\'t find process group id'
107
+ when pe.respond_to?(:pgid) then pe.pgid
108
+ when pe.respond_to?(:pgrp) then pe.pgrp
109
+ when pe.respond_to?(:ppid) then pe.ppid
110
+ else fail 'Can\'t find process group id'
105
111
  end
106
- end.map(&:pid) - [$$]
112
+ end.map(&:pid) - [$PROCESS_ID]
107
113
  end
108
114
  end
data/restartable.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'restartable'
5
- s.version = '0.2.2'
5
+ s.version = '1.0.0'
6
6
  s.summary = %q{Run code, Ctrl-C to restart, once more Ctrl-C to stop}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -18,5 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency 'colored', '~> 1.2'
19
19
  s.add_dependency 'sys-proctable', '~> 0.9.3'
20
20
  s.add_development_dependency 'cucumber'
21
- s.add_development_dependency 'rspec'
21
+ s.add_development_dependency 'rspec', '~> 3.0'
22
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
23
+ s.add_development_dependency 'rubocop', '~> 0.27'
24
+ end
22
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restartable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Kuchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-10 00:00:00.000000000 Z
11
+ date: 2014-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colored
@@ -56,16 +56,30 @@ dependencies:
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '3.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ! '>='
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.27'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.27'
69
83
  description:
70
84
  email:
71
85
  executables:
@@ -74,11 +88,11 @@ extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
90
  - .gitignore
91
+ - .rubocop.yml
77
92
  - .travis.yml
78
93
  - Gemfile
79
94
  - LICENSE.txt
80
95
  - README.markdown
81
- - TODO
82
96
  - bin/restartable
83
97
  - features/restarting.feature
84
98
  - features/step_definitions/restartable_steps.rb
@@ -104,10 +118,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
118
  version: '0'
105
119
  requirements: []
106
120
  rubyforge_project: restartable
107
- rubygems_version: 2.0.3
121
+ rubygems_version: 2.4.5
108
122
  signing_key:
109
123
  specification_version: 4
110
124
  summary: Run code, Ctrl-C to restart, once more Ctrl-C to stop
111
125
  test_files:
112
126
  - features/restarting.feature
113
127
  - features/step_definitions/restartable_steps.rb
128
+ has_rdoc:
data/TODO DELETED
@@ -1,3 +0,0 @@
1
- add option to run block on restart
2
- bin option to run command on restart
3
- option to auto restart when block finishes