gracefully 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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