cromwell 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +139 -0
- data/Rakefile +94 -0
- data/VERSION +1 -0
- data/cromwell.gemspec +60 -0
- data/examples/example1.rb +8 -0
- data/examples/example2.rb +8 -0
- data/examples/example3.rb +8 -0
- data/lib/cromwell.rb +74 -0
- data/test/helper.rb +31 -0
- data/test/test_cromwell.rb +141 -0
- metadata +81 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Przemyslaw Kowalczyk
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
= Cromwell
|
2
|
+
|
3
|
+
Lord Protector of your scripts.
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
This is a very simple wrapper over <code>Signal#trap</code> method that allows you to easily protect your scripts from being killed while they are doing something that should not be interrupted (e.g. interacting with some non-transactional service) or is too costly to restart (e.g. long computations).
|
8
|
+
|
9
|
+
While inside the protected block, your script will ignore certain signals and continue its work, but if a signal was caught, it will terminate once the protected block is over. By default, only following signals are ignored: <code>INT</code> (keyboard interrupt <code>^C</code>), <code>TERM</code> (sent by <code>kill</code> by defautl), <code>HUP</code> (sent when shell terminates), and <code>QUIT</code> (a "dump core" signal, sent with <code>^\\</code>), but you can specify any list of them (except for <code>KILL</code> and <code>STOP</code>, of course).
|
10
|
+
|
11
|
+
For a full signal list supported on your operating system, run <code>Signal.list</code> in your <code>irb</code>. For more info on signals and their meaning, check your local <code>man signal</code>.
|
12
|
+
|
13
|
+
This gem is based on real-life production code. It is especially useful for protecting various daemon-like scripts in Rails application that are (might be) restarted with every deploy.
|
14
|
+
|
15
|
+
== Usage examples
|
16
|
+
|
17
|
+
The most important in Cromwell API is the <code>protect</code> method. It can be called in two ways: with a block and without a block.
|
18
|
+
|
19
|
+
=== Block form
|
20
|
+
|
21
|
+
When used with a block, Cromwell executes the code inside the block protecting it from being interrupted with a signal:
|
22
|
+
|
23
|
+
puts 'See you in a while...'
|
24
|
+
Cromwell.protect {
|
25
|
+
sleep 10
|
26
|
+
}
|
27
|
+
puts "You're still here?"
|
28
|
+
|
29
|
+
When you run this script (which lives in <code>examples/example1.rb</code>), you won't be able to interrupt it with <code>^C</code> or simple <code>kill</code> while it's sleeping for ten seconds:
|
30
|
+
|
31
|
+
$ ruby examples/example1.rb
|
32
|
+
See you in a while...
|
33
|
+
^C^C^C^C
|
34
|
+
[ ten seconds pass... ]
|
35
|
+
$
|
36
|
+
|
37
|
+
Because I tried to interrupt the script, it was terminated once the protected block was over. Had I not pressed <code>^C</code>, the last line would be executed:
|
38
|
+
|
39
|
+
$ ruby examples/example1.rb
|
40
|
+
See you in a while...
|
41
|
+
[ ten seconds pass... ]
|
42
|
+
You're still here?
|
43
|
+
$
|
44
|
+
|
45
|
+
The script cannot be killed, too (I run it in background to be able to run other commands in the same shell):
|
46
|
+
|
47
|
+
$ ruby examples/example1.rb &
|
48
|
+
[1] 70300
|
49
|
+
See you in a while...
|
50
|
+
$ kill 70300
|
51
|
+
[ ten seconds pass... ]
|
52
|
+
$
|
53
|
+
[1]+ Done ruby examples/example1.rb
|
54
|
+
|
55
|
+
If you really want to kill it, use <code>kill -9</code>:
|
56
|
+
|
57
|
+
$ ruby examples/example1.rb &
|
58
|
+
[1] 70328
|
59
|
+
See you in a while...
|
60
|
+
$ kill -9 70328
|
61
|
+
$
|
62
|
+
[1]+ Killed ruby examples/example1.rb
|
63
|
+
|
64
|
+
=== Non-block form
|
65
|
+
|
66
|
+
If you want to have more control over what's protected in your script, you can use <code>protect</code> without the block. In that case your code will be protected until you call <code>unprotect</code> method:
|
67
|
+
|
68
|
+
puts 'See you in a while...'
|
69
|
+
Cromwell.protect
|
70
|
+
sleep 10
|
71
|
+
Cromwell.unprotect
|
72
|
+
puts "You're still here?"
|
73
|
+
|
74
|
+
The above code lives in <code>examples/example2.rb</code> and behaves in the same way as previous example.
|
75
|
+
|
76
|
+
In general it might be good idea to place the call to <code>unprotect</code> in an <code>ensure</code> block. Or, if you want your script to just run until it finishes on its own, don't call <code>unprotect</code> at all.
|
77
|
+
|
78
|
+
=== Specifying other signals
|
79
|
+
|
80
|
+
If you want to protect from other signals than the default list, specify them as parameters to <code>protect</code> method:
|
81
|
+
|
82
|
+
puts "You can't stop me with ^C but you can kill me. My pid is #{$$}."
|
83
|
+
Cromwell.protect("INT") {
|
84
|
+
sleep 10
|
85
|
+
}
|
86
|
+
puts "You're still here?"
|
87
|
+
|
88
|
+
This script is still immune to <code>^C</code>:
|
89
|
+
|
90
|
+
$ ruby examples/example3.rb
|
91
|
+
You can't stop me with ^C but you can kill me. My pid is 70243.
|
92
|
+
^C^C^C^C
|
93
|
+
[ ten seconds pass... ]
|
94
|
+
$
|
95
|
+
|
96
|
+
But can be killed:
|
97
|
+
|
98
|
+
$ ruby examples/example3.rb &
|
99
|
+
[1] 70245
|
100
|
+
You can't stop me with ^C but you can kill me. My pid is 70245.
|
101
|
+
$ kill 70245
|
102
|
+
[1]+ Terminated ruby examples/example3.rb
|
103
|
+
$
|
104
|
+
|
105
|
+
=== Inspecting state
|
106
|
+
|
107
|
+
You can inspect Cromwell's state with two methods:
|
108
|
+
|
109
|
+
* <code>Cromwell.protected?</code> returns <code>true</code> when your code is protected, <code>false</code> otherwise.
|
110
|
+
* <code>Cromwell.should_exit?</code> returns <code>true</code> when a signal was caught and termination will ocur after the protected code is over.
|
111
|
+
|
112
|
+
== Compatibility
|
113
|
+
|
114
|
+
Works for me. Tested on Mac OS X 10.4--10.6 and a little bit on Debian Linux. If it works for you too, I'd be glad to know. Cromwell's reliability depends heavily on your operating system's signals implementation reliability (which may not be very stable on some systems).
|
115
|
+
|
116
|
+
== To Do list
|
117
|
+
|
118
|
+
* Remove traps when they are not needed anymore. Currently they remain in place and when you call protect again (even with different signals list), the old ones are still effective.
|
119
|
+
* Allow to customize behavior after catching a signal. Right now, the script is terminated after the protected block is done (even if the signal would not normally cause script termination).
|
120
|
+
* Play well with other trap handlers that might be installed.
|
121
|
+
* Add logger support.
|
122
|
+
|
123
|
+
== Note on Patches/Pull Requests
|
124
|
+
|
125
|
+
* Fork the project.
|
126
|
+
* Make your feature addition or bug fix.
|
127
|
+
* Add tests for it. This is important so I don't break it in a
|
128
|
+
future version unintentionally.
|
129
|
+
* Commit, do not mess with rakefile, version, or history.
|
130
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
131
|
+
* Send me a pull request. Bonus points for topic branches.
|
132
|
+
|
133
|
+
== Note on terminology
|
134
|
+
|
135
|
+
The protection from signals provided by Cromwell and the method names <code>protect</code>, <code>unprotect</code>, and <code>protected?</code> have <b>nothing</b> to do with Ruby's <code>protected</code> keyword and the general concept of a <i>protected</i> method in Ruby and other object-oriented languages.
|
136
|
+
|
137
|
+
== Copyright
|
138
|
+
|
139
|
+
Copyright (c) 2009 Przemyslaw Kowalczyk. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "cromwell"
|
8
|
+
gem.summary = %Q{Lord Protector of your scripts}
|
9
|
+
gem.description = %Q{A very simple wrapper over Signal#trap method that allows you to easily protect your scripts from being killed while they are doing something that should not be interrupted (e.g. interacting with some non-transactional service) or is too costly to restart (e.g. long computations). }
|
10
|
+
gem.email = "szeryf@negativeiq.pl"
|
11
|
+
gem.homepage = "http://github.com/szeryf/cromwell"
|
12
|
+
gem.authors = ["Przemyslaw Kowalczyk"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.rcov_opts << "-x /gems/"
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "cromwell #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
55
|
+
|
56
|
+
begin
|
57
|
+
require 'metric_fu'
|
58
|
+
rescue LoadError
|
59
|
+
puts "metric_fu (or a dependency) not available. If you want to run metrics, install it with: gem install metric_fu"
|
60
|
+
end
|
61
|
+
|
62
|
+
MetricFu::Configuration.run do |config|
|
63
|
+
#define which metrics you want to use
|
64
|
+
config.metrics = [:churn, :saikuro, :flog, :flay, :reek, :roodi, :rcov]
|
65
|
+
config.graphs = []
|
66
|
+
config.flay = { :dirs_to_flay => ['lib'] }
|
67
|
+
config.flog = { :dirs_to_flog => ['lib'] }
|
68
|
+
config.reek = { :dirs_to_reek => ['lib'] }
|
69
|
+
config.roodi = { :dirs_to_roodi => ['lib'] }
|
70
|
+
config.saikuro = { :output_directory => 'scratch_directory/saikuro',
|
71
|
+
:input_directory => ['lib'],
|
72
|
+
:cyclo => "",
|
73
|
+
:filter_cyclo => "0",
|
74
|
+
:warn_cyclo => "5",
|
75
|
+
:error_cyclo => "7",
|
76
|
+
:formater => "text"} #this needs to be set to "text"
|
77
|
+
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
78
|
+
config.rcov = { :test_files => ['test/**/test_*.rb'],
|
79
|
+
:rcov_opts => ["--sort coverage",
|
80
|
+
"--no-html",
|
81
|
+
"--text-coverage",
|
82
|
+
"--no-color",
|
83
|
+
"--exclude /gems/,/Library/,spec"]}
|
84
|
+
end
|
85
|
+
|
86
|
+
# fix for failing on NaN
|
87
|
+
module MetricFu
|
88
|
+
class Generator
|
89
|
+
def round_to_tenths(decimal)
|
90
|
+
decimal=0.0 if decimal.to_s.eql?('NaN')
|
91
|
+
(decimal.to_i * 10).round / 10.0
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/cromwell.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cromwell}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Przemyslaw Kowalczyk"]
|
12
|
+
s.date = %q{2010-01-03}
|
13
|
+
s.description = %q{A very simple wrapper over Signal#trap method that allows you to easily protect your scripts from being killed while they are doing something that should not be interrupted (e.g. interacting with some non-transactional service) or is too costly to restart (e.g. long computations). }
|
14
|
+
s.email = %q{szeryf@negativeiq.pl}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"cromwell.gemspec",
|
27
|
+
"examples/example1.rb",
|
28
|
+
"examples/example2.rb",
|
29
|
+
"examples/example3.rb",
|
30
|
+
"lib/cromwell.rb",
|
31
|
+
"test/helper.rb",
|
32
|
+
"test/test_cromwell.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/szeryf/cromwell}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.5}
|
38
|
+
s.summary = %q{Lord Protector of your scripts}
|
39
|
+
s.test_files = [
|
40
|
+
"test/helper.rb",
|
41
|
+
"test/test_cromwell.rb",
|
42
|
+
"examples/example1.rb",
|
43
|
+
"examples/example2.rb",
|
44
|
+
"examples/example3.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
55
|
+
end
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/cromwell.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
class Cromwell
|
2
|
+
DEFAULT_SIGNAL_LIST = %w[INT TERM HUP QUIT].freeze
|
3
|
+
|
4
|
+
class << self
|
5
|
+
# call-seq:
|
6
|
+
# Cromwell.protect(*signals) { ... some code ... }
|
7
|
+
# Cromwell.protect(*signals)
|
8
|
+
# Cromwell.protect { ... some code ... }
|
9
|
+
# Cromwell.protect
|
10
|
+
#
|
11
|
+
# Starts protecting your code. If called with a block, only the code within a block is
|
12
|
+
# executed with signal protection. Without a block, script is protected until unprotect
|
13
|
+
# is called. Signals can be given in all the forms that <code>Signal#trap</code> recognizes.
|
14
|
+
# Without parameters, the code is protected from the signals in DEFAULT_SIGNAL_LIST.
|
15
|
+
# More info and examples in README.rdoc.
|
16
|
+
def protect *signals
|
17
|
+
set_up_traps(signals.empty? ? DEFAULT_SIGNAL_LIST : signals.flatten)
|
18
|
+
@@should_exit = false
|
19
|
+
@@protected = true
|
20
|
+
if block_given?
|
21
|
+
begin
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
unprotect
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# call-seq:
|
30
|
+
# Cromwell.unprotect
|
31
|
+
#
|
32
|
+
# Turns off protection from signals. Will terminate the script with <code>Kernel#exit</code>
|
33
|
+
# if signal was caught earlier (so any <code>at_exit</code> code will be executed).
|
34
|
+
# The protect method calls this automatically when executed with a block.
|
35
|
+
def unprotect
|
36
|
+
@@protected = false
|
37
|
+
exit if @@should_exit
|
38
|
+
end
|
39
|
+
|
40
|
+
# call-seq:
|
41
|
+
# Cromwell.should_exit?
|
42
|
+
#
|
43
|
+
# True when the script will be terminated after protected block, i.e. when a signal
|
44
|
+
# was caught that was protected from.
|
45
|
+
def should_exit?
|
46
|
+
@@should_exit
|
47
|
+
end
|
48
|
+
|
49
|
+
# call-seq:
|
50
|
+
# Cromwell.protected?
|
51
|
+
#
|
52
|
+
# True if the protection is currently active.
|
53
|
+
def protected?
|
54
|
+
@@protected
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def set_up_traps signals
|
59
|
+
signals.each { |signal| set_up_trap signal }
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_up_trap signal
|
63
|
+
trap signal do
|
64
|
+
if @@protected
|
65
|
+
#puts "Just a minute now."
|
66
|
+
@@should_exit = true
|
67
|
+
"IGNORE"
|
68
|
+
else
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'cromwell'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
def assert_slept_at_least seconds, message
|
12
|
+
Process.waitpid @pid
|
13
|
+
end_time = Time.now
|
14
|
+
assert end_time - @start_time >= seconds, message
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_killed_before seconds, message
|
18
|
+
Process.waitpid @pid
|
19
|
+
end_time = Time.now
|
20
|
+
assert end_time - @start_time < seconds, message
|
21
|
+
end
|
22
|
+
|
23
|
+
def do_fork &block
|
24
|
+
@pid = fork(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
@start_time = Time.now
|
29
|
+
sleep 1 # so the forked process gets to execute protected block
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestCromwell < Test::Unit::TestCase
|
4
|
+
|
5
|
+
# The tests below are hackish and slow, because I had to use sleep to find out whether
|
6
|
+
# forked process was killed or not. If you know a better (and portable!) way to do it,
|
7
|
+
# please let me know.
|
8
|
+
#
|
9
|
+
# On my OS X 10.5:
|
10
|
+
# - Process.kill always returns 1
|
11
|
+
# - Process.waitpid(@pid) always returns @pid
|
12
|
+
# - Process.waitpid(@pid, Process::WNOHANG) always returns nil
|
13
|
+
# so these methods cannot be used to determine if the forked process is still alive :(
|
14
|
+
|
15
|
+
PROCS = {
|
16
|
+
"block form" => proc {
|
17
|
+
Cromwell.protect("HUP") { sleep 3 }
|
18
|
+
},
|
19
|
+
"non block form" => proc {
|
20
|
+
Cromwell.protect "HUP"
|
21
|
+
sleep 3
|
22
|
+
Cromwell.unprotect
|
23
|
+
},
|
24
|
+
}
|
25
|
+
|
26
|
+
PROCS.each do |name, code|
|
27
|
+
context "general functionality of #{name}" do
|
28
|
+
setup do
|
29
|
+
do_fork &code
|
30
|
+
start
|
31
|
+
end
|
32
|
+
|
33
|
+
should "protect from given signal" do
|
34
|
+
Process.kill("HUP", @pid)
|
35
|
+
assert_slept_at_least 3, "child should not be killed before 3 seconds"
|
36
|
+
end
|
37
|
+
|
38
|
+
should "not protect from other signals" do
|
39
|
+
Process.kill("TERM", @pid)
|
40
|
+
assert_killed_before 3, "child should be killed before 3 seconds"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "general functionality" do
|
46
|
+
PROCS2 = {
|
47
|
+
"block form" => proc {
|
48
|
+
Cromwell.protect { sleep 3 }
|
49
|
+
},
|
50
|
+
"non block form" => proc {
|
51
|
+
Cromwell.protect
|
52
|
+
sleep 3
|
53
|
+
Cromwell.unprotect
|
54
|
+
},
|
55
|
+
}
|
56
|
+
|
57
|
+
PROCS2.each do |name, code|
|
58
|
+
context name do
|
59
|
+
should "protect from default signals" do
|
60
|
+
do_fork &code
|
61
|
+
start
|
62
|
+
|
63
|
+
Cromwell::DEFAULT_SIGNAL_LIST.each do |signal|
|
64
|
+
Process.kill(signal, @pid)
|
65
|
+
end
|
66
|
+
assert_slept_at_least 3, "child should not be killed before 3 seconds"
|
67
|
+
end
|
68
|
+
|
69
|
+
should "exit right after protected block" do
|
70
|
+
do_fork {
|
71
|
+
code.call
|
72
|
+
sleep 10
|
73
|
+
}
|
74
|
+
start
|
75
|
+
|
76
|
+
Process.kill("HUP", @pid)
|
77
|
+
assert_killed_before 4, "child should be killed after 3 seconds"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end # general functionality
|
82
|
+
|
83
|
+
context "method protect" do
|
84
|
+
should "set up trap with given signals" do
|
85
|
+
Cromwell.expects(:set_up_traps).with(["HUP", "TERM"])
|
86
|
+
Cromwell.protect "HUP", "TERM"
|
87
|
+
end
|
88
|
+
|
89
|
+
should "set up trap with default signals" do
|
90
|
+
Cromwell.expects(:set_up_traps).with(Cromwell::DEFAULT_SIGNAL_LIST)
|
91
|
+
Cromwell.protect
|
92
|
+
end
|
93
|
+
|
94
|
+
should "assign false to should_exit" do
|
95
|
+
Cromwell.protect
|
96
|
+
assert !Cromwell.should_exit?
|
97
|
+
end
|
98
|
+
|
99
|
+
should "assign true to protected" do
|
100
|
+
Cromwell.protect
|
101
|
+
assert Cromwell.protected?
|
102
|
+
end
|
103
|
+
|
104
|
+
end # method protect
|
105
|
+
|
106
|
+
context "method protect with block" do
|
107
|
+
should "yield to block" do
|
108
|
+
im_in_ur_blok_touchin_ur_vars = false
|
109
|
+
Cromwell.protect {
|
110
|
+
im_in_ur_blok_touchin_ur_vars = true
|
111
|
+
}
|
112
|
+
assert im_in_ur_blok_touchin_ur_vars
|
113
|
+
end
|
114
|
+
|
115
|
+
should "call unprotect after block is finished" do
|
116
|
+
Cromwell.protect { i = 1 }
|
117
|
+
assert !Cromwell.protected?
|
118
|
+
end
|
119
|
+
|
120
|
+
should "assign true to protected inside block" do
|
121
|
+
Cromwell.protect {
|
122
|
+
assert Cromwell.protected?
|
123
|
+
}
|
124
|
+
end
|
125
|
+
end # method protect with block
|
126
|
+
|
127
|
+
context "method unprotect" do
|
128
|
+
should "assign false to protected" do
|
129
|
+
Cromwell.protect
|
130
|
+
Cromwell.unprotect
|
131
|
+
assert !Cromwell.protected?
|
132
|
+
end
|
133
|
+
|
134
|
+
should "terminate if should_exit is true" do
|
135
|
+
Cromwell.send(:class_variable_set, "@@should_exit", true)
|
136
|
+
Cromwell.expects(:exit)
|
137
|
+
Cromwell.unprotect
|
138
|
+
end
|
139
|
+
end # method unprotect
|
140
|
+
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cromwell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Przemyslaw Kowalczyk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-03 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: "A very simple wrapper over Signal#trap method that allows you to easily protect your scripts from being killed while they are doing something that should not be interrupted (e.g. interacting with some non-transactional service) or is too costly to restart (e.g. long computations). "
|
26
|
+
email: szeryf@negativeiq.pl
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- cromwell.gemspec
|
42
|
+
- examples/example1.rb
|
43
|
+
- examples/example2.rb
|
44
|
+
- examples/example3.rb
|
45
|
+
- lib/cromwell.rb
|
46
|
+
- test/helper.rb
|
47
|
+
- test/test_cromwell.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://github.com/szeryf/cromwell
|
50
|
+
licenses: []
|
51
|
+
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --charset=UTF-8
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.3.5
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Lord Protector of your scripts
|
76
|
+
test_files:
|
77
|
+
- test/helper.rb
|
78
|
+
- test/test_cromwell.rb
|
79
|
+
- examples/example1.rb
|
80
|
+
- examples/example2.rb
|
81
|
+
- examples/example3.rb
|