gracefully 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +30 -0
  3. data/Gemfile +2 -0
  4. data/README.md +34 -1
  5. data/lib/gracefully.rb +22 -4
  6. data/lib/gracefully/all.rb +6 -0
  7. data/lib/gracefully/circuit_breaker.rb +88 -0
  8. data/lib/gracefully/command.rb +23 -0
  9. data/lib/gracefully/command_disabled_error.rb +6 -0
  10. data/lib/gracefully/consecutive_failures_based_health.rb +75 -0
  11. data/lib/gracefully/counter.rb +29 -0
  12. data/lib/gracefully/degradable.rb +36 -0
  13. data/lib/gracefully/{feature.rb → degradable_command.rb} +4 -3
  14. data/lib/gracefully/degradable_command_builder.rb +23 -0
  15. data/lib/gracefully/error.rb +37 -0
  16. data/lib/gracefully/health.rb +29 -0
  17. data/lib/gracefully/mutex_based_synchronized_counter.rb +30 -0
  18. data/lib/gracefully/retried_command.rb +25 -0
  19. data/lib/gracefully/short_circuited_command.rb +23 -0
  20. data/lib/gracefully/timed_command.rb +19 -0
  21. data/lib/gracefully/togglable_command.rb +20 -0
  22. data/lib/gracefully/try.rb +6 -34
  23. data/lib/gracefully/version.rb +1 -1
  24. data/spec/circuit_breaker_spec.rb +142 -0
  25. data/spec/command_spec.rb +34 -0
  26. data/spec/consecutive_failures_based_health_spec.rb +78 -0
  27. data/spec/degradable_spec.rb +49 -0
  28. data/spec/gracefully_spec.rb +136 -10
  29. data/spec/mutex_based_synchronized_counter_spec.rb +50 -0
  30. data/spec/retried_command_spec.rb +93 -0
  31. data/spec/short_circuited_command_spec.rb +136 -0
  32. data/spec/spec_helper.rb +3 -0
  33. data/spec/timecop_helper.rb +3 -0
  34. data/spec/timed_command_spec.rb +73 -0
  35. data/spec/togglable_command_spec.rb +31 -0
  36. metadata +39 -5
  37. data/lib/gracefully/feature_builder.rb +0 -24
  38. data/lib/gracefully/health_meter.rb +0 -90
data/spec/spec_helper.rb CHANGED
@@ -21,3 +21,6 @@ RSpec.configure do |config|
21
21
 
22
22
  Kernel.srand config.seed
23
23
  end
