resilient 0.4.0.beta1 → 0.5.1

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: d58c0c8c5444d5e583d6667644d2309fd1d76c75
4
- data.tar.gz: 92b729571e650001c424eac22b707eabb5f02806
2
+ SHA256:
3
+ metadata.gz: 2e439a74385ae133b7d375d90c75dcc302a1cd77a56d69facda7b04f2de07124
4
+ data.tar.gz: bceca2780a2b2b71ff1b0084db9e2c6ed206345114d2849d1da1c957e1995365
5
5
  SHA512:
6
- metadata.gz: de26fd92ca043468781a0cc18544d8904a37da9170c86f347141519e07e36304f005be212618f3dd909e379e3f694b638be480a483107a2ef9627440c70153d8
7
- data.tar.gz: 149eba99b8f5d6e3310d0ee4933d4b3ea23cdbee945730a74f4d3189665a6432fda514b1a40a43e7eb51f89c1cc16bc0368642e8e37850ad26724c18be8cd151
6
+ metadata.gz: 8a58ea0dce1f90dc61f4a3d34d0fa6f7ad7ca64fd246776a6696ee2e7ae1f2df9447b9f1ba394a914503320b40a1d3a2b326fd27e0e954e826fb985209233330
7
+ data.tar.gz: '088e66a7f364869da5da43f926dead2cd62af861baaa07b3a4615e521ac1ab75ecdeb4e6c7aee0b1f40ce2a692e66cca25cd2737bfe2c3fdc1635142fb96fb3a'
@@ -0,0 +1,28 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ matrix:
8
+ ruby: ['2.5', '2.6', '2.7']
9
+ steps:
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v2
12
+ - name: Do some action caching
13
+ uses: actions/cache@v1
14
+ with:
15
+ path: vendor/bundle
16
+ key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
17
+ restore-keys: |
18
+ ${{ runner.os }}-gem-
19
+ - name: Set up Ruby
20
+ uses: actions/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ - name: Install bundler
24
+ run: gem install bundler
25
+ - name: Run bundler
26
+ run: bundle install --jobs 4 --retry 3
27
+ - name: Run Tests
28
+ run: script/test
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ ## 0.5.1
2
+
3
+ * [Added two new events open_circuit and close_circuit](https://github.com/jnunemaker/resilient/pull/16) and [renamed them here](https://github.com/jnunemaker/resilient/commit/407e673eb5268912814398bebdeb173793d0af05).
4
+
5
+ ## 0.5.0
6
+
7
+ * [tweaked interface for metrics](https://github.com/jnunemaker/resilient/pull/15)
8
+ * [stop rounding error percentage](https://github.com/jnunemaker/resilient/commit/6cdade5c3fc5f75a6ffde87082771608d582026c)
9
+
10
+ ## prior to 0.5.0
11
+
12
+ * No changelog kept so just checkout the commits. There aren't too terrible many.
data/Gemfile CHANGED
@@ -1,5 +1,10 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec
3
3
 
4
- gem "guard", "~> 2.13.0"
5
- gem "guard-minitest", "~> 2.4.4"
4
+ gem "timecop", "~> 0.8"
5
+ gem "minitest", "~> 5.8"
6
+
7
+ group :development do
8
+ gem "guard", "~> 2.13.0"
9
+ gem "guard-minitest", "~> 2.4.4"
10
+ end
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Some tools to aid in resiliency in Ruby. For now, just a circuit breaker (~~stolen from~~ based on [hystrix](https://github.com/netflix/hystrix)). Soon much more...
4
4
 
5
- Nothing asynchronous or thread safe yet either, but open to it and would like to see more around it in the future.
5
+ Nothing asynchronous or thread safe yet either, but open to it and would like to see more around it in the future. See more here: [jnunemaker/resilient#18](https://github.com/jnunemaker/resilient/issues/18).
6
6
 
7
7
  ## Installation
8
8
 
@@ -29,7 +29,7 @@ require "resilient/circuit_breaker"
29
29
  # CircuitBreaker.new as `get` keeps a registry of circuits by key to prevent
30
30
  # creating multiple instances of the same circuit breaker for a key; not using
31
31
  # `get` means you would have multiple instances of the circuit breaker and thus
32
- # separate state and metrics; you can read more in examples/for_vs_new.rb
32
+ # separate state and metrics; you can read more in examples/get_vs_new.rb
33
33
  circuit_breaker = Resilient::CircuitBreaker.get("example")
34
34
  if circuit_breaker.allow_request?
35
35
  begin
@@ -82,14 +82,30 @@ circuit_breaker = Resilient::CircuitBreaker.get("example", {
82
82
  # etc etc etc
83
83
  ```
84
84
 
85
+ ## Default Properties
86
+
87
+ Property | Default | Notes
88
+ --------------------------------|------------------------|--------
89
+ **:force_open** | `false` | allows forcing the circuit open (stopping all requests)
90
+ **:force_closed** | `false` | allows ignoring errors and therefore never trip "open" (e.g. allow all traffic through); normal instrumentation will still happen, thus allowing you to "test" configuration live without impact
91
+ **:instrumenter** | `Instrumenters::Noop` | what to use to instrument all events that happen (e.g. `ActiveSupport::Notifications`)
92
+ **:sleep_window_seconds** | `5` | seconds after tripping circuit before allowing retry
93
+ **:request_volume_threshold** | `20` | number of requests that must be made within a statistical window before open/close decisions are made using stats
94
+ **:error_threshold_percentage** | `50` | % of "marks" that must be failed to trip the circuit
95
+ **:window_size_in_seconds** | `60` | number of seconds in the statistical window
96
+ **:bucket_size_in_seconds** | `10` | size of buckets in statistical window
97
+ **:metrics** | `Resilient::Metrics.new` | metrics instance used to keep track of success and failure
98
+
85
99
  ## Tests
86
100
 
87
- To ensure that you have a clean circuit for each test case, be sure to run the following in the setup for your tests (which resets each registered circuit breaker) either before every test case or at a minimum each test case that uses circuit breakers.
101
+ To ensure that you have clean circuit breakers for each test case, be sure to run the following in the setup for your tests (which resets the default registry and thus clears all the registered circuits) either before every test case or at a minimum each test case that uses circuit breakers.
88
102
 
89
103
  ```ruby
90
- Resilient::CircuitBreaker.reset
104
+ Resilient::CircuitBreaker::Registry.reset
91
105
  ```
92
106
 
107
+ **Note**: If you use a non-default registry, you'll need to reset that on your own. If you don't know what I'm talking about, you are fine.
108
+
93
109
  ## Development
94
110
 
95
111
  ```bash
@@ -0,0 +1,114 @@
1
+ # setting up load path
2
+ require "pathname"
3
+ root_path = Pathname(__FILE__).dirname.join("..").expand_path
4
+ lib_path = root_path.join("lib")
5
+ $:.unshift(lib_path)
6
+
7
+ # requiring stuff for this example
8
+ require "pp"
9
+ require "minitest/autorun"
10
+ require "resilient/circuit_breaker"
11
+ require "resilient/test/metrics_interface"
12
+
13
+ # Metrics class that closes circuit on every success call and opens circuit for
14
+ # sleep_window_seconds on every failure.
15
+ class BinaryMetrics
16
+ def initialize(options = {})
17
+ reset
18
+ end
19
+
20
+ def success
21
+ reset
22
+ nil
23
+ end
24
+
25
+ def failure
26
+ @closed = false
27
+ nil
28
+ end
29
+
30
+ def reset
31
+ @closed = true
32
+ nil
33
+ end
34
+
35
+ def under_request_volume_threshold?(request_volume_threshold)
36
+ false
37
+ end
38
+
39
+ def under_error_threshold_percentage?(error_threshold_percentage)
40
+ @closed
41
+ end
42
+ end
43
+
44
+ class BinaryMetricsTest < Minitest::Test
45
+ def setup
46
+ @object = BinaryMetrics.new
47
+ end
48
+
49
+ include Resilient::Test::MetricsInterface
50
+ end
51
+
52
+ circuit_breaker = Resilient::CircuitBreaker.get("example", {
53
+ sleep_window_seconds: 1,
54
+ metrics: BinaryMetrics.new,
55
+ })
56
+
57
+ # success
58
+ if circuit_breaker.allow_request?
59
+ begin
60
+ puts "do expensive thing"
61
+ circuit_breaker.success
62
+ rescue => boom
63
+ # won't get here in this example
64
+ circuit_breaker.failure
65
+ end
66
+ else
67
+ raise "will not get here"
68
+ end
69
+
70
+ # failure
71
+ if circuit_breaker.allow_request?
72
+ begin
73
+ raise
74
+ rescue => boom
75
+ circuit_breaker.failure
76
+ puts "failed slow, do fallback"
77
+ end
78
+ else
79
+ raise "will not get here"
80
+ end
81
+
82
+ # fail fast
83
+ if circuit_breaker.allow_request?
84
+ raise "will not get here"
85
+ else
86
+ puts "failed fast, do fallback"
87
+ end
88
+
89
+ start = Time.now
90
+
91
+ while (Time.now - start) < 3
92
+ if circuit_breaker.allow_request?
93
+ puts "doing a single attempt as we've failed fast for sleep_window_seconds"
94
+ break
95
+ else
96
+ puts "failed fast, do fallback"
97
+ end
98
+ sleep rand(0.1)
99
+ end
100
+
101
+ if circuit_breaker.allow_request?
102
+ raise "will not get here"
103
+ else
104
+ puts "request denied because single request has not been marked success yet"
105
+ end
106
+
107
+ puts "marking single request as success"
108
+ circuit_breaker.success
109
+
110
+ if circuit_breaker.allow_request?
111
+ puts "circuit reset and back closed now, allowing requests"
112
+ else
113
+ raise "will not get here"
114
+ end
@@ -2,15 +2,11 @@ require "resilient/key"
2
2
  require "resilient/circuit_breaker/metrics"
3
3
  require "resilient/circuit_breaker/properties"
4
4
  require "resilient/circuit_breaker/registry"
5
+ require "forwardable"
5
6
 
6
7
  module Resilient
7
8
  class CircuitBreaker
8
- # Public: Resets all registered circuit breakers (and thus their state/metrics).
9
- # Useful for ensuring a clean environment between tests. If you are using a
10
- # registry other than the default, you will need to handle resets on your own.
11
- def self.reset
12
- Registry.default.reset
13
- end
9
+ extend Forwardable
14
10
 
15
11
  # Public: Returns an instance of circuit breaker based on key and registry.
16
12
  # Default registry is used if none is provided. If key does not exist, it is
@@ -18,11 +14,11 @@ module Resilient
18
14
  # allocating a new instance in order to ensure that state/metrics are the
19
15
  # same per key.
20
16
  #
21
- # See #initialize for docs on key, properties and metrics.
22
- def self.get(key, properties = nil, metrics = nil, registry = nil)
17
+ # See #initialize for docs on key and properties.
18
+ def self.get(key, properties = nil, registry = nil)
23
19
  key = Key.wrap(key)
24
20
  (registry || Registry.default).fetch(key) {
25
- new(key, properties, metrics)
21
+ new(key, properties)
26
22
  }
27
23
  end
28
24
 
@@ -33,10 +29,11 @@ module Resilient
33
29
  end
34
30
  end
35
31
 
32
+ def_delegator :@properties, :metrics
33
+
36
34
  attr_reader :key
37
35
  attr_reader :open
38
36
  attr_reader :opened_or_last_checked_at_epoch
39
- attr_reader :metrics
40
37
  attr_reader :properties
41
38
 
42
39
  # Private: Builds new instance of a CircuitBreaker.
@@ -48,32 +45,14 @@ module Resilient
48
45
  # circuit breaker should behave. Optional. Defaults to new
49
46
  # Resilient::CircuitBreaker::Properties instance.
50
47
  #
51
- # metrics - The object that stores successes and failures. Optional.
52
- # Defaults to new Resilient::CircuitBreaker::Metrics instance
53
- # based on window size and bucket size properties.
54
- #
55
48
  # Returns CircuitBreaker instance.
56
- def initialize(key, properties = nil, metrics = nil)
49
+ def initialize(key, properties = nil)
57
50
  raise ArgumentError, "key argument is required" if key.nil?
58
51
 
59
52
  @key = Key.wrap(key)
53
+ @properties = Properties.wrap(properties)
60
54
  @open = false
61
55
  @opened_or_last_checked_at_epoch = 0
62
-
63
- @properties = if properties
64
- Properties.wrap(properties)
65
- else
66
- Properties.new
67
- end
68
-
69
- @metrics = if metrics
70
- metrics
71
- else
72
- Metrics.new({
73
- window_size_in_seconds: @properties.window_size_in_seconds,
74
- bucket_size_in_seconds: @properties.bucket_size_in_seconds,
75
- })
76
- end
77
56
  end
78
57
 
79
58
  def allow_request?
@@ -102,7 +81,7 @@ module Resilient
102
81
  payload[:closed_the_circuit] = true
103
82
  close_circuit
104
83
  else
105
- @metrics.success
84
+ metrics.success
106
85
  end
107
86
  nil
108
87
  }
@@ -110,7 +89,7 @@ module Resilient
110
89
 
111
90
  def failure
112
91
  instrument("resilient.circuit_breaker.failure", key: @key) { |payload|
113
- @metrics.failure
92
+ metrics.failure
114
93
  nil
115
94
  }
116
95
  end
@@ -119,7 +98,7 @@ module Resilient
119
98
  instrument("resilient.circuit_breaker.reset", key: @key) { |payload|
120
99
  @open = false
121
100
  @opened_or_last_checked_at_epoch = 0
122
- @metrics.reset
101
+ metrics.reset
123
102
  nil
124
103
  }
125
104
  end
@@ -127,22 +106,29 @@ module Resilient
127
106
  private
128
107
 
129
108
  def open_circuit
130
- @opened_or_last_checked_at_epoch = Time.now.to_i
131
- @open = true
109
+ instrument("resilient.circuit_breaker.open_circuit", key: @key) { |payload|
110
+ @opened_or_last_checked_at_epoch = Time.now.to_i
111
+ @open = true
112
+ payload[:open] = @open
113
+ }
132
114
  end
133
115
 
134
116
  def close_circuit
135
- @open = false
136
- @opened_or_last_checked_at_epoch = 0
137
- @metrics.reset
117
+ instrument("resilient.circuit_breaker.close_circuit", key: @key) { |payload|
118
+ @open = false
119
+ payload[:open] = @open
120
+ payload[:interval] = Time.now.to_i - @opened_or_last_checked_at_epoch
121
+ @opened_or_last_checked_at_epoch = 0
122
+ }
123
+ metrics.reset
138
124
  end
139
125
 
140
126
  def under_request_volume_threshold?
141
- @metrics.requests < @properties.request_volume_threshold
127
+ metrics.under_request_volume_threshold?(@properties.request_volume_threshold)
142
128
  end
143
129
 
144
130
  def under_error_threshold_percentage?
145
- @metrics.error_percentage < @properties.error_threshold_percentage
131
+ metrics.under_error_threshold_percentage?(@properties.error_threshold_percentage)
146
132
  end
147
133
 
148
134
  def open?
@@ -30,6 +30,14 @@ module Resilient
30
30
  @buckets = []
31
31
  end
32
32
 
33
+ def under_request_volume_threshold?(request_volume_threshold)
34
+ requests < request_volume_threshold
35
+ end
36
+
37
+ def under_error_threshold_percentage?(error_threshold_percentage)
38
+ error_percentage < error_threshold_percentage
39
+ end
40
+
33
41
  def success
34
42
  @storage.increment(current_bucket, StorageSuccessKeys)
35
43
  prune_buckets
@@ -42,15 +50,12 @@ module Resilient
42
50
  nil
43
51
  end
44
52
 
45
- def successes
46
- prune_buckets
47
- @storage.sum(@buckets, :successes)[:successes]
53
+ def reset
54
+ @storage.prune(@buckets, StorageKeys)
55
+ nil
48
56
  end
49
57
 
50
- def failures
51
- prune_buckets
52
- @storage.sum(@buckets, :failures)[:failures]
53
- end
58
+ private
54
59
 
55
60
  def requests
56
61
  prune_buckets
@@ -71,16 +76,9 @@ module Resilient
71
76
  requests = successes + failures
72
77
  return 0 if failures == 0 || requests == 0
73
78
 
74
- ((failures / requests.to_f) * 100).round
79
+ (failures / requests.to_f) * 100
75
80
  end
76
81
 
77
- def reset
78
- @storage.prune(@buckets, StorageKeys)
79
- nil
80
- end
81
-
82
- private
83
-
84
82
  def current_bucket(timestamp = Time.now.to_i)
85
83
  bucket = @buckets.detect { |bucket| bucket.include?(timestamp) }
86
84
  return bucket if bucket
@@ -12,6 +12,8 @@ module Resilient
12
12
  hash_or_instance
13
13
  when Hash
14
14
  new(hash_or_instance)
15
+ when NilClass
16
+ new
15
17
  else
16
18
  raise TypeError, "properties must be Hash or Resilient::Properties instance"
17
19
  end
@@ -45,6 +47,9 @@ module Resilient
45
47
  # size of buckets in statistical window
46
48
  attr_reader :bucket_size_in_seconds
47
49
 
50
+ # metrics instance used to keep track of success and failure
51
+ attr_reader :metrics
52
+
48
53
  def initialize(options = {})
49
54
  @force_open = options.fetch(:force_open, false)
50
55
  @force_closed = options.fetch(:force_closed, false)
@@ -64,6 +69,13 @@ module Resilient
64
69
  "window_size_in_seconds must be perfectly divisible by" +
65
70
  " bucket_size_in_seconds in order to evenly partition the buckets"
66
71
  end
72
+
73
+ @metrics = options.fetch(:metrics) {
74
+ Metrics.new({
75
+ window_size_in_seconds: @window_size_in_seconds,
76
+ bucket_size_in_seconds: @bucket_size_in_seconds,
77
+ })
78
+ }
67
79
  end
68
80
  end
69
81
  end
@@ -11,11 +11,19 @@ module Resilient
11
11
  @default = value
12
12
  end
13
13
 
14
+ # Public: Reset the default registry. This completely wipes all instances
15
+ # by swapping out the default registry for a new one and letting the old
16
+ # one get GC'd. Useful in tests to get a completely clean slate.
17
+ def self.reset
18
+ default.reset
19
+ end
20
+
14
21
  def initialize(source = nil)
15
22
  @source = source || {}
16
23
  end
17
24
 
18
- # Setup default to new instance.
25
+ # Setup new instance as default. Needs to be after initialize so hash gets
26
+ # initialize correctly.
19
27
  @default = new
20
28
 
21
29
  # Internal: To be used by CircuitBreaker to either get an instance for a
@@ -38,7 +46,7 @@ module Resilient
38
46
  # breakers, which should only really be used for cleaning up in
39
47
  # test environment.
40
48
  def reset
41
- @source.each_value(&:reset)
49
+ @source = {}
42
50
  nil
43
51
  end
44
52
  end
@@ -20,18 +20,17 @@ module Resilient
20
20
  end
21
21
 
22
22
  def test_reset
23
- bar_value = Minitest::Mock.new
24
- wick_value = Minitest::Mock.new
25
- bar_value.expect :reset, nil, []
26
- wick_value.expect :reset, nil, []
27
-
28
- @object.fetch("foo") { bar_value }
29
- @object.fetch("baz") { wick_value }
23
+ original_foo = @object.fetch("foo") { Object.new }
24
+ original_bar = @object.fetch("bar") { Object.new }
30
25
 
31
26
  assert_nil @object.reset
32
27
 
33
- bar_value.verify
34
- wick_value.verify
28
+ foo = @object.fetch("foo") { Object.new }
29
+ bar = @object.fetch("bar") { Object.new }
30
+
31
+ # assert that the objects before and after reset are not the same object
32
+ refute original_foo.equal?(foo)
33
+ refute original_bar.equal?(bar)
35
34
  end
36
35
 
37
36
  def test_reset_empty_registry
@@ -1,6 +1,16 @@
1
1
  module Resilient
2
2
  class Test
3
3
  module MetricsInterface
4
+ def test_responds_to_under_request_volume_threshold_predicate
5
+ assert_respond_to @object, :under_request_volume_threshold?
6
+ assert_equal 1, @object.method(:under_request_volume_threshold?).arity
7
+ end
8
+
9
+ def test_responds_to_under_error_threshold_percentage_predicate
10
+ assert_respond_to @object, :under_error_threshold_percentage?
11
+ assert_equal 1, @object.method(:under_error_threshold_percentage?).arity
12
+ end
13
+
4
14
  def test_responds_to_success
5
15
  assert_respond_to @object, :success
6
16
  end
@@ -17,22 +27,6 @@ module Resilient
17
27
  assert_nil @object.failure
18
28
  end
19
29
 
20
- def test_responds_to_successes
21
- assert_respond_to @object, :successes
22
- end
23
-
24
- def test_responds_to_failures
25
- assert_respond_to @object, :failures
26
- end
27
-
28
- def test_responds_to_requests
29
- assert_respond_to @object, :requests
30
- end
31
-
32
- def test_responds_to_error_percentage
33
- assert_respond_to @object, :error_percentage
34
- end
35
-
36
30
  def test_responds_to_reset
37
31
  assert_respond_to @object, :reset
38
32
  end
@@ -1,3 +1,3 @@
1
1
  module Resilient
2
- VERSION = "0.4.0.beta1"
2
+ VERSION = "0.5.1"
3
3
  end
data/resilient.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.10"
21
+ spec.add_development_dependency "bundler", "< 3.0"
22
22
  spec.add_development_dependency "minitest", "~> 5.8"
23
23
  spec.add_development_dependency "timecop", "~> 0.8.0"
24
24
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resilient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.beta1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-02 00:00:00.000000000 Z
11
+ date: 2021-04-30 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.10'
19
+ version: '3.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.10'
26
+ version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -59,13 +59,15 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/workflows/ci.yml"
62
63
  - ".gitignore"
63
- - ".travis.yml"
64
+ - CHANGELOG.md
64
65
  - Gemfile
65
66
  - Guardfile
66
67
  - LICENSE.txt
67
68
  - README.md
68
69
  - examples/basic.rb
70
+ - examples/binary.rb
69
71
  - examples/get_vs_new.rb
70
72
  - examples/long_running.rb
71
73
  - lib/resilient.rb
@@ -108,12 +110,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
110
  version: '0'
109
111
  required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  requirements:
111
- - - ">"
113
+ - - ">="
112
114
  - !ruby/object:Gem::Version
113
- version: 1.3.1
115
+ version: '0'
114
116
  requirements: []
115
- rubyforge_project:
116
- rubygems_version: 2.4.5.1
117
+ rubygems_version: 3.0.3
117
118
  signing_key:
118
119
  specification_version: 4
119
120
  summary: toolkit for resilient ruby apps
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.0.0
4
- - 2.1.4
5
- - 2.2.3
6
- before_install: gem install bundler -v 1.10.6
7
- script: script/test