supervision 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +22 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +8 -0
- data/lib/supervision/atomic.rb +41 -0
- data/lib/supervision/circuit_breaker.rb +89 -0
- data/lib/supervision/circuit_control.rb +182 -0
- data/lib/supervision/circuit_monitor.rb +13 -0
- data/lib/supervision/circuit_system.rb +16 -0
- data/lib/supervision/configuration.rb +73 -0
- data/lib/supervision/factory.rb +7 -0
- data/lib/supervision/registry.rb +69 -0
- data/lib/supervision/time_dsl.rb +36 -0
- data/lib/supervision/version.rb +5 -0
- data/lib/supervision.rb +67 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/atomic_spec.rb +30 -0
- data/spec/unit/circuit_breaker_spec.rb +102 -0
- data/spec/unit/circuit_control_spec.rb +72 -0
- data/spec/unit/circuit_monitor_spec.rb +6 -0
- data/spec/unit/configuration_spec.rb +14 -0
- data/spec/unit/initialize_spec.rb +55 -0
- data/spec/unit/registry_spec.rb +30 -0
- data/spec/unit/time_dsl_spec.rb +31 -0
- data/supervision.gemspec +23 -0
- data/tasks/console.rake +10 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- metadata +117 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Supervision
|
4
|
+
# A class responsible for registering/unregistering circuits
|
5
|
+
class Registry
|
6
|
+
|
7
|
+
# Initialize a Registry
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
def initialize
|
11
|
+
@lock = Mutex.new
|
12
|
+
@map = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Register a circuit
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def []=(name, circuit)
|
19
|
+
unless circuit.is_a?(CircuitBreaker)
|
20
|
+
raise TypeError, 'not a circuit'
|
21
|
+
end
|
22
|
+
@lock.synchronize do
|
23
|
+
@map[name.to_sym] = circuit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve a circuit by name
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def [](name)
|
31
|
+
@lock.synchronize do
|
32
|
+
@map[name.to_sym]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Remove from registry
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def delete(name)
|
40
|
+
@lock.synchronize do
|
41
|
+
@map.delete name.to_sym
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :register, :[]=
|
46
|
+
alias_method :get, :[]
|
47
|
+
alias_method :unregister, :delete
|
48
|
+
|
49
|
+
# Check if circuit is in registry
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def registered?(name)
|
53
|
+
names.include?(name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def names
|
57
|
+
@lock.synchronize { @map.keys }
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear
|
61
|
+
hash = nil
|
62
|
+
@lock.synchronize do
|
63
|
+
hash = @map.dup
|
64
|
+
@map.clear
|
65
|
+
end
|
66
|
+
hash
|
67
|
+
end
|
68
|
+
end # Registry
|
69
|
+
end # Supervision
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Supervision
|
4
|
+
# A mixin to define time related helpers
|
5
|
+
module TimeDSL
|
6
|
+
def millisecond
|
7
|
+
self / 1000.0
|
8
|
+
end
|
9
|
+
alias_method :milliseconds, :millisecond
|
10
|
+
alias_method :milli , :millisecond
|
11
|
+
alias_method :millis , :millisecond
|
12
|
+
|
13
|
+
def second
|
14
|
+
self * 1
|
15
|
+
end
|
16
|
+
alias_method :seconds, :second
|
17
|
+
alias_method :sec , :second
|
18
|
+
alias_method :secs , :second
|
19
|
+
|
20
|
+
def minute
|
21
|
+
self * 60
|
22
|
+
end
|
23
|
+
alias_method :minutes, :minute
|
24
|
+
alias_method :min , :minute
|
25
|
+
alias_method :mins , :minute
|
26
|
+
|
27
|
+
def hour
|
28
|
+
self * 3600
|
29
|
+
end
|
30
|
+
alias_method :hours, :hour
|
31
|
+
end # TimeDSL
|
32
|
+
end # Supervision
|
33
|
+
|
34
|
+
unless Numeric.method_defined?(:second)
|
35
|
+
Numeric.send(:include, Supervision::TimeDSL)
|
36
|
+
end
|
data/lib/supervision.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "thread"
|
4
|
+
require "timeout"
|
5
|
+
require "finite_machine"
|
6
|
+
|
7
|
+
require "supervision/version"
|
8
|
+
require "supervision/atomic"
|
9
|
+
require "supervision/time_dsl"
|
10
|
+
require "supervision/configuration"
|
11
|
+
require "supervision/registry"
|
12
|
+
require "supervision/circuit_control"
|
13
|
+
require "supervision/circuit_breaker"
|
14
|
+
require "supervision/circuit_system"
|
15
|
+
require "supervision/circuit_monitor"
|
16
|
+
|
17
|
+
module Supervision
|
18
|
+
# Generic error
|
19
|
+
SupervisionError = Class.new(::StandardError)
|
20
|
+
|
21
|
+
# Raised when circuit opens
|
22
|
+
CircuitBreakerOpenError = Class.new(SupervisionError)
|
23
|
+
|
24
|
+
TypeError = Class.new(SupervisionError)
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def included(base)
|
28
|
+
base.send :extend, ClassMethods
|
29
|
+
end
|
30
|
+
|
31
|
+
def configuration
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def init
|
36
|
+
@circuit_system = CircuitSystem.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def circuit_system
|
40
|
+
Thread.current[:supervision_circuit_system] ||= @circuit_system
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a new circuit breaker
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def new(name = nil, options = {}, &block)
|
47
|
+
name ? supervise_as(name, options, &block) : supervise(options, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
def supervise(options = {}, &block)
|
53
|
+
CircuitBreaker.new(options, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def supervise_as(name, options = {}, &block)
|
57
|
+
circuit = supervise(options, &block)
|
58
|
+
Supervision.circuit_system[name] = circuit
|
59
|
+
send(:define_method, name) { |*args| circuit.call(args) }
|
60
|
+
circuit
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
extend ClassMethods
|
65
|
+
end # Supervision
|
66
|
+
|
67
|
+
Supervision.init
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'supervision'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
module Helpers
|
7
|
+
def wait_for(duration = nil)
|
8
|
+
Timeout.timeout 1 do
|
9
|
+
sleep(duration || 0.01) until yield
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
config.filter_run :focus
|
18
|
+
config.order = 'random'
|
19
|
+
config.include Helpers
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision::Atomic do
|
6
|
+
|
7
|
+
let(:object) { described_class }
|
8
|
+
|
9
|
+
it "sets the value to nil" do
|
10
|
+
atomic = object.new
|
11
|
+
expect(atomic.value).to eql(nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sets the value" do
|
15
|
+
atomic = object.new(1)
|
16
|
+
expect(atomic.value).to eql(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "set value" do
|
20
|
+
atomic = object.new
|
21
|
+
atomic.value = 1000
|
22
|
+
expect(atomic.value).to eql(1000)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "updates current value" do
|
26
|
+
atomic = object.new(1000)
|
27
|
+
new_value = atomic.update { |v| v + 1 }
|
28
|
+
expect(new_value).to eql(1001)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision::CircuitBreaker do
|
6
|
+
|
7
|
+
let(:dangerous_call_timeout) { sleep 1 }
|
8
|
+
|
9
|
+
let(:dangerous_call_error) { raise StandardError }
|
10
|
+
|
11
|
+
let(:safe_call) { 'value' }
|
12
|
+
|
13
|
+
let(:object) { described_class }
|
14
|
+
|
15
|
+
|
16
|
+
context 'when closed' do
|
17
|
+
it "successfully calls the method" do
|
18
|
+
circuit = object.new call_timeout: 1.milli do |arg|
|
19
|
+
arg == :danger ? dangerouse_call_error : safe_call
|
20
|
+
end
|
21
|
+
expect(circuit.call(:safe)).to eql(safe_call)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "increments a failure counter for exceptions" do
|
25
|
+
circuit = object.new call_timeout: 1.milli do
|
26
|
+
arg == :danger ? dangerouse_call_error : safe_call
|
27
|
+
end
|
28
|
+
circuit.call(:danger)
|
29
|
+
expect(circuit.control.failure_count).to eql(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "increments a failure counter for calls exceeding :call_timeout" do
|
33
|
+
circuit = object.new call_timeout: 1.milli do
|
34
|
+
dangerous_call_timeout
|
35
|
+
end
|
36
|
+
circuit.call
|
37
|
+
expect(circuit.control.failure_count).to eql(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when open' do
|
42
|
+
it "fails all calls with a CircuitBreakerOpenError" do
|
43
|
+
circuit = object.new max_failures: 2, reset_timeout: 1.sec do
|
44
|
+
dangerous_call_error
|
45
|
+
end
|
46
|
+
circuit.call
|
47
|
+
circuit.call
|
48
|
+
expect { circuit.call }.to raise_error(Supervision::CircuitBreakerOpenError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "enters a :half_open state after the :reset_timeout" do
|
52
|
+
circuit = object.new reset_timeout: 0.1.sec, max_failures: 0 do
|
53
|
+
dangerous_call_error
|
54
|
+
end
|
55
|
+
expect { circuit.call }.to raise_error(Supervision::CircuitBreakerOpenError)
|
56
|
+
expect(circuit.control.current).to eq(:open)
|
57
|
+
sleep 0.2
|
58
|
+
expect(circuit.control.current).to eq(:half_open)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when half open' do
|
63
|
+
it "resets the breaker back to :closed state on successful call" do
|
64
|
+
circuit = object.new reset_timeout: 100.milli, max_failures: 0 do |arg|
|
65
|
+
arg == :danger ? dangerous_call_error : safe_call
|
66
|
+
end
|
67
|
+
expect {
|
68
|
+
circuit.call(:danger)
|
69
|
+
}.to raise_error(Supervision::CircuitBreakerOpenError)
|
70
|
+
expect(circuit.control.current).to eql(:open)
|
71
|
+
sleep 0.2
|
72
|
+
expect(circuit.control.current).to eql(:half_open)
|
73
|
+
circuit.call(:safe)
|
74
|
+
expect(circuit.control.current).to eql(:closed)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when with callback' do
|
79
|
+
it "notifies about successful call" do
|
80
|
+
callbacks = []
|
81
|
+
circuit = object.new do safe_call end
|
82
|
+
circuit.on_success { callbacks << 'on_success' }
|
83
|
+
circuit.call
|
84
|
+
expect(callbacks).to eql(["on_success"])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "notifies about failed call" do
|
88
|
+
callbacks = []
|
89
|
+
circuit = object.new do dangerous_call_error end
|
90
|
+
circuit.on_failure { callbacks << 'on_failure'}
|
91
|
+
circuit.before { callbacks << 'before'}
|
92
|
+
circuit.call
|
93
|
+
expect(callbacks).to eql(['before', 'on_failure'])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "fails fast with unknown config option" do
|
98
|
+
expect {
|
99
|
+
object.new max_fail: 2 do safe_call end
|
100
|
+
}.to raise_error(ArgumentError)
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision::CircuitControl do
|
6
|
+
let(:object) { described_class }
|
7
|
+
|
8
|
+
let(:max_failures) { 1 }
|
9
|
+
|
10
|
+
let(:reset_timeout) { 0.1.sec }
|
11
|
+
|
12
|
+
subject(:control) {
|
13
|
+
object.new max_failures: max_failures,
|
14
|
+
reset_timeout: reset_timeout
|
15
|
+
}
|
16
|
+
|
17
|
+
context 'when closed' do
|
18
|
+
it "resets the failure count on success" do
|
19
|
+
expect(control.failure_count).to eql(0)
|
20
|
+
expect(control.fsm.current).to eql(:closed)
|
21
|
+
control.record_failure
|
22
|
+
expect(control.failure_count).to eql(1)
|
23
|
+
control.reset_failure
|
24
|
+
expect(control.failure_count).to eql(0)
|
25
|
+
expect(control.fsm.current).to eql(:closed)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "increments failure count on exceptions and trips the wire" do
|
29
|
+
expect(control.failure_count).to eql(0)
|
30
|
+
expect(control.fsm.current).to eql(:closed)
|
31
|
+
|
32
|
+
control.handle
|
33
|
+
expect(control.failure_count).to eql(1)
|
34
|
+
expect(control.fsm.current).to eql(:closed)
|
35
|
+
|
36
|
+
expect{ control.handle }.to raise_error(Supervision::CircuitBreakerOpenError)
|
37
|
+
expect(control.failure_count).to eql(2)
|
38
|
+
expect(control.fsm.current).to eql(:open)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when open' do
|
43
|
+
it "fails all calls fast with CircuitBreakerOpenError" do
|
44
|
+
control.fsm.state = :open
|
45
|
+
expect { control.handle }.to raise_error(Supervision::CircuitBreakerOpenError)
|
46
|
+
expect(control.fsm.current).to eql(:open)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "enters :half_open state after the configured :reset_timeout" do
|
50
|
+
control.record_failure
|
51
|
+
expect{ control.handle }.to raise_error(Supervision::CircuitBreakerOpenError)
|
52
|
+
sleep 0.2
|
53
|
+
expect(control.fsm.current).to eql(:half_open)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when half open' do
|
58
|
+
before { control.fsm.state = :half_open }
|
59
|
+
|
60
|
+
it "resets the breaker back to :closed state on successful call" do
|
61
|
+
control.record_success
|
62
|
+
expect(control.fsm.current).to eql(:closed)
|
63
|
+
expect(control.failure_count).to eql(0)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "trips the breaker back to :open state on failed call" do
|
67
|
+
expect { control.handle }.to raise_error(Supervision::CircuitBreakerOpenError)
|
68
|
+
expect(control.failure_count).to eql(1)
|
69
|
+
expect(control.fsm.current).to eql(:open)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision::Configuration do
|
6
|
+
|
7
|
+
subject(:config) { described_class.new }
|
8
|
+
|
9
|
+
it { expect(config.max_failures).to eql(5) }
|
10
|
+
|
11
|
+
it { expect(config.call_timeout).to eql(0.01) }
|
12
|
+
|
13
|
+
it { expect(config.reset_timeout).to eql(0.1) }
|
14
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision do
|
6
|
+
|
7
|
+
context "when used as instance" do
|
8
|
+
it "permits options configuration" do
|
9
|
+
supervision = Supervision.new { }
|
10
|
+
supervision.configure do
|
11
|
+
call_timeout 1.sec
|
12
|
+
max_failures 10
|
13
|
+
end
|
14
|
+
expect(supervision.control.max_failures).to eql(10)
|
15
|
+
expect(supervision.control.call_timeout).to eql(1.sec)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows to supervise call" do
|
19
|
+
called = []
|
20
|
+
supervision = Supervision.supervise { called << 'method_call'}
|
21
|
+
supervision.call
|
22
|
+
expect(called).to eql(['method_call'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "registers named supervision" do
|
26
|
+
called = []
|
27
|
+
supervision = Supervision.supervise_as(:danger) { called << 'method_call'}
|
28
|
+
supervision.call
|
29
|
+
expect(called).to eql(['method_call'])
|
30
|
+
expect(Supervision.circuit_system[:danger]).to eql(supervision)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when included as module" do
|
35
|
+
class RemoteApi
|
36
|
+
include Supervision
|
37
|
+
|
38
|
+
def danger_call(state)
|
39
|
+
state == :safe ? "hello" : raise(StandardError)
|
40
|
+
end
|
41
|
+
supervise_as(:danger, max_failures: 2) { |args| danger_call(args) }
|
42
|
+
|
43
|
+
def wrapped_danger
|
44
|
+
supervise { |args| danger_call(args) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "allows to call registerd circuit" do
|
49
|
+
api = RemoteApi.new
|
50
|
+
api.danger
|
51
|
+
api.danger
|
52
|
+
expect { api.danger }.to raise_error(Supervision::CircuitBreakerOpenError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end # Supervision
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Supervision::Registry do
|
4
|
+
|
5
|
+
let(:circuit) { Supervision.supervise { } }
|
6
|
+
|
7
|
+
subject(:registry) { described_class.new }
|
8
|
+
|
9
|
+
it "registers" do
|
10
|
+
registry[:danger] = circuit
|
11
|
+
expect(registry[:danger]).to eql(circuit)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "refuses to add non circuit object" do
|
15
|
+
expect {
|
16
|
+
registry[:danger] = Object.new
|
17
|
+
}.to raise_error(Supervision::TypeError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "" do
|
21
|
+
registry[:danger] = circuit
|
22
|
+
expect(registry.names).to eql([:danger])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "" do
|
26
|
+
registry[:danger] = circuit
|
27
|
+
registry.delete(:danger)
|
28
|
+
expect(registry.names).to be_empty
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Supervision::TimeDSL do
|
6
|
+
it "defines millisecond/milliseconds/milli/millis" do
|
7
|
+
expect(1.millisecond).to eql(0.001)
|
8
|
+
expect(1.milli).to eql(0.001)
|
9
|
+
expect(10.milliseconds).to eql(0.01)
|
10
|
+
expect(10.millis).to eql(0.01)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "defines second/seconds" do
|
14
|
+
expect(1.second).to eql(1)
|
15
|
+
expect(10.seconds).to eql(10)
|
16
|
+
expect(1.sec).to eql(1)
|
17
|
+
expect(10.secs).to eql(10)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "defines minute/minutes" do
|
21
|
+
expect(1.minute).to eql(60)
|
22
|
+
expect(10.minutes).to eql(600)
|
23
|
+
expect(1.min).to eql(60)
|
24
|
+
expect(10.mins).to eql(600)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "defines hours/hours" do
|
28
|
+
expect(1.hour).to eql(3600)
|
29
|
+
expect(2.hours).to eql(7200)
|
30
|
+
end
|
31
|
+
end
|
data/supervision.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'supervision/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "supervision"
|
8
|
+
spec.version = Supervision::VERSION
|
9
|
+
spec.authors = ["Piotr Murach"]
|
10
|
+
spec.email = [""]
|
11
|
+
spec.summary = %q{Write distributed systems that are resilient and self-heal.}
|
12
|
+
spec.description = %q{Write distributed systems that are resilient and self-heal. Remote calls can fail or hang indefinietly without a response. Supervision will help to isolate failure and keep individual components from bringing down the whole system.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "finite_machine", "~> 0.4"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
23
|
+
end
|
data/tasks/console.rake
ADDED
data/tasks/coverage.rake
ADDED
data/tasks/spec.rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc 'Run all specs'
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
8
|
+
task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :spec do
|
12
|
+
desc 'Run unit specs'
|
13
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
14
|
+
task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run integration specs'
|
18
|
+
RSpec::Core::RakeTask.new(:integration) do |task|
|
19
|
+
task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
rescue LoadError
|
24
|
+
%w[spec spec:unit spec:integration].each do |name|
|
25
|
+
task name do
|
26
|
+
$stderr.puts "In order to run #{name}, do `gem install rspec`"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|