24
+
25
+ require 'coveralls'
26
+ Coveralls.wear!
@@ -0,0 +1,3 @@
1
+ require 'timecop'
2
+
3
+ Timecop.safe_mode = true
@@ -0,0 +1,73 @@
1
+ require 'gracefully'
2
+ require 'gracefully/command'
3
+ require 'gracefully/timed_command'
4
+
5
+ RSpec.shared_examples "a timed command" do
6
+ let(:input1) {
7
+ 'input1'
8
+ }
9
+
10
+ let(:callable) {
11
+ Gracefully::Command.new(&usually)
12
+ }
13
+
14
+ context 'when the block given to the command succeeds without any error' do
15
+ let(:usually) {
16
+ -> arg1 { 'usually:arg1:' + arg1 }
17
+ }
18
+
19
+ it { is_expected.to eq('usually:arg1:input1') }
20
+ end
21
+
22
+ context 'when the usually block fails with an error' do
23
+ let(:usually) {
24
+ -> arg1 { raise 'simulated error' }
25
+ }
26
+
27
+ specify { expect { subject }.to raise_error('simulated error') }
28
+ end
29
+
30
+ context 'when the usually block call takes longer than the time out period' do
31
+ let(:usually) {
32
+ -> arg1 { sleep(0.1) }
33
+ }
34
+
35
+ specify { expect { subject }.to raise_error(Timeout::Error) }
36
+ end
37
+ end
38
+
39
+ RSpec.describe Gracefully::TimedCommand do
40
+ context "command creation with invalid number of arguments" do
41
+ subject {
42
+ described_class.new(1, 2, 3)
43
+ }
44
+
45
+ specify { expect { subject }.to raise_error(/Invalid number of arguments: 3/) }
46
+ end
47
+
48
+ describe "feature call result" do
49
+ context 'made of a callable object' do
50
+ subject {
51
+ described_class.new(usually, timeout: 0.01).call input1
52
+ }
53
+
54
+ it_behaves_like 'a timed command'
55
+ end
56
+
57
+ context 'made of a command' do
58
+ subject {
59
+ described_class.new(callable, timeout: 0.01).call input1
60
+ }
61
+
62
+ it_behaves_like 'a timed command'
63
+ end
64
+
65
+ context 'made of a block' do
66
+ subject {
67
+ described_class.new(timeout: 0.01, &usually).call input1
68
+ }
69
+
70
+ it_behaves_like 'a timed command'
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,31 @@
1
+ require 'gracefully/togglable_command'
2
+
3
+ RSpec.describe Gracefully::TogglableCommand do
4
+ subject {
5
+ described_class.new(run_only_if: -> { current_user == target_user }) { |a| a + ' modified' }.call arg
6
+ }
7
+
8
+ let(:arg) {
9
+ 'input'
10
+ }
11
+
12
+ let(:current_user) {
13
+ 'mikoto'
14
+ }
15
+
16
+ context 'when the `run_only_if` block returns true' do
17
+ let(:target_user) {
18
+ 'mikoto'
19
+ }
20
+
21
+ it { is_expected.to eq('input modified') }
22
+ end
23
+
24
+ context 'when the `run_only_if` block returns false' do
25
+ let(:target_user) {
26
+ 'kuroko'
27
+ }
28
+
29
+ specify { expect { subject }.to raise_error(Gracefully::CommandDisabledError) }
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gracefully
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke KUOKA
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-22 00:00:00.000000000 Z
11
+ date: 2014-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -47,20 +47,44 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
49
  - ".rspec"
50
+ - ".travis.yml"
50
51
  - Gemfile
51
52
  - LICENSE.txt
52
53
  - README.md
53
54
  - Rakefile
54
55
  - gracefully.gemspec
55
56
  - lib/gracefully.rb
56
- - lib/gracefully/feature.rb
57
- - lib/gracefully/feature_builder.rb
57
+ - lib/gracefully/all.rb
58
+ - lib/gracefully/circuit_breaker.rb
59
+ - lib/gracefully/command.rb
60
+ - lib/gracefully/command_disabled_error.rb
61
+ - lib/gracefully/consecutive_failures_based_health.rb
62
+ - lib/gracefully/counter.rb
63
+ - lib/gracefully/degradable.rb
64
+ - lib/gracefully/degradable_command.rb
65
+ - lib/gracefully/degradable_command_builder.rb
66
+ - lib/gracefully/error.rb
58
67
  - lib/gracefully/feature_repository.rb
59
- - lib/gracefully/health_meter.rb
68
+ - lib/gracefully/health.rb
69
+ - lib/gracefully/mutex_based_synchronized_counter.rb
70
+ - lib/gracefully/retried_command.rb
71
+ - lib/gracefully/short_circuited_command.rb
72
+ - lib/gracefully/timed_command.rb
73
+ - lib/gracefully/togglable_command.rb
60
74
  - lib/gracefully/try.rb
61
75
  - lib/gracefully/version.rb
76
+ - spec/circuit_breaker_spec.rb
77
+ - spec/command_spec.rb
78
+ - spec/consecutive_failures_based_health_spec.rb
79
+ - spec/degradable_spec.rb
62
80
  - spec/gracefully_spec.rb
81
+ - spec/mutex_based_synchronized_counter_spec.rb
82
+ - spec/retried_command_spec.rb
83
+ - spec/short_circuited_command_spec.rb
63
84
  - spec/spec_helper.rb
85
+ - spec/timecop_helper.rb
86
+ - spec/timed_command_spec.rb
87
+ - spec/togglable_command_spec.rb
64
88
  homepage: https://github.com/crowdworks/gracefully
65
89
  licenses:
66
90
  - MIT
@@ -86,5 +110,15 @@ signing_key:
86
110
  specification_version: 4
87
111
  summary: ensures features gracefully degrade based on error rate or turnaround time.
88
112
  test_files:
113
+ - spec/circuit_breaker_spec.rb
114
+ - spec/command_spec.rb
115
+ - spec/consecutive_failures_based_health_spec.rb
116
+ - spec/degradable_spec.rb
89
117
  - spec/gracefully_spec.rb
118
+ - spec/mutex_based_synchronized_counter_spec.rb
119
+ - spec/retried_command_spec.rb
120
+ - spec/short_circuited_command_spec.rb
90
121
  - spec/spec_helper.rb
122
+ - spec/timecop_helper.rb
123
+ - spec/timed_command_spec.rb
124
+ - spec/togglable_command_spec.rb
@@ -1,24 +0,0 @@
1
- module Gracefully
2
- class FeatureBuilder
3
- def initialize(feature_name)
4
- @feature_name = feature_name
5
- end
6
-
7
- def usually(&block)
8
- @usually = block
9
- self
10
- end
11
-
12
- def fallback_to(&block)
13
- @fallback_to = block
14
-
15
- build
16
- end
17
-
18
- private
19
-
20
- def build
21
- Feature.new(name: @feature_name, usually: @usually, fallback_to: @fallback_to)
22
- end
23
- end
24
- end
@@ -1,90 +0,0 @@
1
- module Gracefully
2
- class HealthMeter
3
- def initialize(args)
4
- @healthy_count = 0
5
- @unhealthy_count = 0
6
- @state = Healthy.new configuration: args[:configuration]
7
- end
8
-
9
- def mark_healthy
10
- @state = @state.mark_healthy
11
- end
12
-
13
- def mark_unhealthy
14
- @state = @state.mark_unhealthy
15
- end
16
-
17
- def healthy?
18
- @state.healthy?
19
- end
20
-
21
- def unhealthy?
22
- @state.unhealthy?
23
- end
24
-
25
- class State
26
- def unhealthy?
27
- !healthy?
28
- end
29
- end
30
-
31
- class Configuration
32
- attr_reader :healthy_threshold, :unhealthy_threshold
33
-
34
- def initialize(args)
35
- @healthy_threshold = args[:healthy_threshold]
36
- @unhealthy_threshold = args[:unhealthy_threshold]
37
- end
38
- end
39
-
40
- class Healthy < State
41
- def initialize(args)
42
- @unhealthy_count = 0
43
- @configuration = args[:configuration]
44
- end
45
-
46
- def mark_healthy
47
- self
48
- end
49
-
50
- def mark_unhealthy
51
- @unhealthy_count += 1
52
-
53
- if @unhealthy_count <= @configuration.unhealthy_threshold
54
- self
55
- else
56
- Unhealthy.new configuration: @configuration
57
- end
58
- end
59
-
60
- def healthy?
61
- true
62
- end
63
- end
64
-
65
- class Unhealthy < State
66
- def initialize(args)
67
- @healthy_count = 0
68
- @configuration = args[:configuration]
69
- end
70
-
71
- def mark_healthy
72
- @healthy_count += 1
73
-
74
- if @healthy_count <= @configuration.healthy_threshold
75
- self
76
- else
77
- Healthy.new configuration: @configuration
78
- end
79
- end
80
-
81
- def mark_unhealthy
82
- self
83
- end
84
-
85
- def healthy?
86
- false
87
- end
88
- end
89
- end
90
- end