stoplight 4.1.0 → 5.0.1

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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +289 -350
  3. data/lib/stoplight/admin/actions/action.rb +24 -0
  4. data/lib/stoplight/admin/actions/lock.rb +23 -0
  5. data/lib/stoplight/admin/actions/lock_all_green.rb +18 -0
  6. data/lib/stoplight/admin/actions/lock_green.rb +23 -0
  7. data/lib/stoplight/admin/actions/lock_red.rb +23 -0
  8. data/lib/stoplight/admin/actions/stats.rb +27 -0
  9. data/lib/stoplight/admin/actions/unlock.rb +23 -0
  10. data/lib/stoplight/admin/dependencies.rb +50 -0
  11. data/lib/stoplight/admin/helpers.rb +27 -0
  12. data/lib/stoplight/admin/lights_repository/light.rb +155 -0
  13. data/lib/stoplight/admin/lights_repository.rb +74 -0
  14. data/lib/stoplight/admin/lights_stats.rb +77 -0
  15. data/lib/stoplight/admin/views/_card.erb +120 -0
  16. data/lib/stoplight/admin/views/index.erb +36 -0
  17. data/lib/stoplight/admin/views/layout.erb +66 -0
  18. data/lib/stoplight/admin.rb +68 -0
  19. data/lib/stoplight/color.rb +3 -3
  20. data/lib/stoplight/config/config_provider.rb +62 -0
  21. data/lib/stoplight/config/library_default_config.rb +29 -0
  22. data/lib/stoplight/config/user_default_config.rb +83 -0
  23. data/lib/stoplight/data_store/base.rb +59 -33
  24. data/lib/stoplight/data_store/fail_safe.rb +105 -0
  25. data/lib/stoplight/data_store/memory.rb +257 -50
  26. data/lib/stoplight/data_store/redis/get_metadata.lua +38 -0
  27. data/lib/stoplight/data_store/redis/lua.rb +23 -0
  28. data/lib/stoplight/data_store/redis/record_failure.lua +36 -0
  29. data/lib/stoplight/data_store/redis/record_success.lua +35 -0
  30. data/lib/stoplight/data_store/redis/transition_to_green.lua +10 -0
  31. data/lib/stoplight/data_store/redis/transition_to_red.lua +10 -0
  32. data/lib/stoplight/data_store/redis/transition_to_yellow.lua +9 -0
  33. data/lib/stoplight/data_store/redis.rb +345 -106
  34. data/lib/stoplight/default.rb +11 -9
  35. data/lib/stoplight/error.rb +1 -13
  36. data/lib/stoplight/failure.rb +14 -13
  37. data/lib/stoplight/light/config.rb +118 -0
  38. data/lib/stoplight/light/configuration_builder_interface.rb +128 -0
  39. data/lib/stoplight/light/green_run_strategy.rb +53 -0
  40. data/lib/stoplight/light/red_run_strategy.rb +26 -0
  41. data/lib/stoplight/light/run_strategy.rb +30 -0
  42. data/lib/stoplight/light/yellow_run_strategy.rb +78 -0
  43. data/lib/stoplight/light.rb +164 -84
  44. data/lib/stoplight/metadata.rb +71 -0
  45. data/lib/stoplight/notifier/base.rb +14 -7
  46. data/lib/stoplight/notifier/fail_safe.rb +67 -0
  47. data/lib/stoplight/notifier/generic.rb +54 -5
  48. data/lib/stoplight/rspec/generic_notifier.rb +11 -12
  49. data/lib/stoplight/rspec.rb +1 -1
  50. data/lib/stoplight/state.rb +3 -3
  51. data/lib/stoplight/traffic_control/base.rb +35 -0
  52. data/lib/stoplight/traffic_control/consecutive_failures.rb +43 -0
  53. data/lib/stoplight/traffic_recovery/base.rb +51 -0
  54. data/lib/stoplight/traffic_recovery/single_success.rb +35 -0
  55. data/lib/stoplight/version.rb +1 -1
  56. data/lib/stoplight.rb +111 -51
  57. metadata +49 -98
  58. data/lib/stoplight/builder.rb +0 -70
  59. data/lib/stoplight/circuit_breaker.rb +0 -102
  60. data/lib/stoplight/configurable.rb +0 -95
  61. data/lib/stoplight/configuration.rb +0 -126
  62. data/lib/stoplight/light/deprecated.rb +0 -44
  63. data/lib/stoplight/light/lockable.rb +0 -45
  64. data/lib/stoplight/light/runnable.rb +0 -127
  65. data/lib/stoplight/notifier.rb +0 -6
  66. data/spec/spec_helper.rb +0 -22
  67. data/spec/stoplight/builder_spec.rb +0 -165
  68. data/spec/stoplight/circuit_breaker_spec.rb +0 -43
  69. data/spec/stoplight/color_spec.rb +0 -39
  70. data/spec/stoplight/configurable_spec.rb +0 -25
  71. data/spec/stoplight/data_store/base_spec.rb +0 -71
  72. data/spec/stoplight/data_store/memory_spec.rb +0 -22
  73. data/spec/stoplight/data_store/redis_spec.rb +0 -45
  74. data/spec/stoplight/data_store_spec.rb +0 -9
  75. data/spec/stoplight/default_spec.rb +0 -80
  76. data/spec/stoplight/error_spec.rb +0 -39
  77. data/spec/stoplight/failure_spec.rb +0 -108
  78. data/spec/stoplight/light/lockable_spec.rb +0 -93
  79. data/spec/stoplight/light/runnable_spec.rb +0 -38
  80. data/spec/stoplight/light_spec.rb +0 -156
  81. data/spec/stoplight/notifier/base_spec.rb +0 -18
  82. data/spec/stoplight/notifier/generic_spec.rb +0 -50
  83. data/spec/stoplight/notifier/io_spec.rb +0 -41
  84. data/spec/stoplight/notifier/logger_spec.rb +0 -75
  85. data/spec/stoplight/notifier_spec.rb +0 -9
  86. data/spec/stoplight/state_spec.rb +0 -39
  87. data/spec/stoplight/version_spec.rb +0 -9
  88. data/spec/stoplight_spec.rb +0 -32
  89. data/spec/support/configurable.rb +0 -69
  90. data/spec/support/data_store/base/clear_failures.rb +0 -18
  91. data/spec/support/data_store/base/clear_state.rb +0 -20
  92. data/spec/support/data_store/base/get_all.rb +0 -44
  93. data/spec/support/data_store/base/get_failures.rb +0 -30
  94. data/spec/support/data_store/base/get_state.rb +0 -7
  95. data/spec/support/data_store/base/names.rb +0 -29
  96. data/spec/support/data_store/base/record_failures.rb +0 -70
  97. data/spec/support/data_store/base/set_state.rb +0 -15
  98. data/spec/support/data_store/base/with_notification_lock.rb +0 -27
  99. data/spec/support/data_store/base.rb +0 -21
  100. data/spec/support/database_cleaner.rb +0 -26
  101. data/spec/support/exception_helpers.rb +0 -9
  102. data/spec/support/light/runnable/color.rb +0 -79
  103. data/spec/support/light/runnable/run.rb +0 -247
  104. data/spec/support/light/runnable/state.rb +0 -31
  105. data/spec/support/light/runnable.rb +0 -5
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module TrafficRecovery
5
+ # Strategies for determining how to recover traffic flow through the Stoplight.
6
+ # These strategies evaluate recovery metrics to decide which color the Stoplight should
7
+ # transition to during the recovery process.
8
+ #
9
+ # @example Creating a custom traffic recovery strategy
10
+ # class GradualRecovery < Stoplight::TrafficRecovery::Base
11
+ # def initialize(min_success_rate: 0.8, min_samples: 100)
12
+ # @min_success_rate = min_success_rate
13
+ # @min_samples = min_samples
14
+ # end
15
+ #
16
+ # def determine_color(config, metadata)
17
+ # total_probes = metadata.recovery_probe_successes + metadata.recovery_probe_errors
18
+ #
19
+ # if total_probes < @min_samples
20
+ # return Color::YELLOW # Keep recovering, not enough samples
21
+ # end
22
+ #
23
+ # success_rate = metadata.recovery_probe_successes.fdiv(total_probes)
24
+ # if success_rate >= @min_success_rate
25
+ # Color::GREEN # Recovery successful
26
+ # elsif success_rate <= 0.2
27
+ # Color::RED # Recovery failed, too many errors
28
+ # else
29
+ # Color::YELLOW # Continue recovery
30
+ # end
31
+ # end
32
+ # end
33
+ #
34
+ # @abstract
35
+ # @api private
36
+ class Base
37
+ # Determines the appropriate recovery state based on the Stoplight's
38
+ # current metrics and recovery progress.
39
+ #
40
+ # @param config [Stoplight::Light::Config]
41
+ # @param metadata [Stoplight::Metadata]
42
+ # @return [String] One of the Stoplight::Color constants:
43
+ # - Stoplight::Color::RED: Recovery failed, block all traffic
44
+ # - Stoplight::Color::YELLOW: Continue recovery process
45
+ # - Stoplight::Color::GREEN: Recovery successful, return to normal traffic flow
46
+ def determine_color(config, metadata)
47
+ raise NotImplementedError
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module TrafficRecovery
5
+ # A basic strategy that recovers traffic flow after a successful recovery probe.
6
+ #
7
+ # This strategy allows traffic to resume when a single successful probe
8
+ # occurs after the Stoplight has been in red state. It's a simple "one success and we're back"
9
+ # approach.
10
+ #
11
+ # @example Basic usage
12
+ # config = Stoplight::Light::Config.new(cool_off_time: 60)
13
+ # strategy = Stoplight::TrafficRecovery::SingleSuccess.new
14
+ #
15
+ # After the Stoplight turns red:
16
+ # - The Stoplight will wait for the cool-off period (60 seconds)
17
+ # - Then enter the recovery phase (YELLOW color)
18
+ # - The first successful probe will resume normal traffic flow (green color)
19
+ # @api private
20
+ class SingleSuccess < Base
21
+ # @param config [Stoplight::Light::Config]
22
+ # @param metadata [Stoplight::Metadata]
23
+ # @return [String]
24
+ def determine_color(config, metadata)
25
+ recovery_started_at = metadata.recovery_started_at || metadata.recovery_scheduled_after
26
+ last_success_at = metadata.last_success_at
27
+ if last_success_at && recovery_started_at <= last_success_at
28
+ Color::GREEN
29
+ else
30
+ Color::RED
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stoplight
4
- VERSION = Gem::Version.new('4.1.0')
4
+ VERSION = Gem::Version.new("5.0.1")
5
5
  end
data/lib/stoplight.rb CHANGED
@@ -1,63 +1,123 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Stoplight # rubocop:disable Style/Documentation
4
- class << self
5
- # @!attribute default_data_store
6
- # @return [DataStore::Base]
7
- attr_accessor :default_data_store
8
-
9
- # @!attribute default_notifiers
10
- # @return [Array<Notifier::Base>]
11
- attr_accessor :default_notifiers
12
-
13
- # @!attribute default_error_notifier
14
- # @return [Proc]
15
- attr_accessor :default_error_notifier
16
- end
17
- end
3
+ require "zeitwerk"
18
4
 
19
- require 'stoplight/version'
5
+ loader = Zeitwerk::Loader.for_gem
6
+ loader.inflector.inflect("io" => "IO")
7
+ loader.do_not_eager_load(
8
+ "#{__dir__}/stoplight/data_store",
9
+ "#{__dir__}/stoplight/admin",
10
+ "#{__dir__}/stoplight/admin.rb"
11
+ )
12
+ loader.ignore("#{__dir__}/stoplight/rspec.rb", "#{__dir__}/stoplight/rspec")
13
+ loader.setup
20
14
 
21
- require 'stoplight/color'
22
- require 'stoplight/error'
23
- require 'stoplight/failure'
24
- require 'stoplight/state'
15
+ module Stoplight # rubocop:disable Style/Documentation
16
+ CONFIG_MUTEX = Mutex.new
17
+ private_constant :CONFIG_MUTEX
25
18
 
26
- require 'stoplight/data_store'
27
- require 'stoplight/data_store/base'
28
- require 'stoplight/data_store/memory'
29
- require 'stoplight/data_store/redis'
19
+ class << self
20
+ ALREADY_CONFIGURED_WARNING = "Stoplight must be configured only once"
21
+ private_constant :ALREADY_CONFIGURED_WARNING
30
22
 
31
- require 'stoplight/notifier'
32
- require 'stoplight/notifier/base'
33
- require 'stoplight/notifier/generic'
23
+ # Configures the Stoplight library.
24
+ #
25
+ # This method allows you to set up the library's configuration using a block.
26
+ # It raises an error if called more than once.
27
+ #
28
+ # @yield [config] Provides a configuration object to the block.
29
+ # @yieldparam config [Stoplight::Config::ProgrammaticConfig] The configuration object.
30
+ # @raise [Stoplight::Error::ConfigurationError] If the library is already configured.
31
+ # @return [void]
32
+ #
33
+ # @example
34
+ # Stoplight.configure do |config|
35
+ # config.window_size = 14
36
+ # config.data_store = Stoplight::DataStore::Redis.new(redis_client)
37
+ # config.notifiers = [Stoplight::Notifier::IO.new($stdout)]
38
+ # config.cool_off_time = 120
39
+ # config.threshold = 5
40
+ # config.tracked_errors = [StandardError]
41
+ # config.skipped_errors = [RuntimeError]
42
+ # end
43
+ #
44
+ # @note It is not recommended to call this method multiple times because after reconfiguring Stoplight
45
+ # it will not be possible to change the configuration of existing circuit breakers. If you do so, the method
46
+ # produces a warning:
47
+ #
48
+ # "Stoplight reconfigured. Existing circuit breakers will not see the new configuration. New
49
+ # configuration: #<Stoplight::Config::ConfigProvider cool_off_time=32, threshold=3, window_size=94, tracked_errors=StandardError, skipped_errors=NoMemoryError,ScriptError,SecurityError,SignalException,SystemExit,SystemStackError, data_store=Stoplight::DataStore::Memory>\n"
50
+ #
51
+ # If you really know what you are doing, you can pass the +trust_me_im_an_engineer+ parameter as +true+ to
52
+ # suppress this warning, which could be useful in test environments.
53
+ #
54
+ def configure(trust_me_im_an_engineer: false)
55
+ user_defaults = Config::UserDefaultConfig.new
56
+ yield(user_defaults) if block_given?
34
57
 
35
- require 'stoplight/notifier/io'
36
- require 'stoplight/notifier/logger'
58
+ reconfigured = !@config_provider.nil?
37
59
 
38
- require 'stoplight/default'
60
+ @config_provider = Config::ConfigProvider.new(
61
+ user_default_config: user_defaults.freeze,
62
+ library_default_config: Config::LibraryDefaultConfig.new
63
+ ).tap do
64
+ if reconfigured && !trust_me_im_an_engineer
65
+ warn(
66
+ "Stoplight reconfigured. Existing circuit breakers will not see new configuration. " \
67
+ "New configuration: #{@config_provider.inspect}"
68
+ )
69
+ end
70
+ end
71
+ end
39
72
 
40
- module Stoplight # rubocop:disable Style/Documentation
41
- @default_data_store = Default::DATA_STORE
42
- @default_notifiers = Default::NOTIFIERS
43
- @default_error_notifier = Default::ERROR_NOTIFIER
73
+ # Retrieves the current configuration provider.
74
+ #
75
+ # @return [Stoplight::Config::ConfigProvider]
76
+ # @api private
77
+ def config_provider
78
+ CONFIG_MUTEX.synchronize do
79
+ @config_provider ||= configure
80
+ end
81
+ end
82
+ end
44
83
  end
45
84
 
46
- require 'stoplight/configurable'
47
- require 'stoplight/circuit_breaker'
48
- require 'stoplight/builder'
49
- require 'stoplight/configuration'
50
- require 'stoplight/light/lockable'
51
- require 'stoplight/light/runnable'
52
- require 'stoplight/light'
53
-
54
- # @return [Stoplight::CircuitBreaker]
55
- def Stoplight(name, &code) # rubocop:disable Naming/MethodName
56
- if block_given?
57
- warn '[DEPRECATED] Calling `Stoplight("name") { ... }` with a code block is deprecated. ' \
58
- 'Please pass code block to the run method `Stoplight("name").run { ... }` method instead.'
59
- Stoplight::Builder.with(name: name).build(&code)
60
- else
61
- Stoplight::Builder.with(name: name)
62
- end
85
+ # Creates a new Stoplight circuit breaker with the given name and settings.
86
+ #
87
+ # @param name [String] The name of the circuit breaker.
88
+ # @param settings [Hash] Optional settings to configure the circuit breaker.
89
+ # @option settings [Numeric] :cool_off_time The time to wait before resetting the circuit breaker.
90
+ # @option settings [Stoplight::DataStore::Base] :data_store The data store to use for storing state.
91
+ # @option settings [Proc] :error_notifier A proc to handle error notifications.
92
+ # @option settings [Array<Stoplight::Notifier::Base>] :notifiers A list of notifiers to use.
93
+ # @option settings [Numeric] :threshold The failure threshold to trip the circuit breaker.
94
+ # @option settings [Numeric] :window_size The size of the rolling window for failure tracking.
95
+ # @option settings [Array<StandardError>] :tracked_errors A list of errors to track.
96
+ # @option settings [Array<Exception>] :skipped_errors A list of errors to skip.
97
+ #
98
+ # @return [Stoplight::Light] A new circuit breaker instance.
99
+ # @raise [ArgumentError] If an unknown option is provided in the settings.
100
+ #
101
+ # @example configure circuit breaker behavior
102
+ # light = Stoplight("Payment API", window_size: 300, threshold: 5, cool_off_time: 60)
103
+ #
104
+ # @example configure data store
105
+ # light = Stoplight("Payment API", data_store: Stoplight::DataStore::Redis.new(redis_client))
106
+ #
107
+ # In the example below, the +TimeoutError+ and +NetworkError+ exceptions
108
+ # will be counted towards the threshold for moving the circuit breaker into the red state.
109
+ # If not configured, the default tracked error is +StandardError+.
110
+ #
111
+ # @example configure tracked errors
112
+ # light = Stoplight("Payment API", tracked_errors: [TimeoutError, NetworkError])
113
+ #
114
+ # In the example below , the +ActiveRecord::RecordNotFound+ doesn't
115
+ # move the circuit breaker into the red state.
116
+ #
117
+ # @example configure skipped errors
118
+ # light = Stoplight("Payment API", skipped_errors: [ActiveRecord::RecordNotFound])
119
+ #
120
+ def Stoplight(name, **settings) # rubocop:disable Naming/MethodName
121
+ config = Stoplight.config_provider.provide(name, **settings)
122
+ Stoplight::Light.new(config)
63
123
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stoplight
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Desautels
@@ -10,22 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-08-20 00:00:00.000000000 Z
13
+ date: 2025-06-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: redlock
16
+ name: zeitwerk
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - "~>"
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: '1.0'
28
+ version: '0'
29
29
  description: An implementation of the circuit breaker pattern.
30
30
  email:
31
31
  - camdez@gmail.com
@@ -39,71 +39,62 @@ files:
39
39
  - LICENSE.md
40
40
  - README.md
41
41
  - lib/stoplight.rb
42
- - lib/stoplight/builder.rb
43
- - lib/stoplight/circuit_breaker.rb
42
+ - lib/stoplight/admin.rb
43
+ - lib/stoplight/admin/actions/action.rb
44
+ - lib/stoplight/admin/actions/lock.rb
45
+ - lib/stoplight/admin/actions/lock_all_green.rb
46
+ - lib/stoplight/admin/actions/lock_green.rb
47
+ - lib/stoplight/admin/actions/lock_red.rb
48
+ - lib/stoplight/admin/actions/stats.rb
49
+ - lib/stoplight/admin/actions/unlock.rb
50
+ - lib/stoplight/admin/dependencies.rb
51
+ - lib/stoplight/admin/helpers.rb
52
+ - lib/stoplight/admin/lights_repository.rb
53
+ - lib/stoplight/admin/lights_repository/light.rb
54
+ - lib/stoplight/admin/lights_stats.rb
55
+ - lib/stoplight/admin/views/_card.erb
56
+ - lib/stoplight/admin/views/index.erb
57
+ - lib/stoplight/admin/views/layout.erb
44
58
  - lib/stoplight/color.rb
45
- - lib/stoplight/configurable.rb
46
- - lib/stoplight/configuration.rb
59
+ - lib/stoplight/config/config_provider.rb
60
+ - lib/stoplight/config/library_default_config.rb
61
+ - lib/stoplight/config/user_default_config.rb
47
62
  - lib/stoplight/data_store.rb
48
63
  - lib/stoplight/data_store/base.rb
64
+ - lib/stoplight/data_store/fail_safe.rb
49
65
  - lib/stoplight/data_store/memory.rb
50
66
  - lib/stoplight/data_store/redis.rb
67
+ - lib/stoplight/data_store/redis/get_metadata.lua
68
+ - lib/stoplight/data_store/redis/lua.rb
69
+ - lib/stoplight/data_store/redis/record_failure.lua
70
+ - lib/stoplight/data_store/redis/record_success.lua
71
+ - lib/stoplight/data_store/redis/transition_to_green.lua
72
+ - lib/stoplight/data_store/redis/transition_to_red.lua
73
+ - lib/stoplight/data_store/redis/transition_to_yellow.lua
51
74
  - lib/stoplight/default.rb
52
75
  - lib/stoplight/error.rb
53
76
  - lib/stoplight/failure.rb
54
77
  - lib/stoplight/light.rb
55
- - lib/stoplight/light/deprecated.rb
56
- - lib/stoplight/light/lockable.rb
57
- - lib/stoplight/light/runnable.rb
58
- - lib/stoplight/notifier.rb
78
+ - lib/stoplight/light/config.rb
79
+ - lib/stoplight/light/configuration_builder_interface.rb
80
+ - lib/stoplight/light/green_run_strategy.rb
81
+ - lib/stoplight/light/red_run_strategy.rb
82
+ - lib/stoplight/light/run_strategy.rb
83
+ - lib/stoplight/light/yellow_run_strategy.rb
84
+ - lib/stoplight/metadata.rb
59
85
  - lib/stoplight/notifier/base.rb
86
+ - lib/stoplight/notifier/fail_safe.rb
60
87
  - lib/stoplight/notifier/generic.rb
61
88
  - lib/stoplight/notifier/io.rb
62
89
  - lib/stoplight/notifier/logger.rb
63
90
  - lib/stoplight/rspec.rb
64
91
  - lib/stoplight/rspec/generic_notifier.rb
65
92
  - lib/stoplight/state.rb
93
+ - lib/stoplight/traffic_control/base.rb
94
+ - lib/stoplight/traffic_control/consecutive_failures.rb
95
+ - lib/stoplight/traffic_recovery/base.rb
96
+ - lib/stoplight/traffic_recovery/single_success.rb
66
97
  - lib/stoplight/version.rb
67
- - spec/spec_helper.rb
68
- - spec/stoplight/builder_spec.rb
69
- - spec/stoplight/circuit_breaker_spec.rb
70
- - spec/stoplight/color_spec.rb
71
- - spec/stoplight/configurable_spec.rb
72
- - spec/stoplight/data_store/base_spec.rb
73
- - spec/stoplight/data_store/memory_spec.rb
74
- - spec/stoplight/data_store/redis_spec.rb
75
- - spec/stoplight/data_store_spec.rb
76
- - spec/stoplight/default_spec.rb
77
- - spec/stoplight/error_spec.rb
78
- - spec/stoplight/failure_spec.rb
79
- - spec/stoplight/light/lockable_spec.rb
80
- - spec/stoplight/light/runnable_spec.rb
81
- - spec/stoplight/light_spec.rb
82
- - spec/stoplight/notifier/base_spec.rb
83
- - spec/stoplight/notifier/generic_spec.rb
84
- - spec/stoplight/notifier/io_spec.rb
85
- - spec/stoplight/notifier/logger_spec.rb
86
- - spec/stoplight/notifier_spec.rb
87
- - spec/stoplight/state_spec.rb
88
- - spec/stoplight/version_spec.rb
89
- - spec/stoplight_spec.rb
90
- - spec/support/configurable.rb
91
- - spec/support/data_store/base.rb
92
- - spec/support/data_store/base/clear_failures.rb
93
- - spec/support/data_store/base/clear_state.rb
94
- - spec/support/data_store/base/get_all.rb
95
- - spec/support/data_store/base/get_failures.rb
96
- - spec/support/data_store/base/get_state.rb
97
- - spec/support/data_store/base/names.rb
98
- - spec/support/data_store/base/record_failures.rb
99
- - spec/support/data_store/base/set_state.rb
100
- - spec/support/data_store/base/with_notification_lock.rb
101
- - spec/support/database_cleaner.rb
102
- - spec/support/exception_helpers.rb
103
- - spec/support/light/runnable.rb
104
- - spec/support/light/runnable/color.rb
105
- - spec/support/light/runnable/run.rb
106
- - spec/support/light/runnable/state.rb
107
98
  homepage: https://github.com/bolshakov/stoplight
108
99
  licenses:
109
100
  - MIT
@@ -116,55 +107,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
107
  requirements:
117
108
  - - ">="
118
109
  - !ruby/object:Gem::Version
119
- version: 3.0.6
110
+ version: '3.2'
120
111
  required_rubygems_version: !ruby/object:Gem::Requirement
121
112
  requirements:
122
113
  - - ">="
123
114
  - !ruby/object:Gem::Version
124
115
  version: '0'
125
116
  requirements: []
126
- rubygems_version: 3.3.7
117
+ rubygems_version: 3.4.19
127
118
  signing_key:
128
119
  specification_version: 4
129
120
  summary: Traffic control for code.
130
- test_files:
131
- - spec/spec_helper.rb
132
- - spec/stoplight/builder_spec.rb
133
- - spec/stoplight/circuit_breaker_spec.rb
134
- - spec/stoplight/color_spec.rb
135
- - spec/stoplight/configurable_spec.rb
136
- - spec/stoplight/data_store/base_spec.rb
137
- - spec/stoplight/data_store/memory_spec.rb
138
- - spec/stoplight/data_store/redis_spec.rb
139
- - spec/stoplight/data_store_spec.rb
140
- - spec/stoplight/default_spec.rb
141
- - spec/stoplight/error_spec.rb
142
- - spec/stoplight/failure_spec.rb
143
- - spec/stoplight/light/lockable_spec.rb
144
- - spec/stoplight/light/runnable_spec.rb
145
- - spec/stoplight/light_spec.rb
146
- - spec/stoplight/notifier/base_spec.rb
147
- - spec/stoplight/notifier/generic_spec.rb
148
- - spec/stoplight/notifier/io_spec.rb
149
- - spec/stoplight/notifier/logger_spec.rb
150
- - spec/stoplight/notifier_spec.rb
151
- - spec/stoplight/state_spec.rb
152
- - spec/stoplight/version_spec.rb
153
- - spec/stoplight_spec.rb
154
- - spec/support/configurable.rb
155
- - spec/support/data_store/base/clear_failures.rb
156
- - spec/support/data_store/base/clear_state.rb
157
- - spec/support/data_store/base/get_all.rb
158
- - spec/support/data_store/base/get_failures.rb
159
- - spec/support/data_store/base/get_state.rb
160
- - spec/support/data_store/base/names.rb
161
- - spec/support/data_store/base/record_failures.rb
162
- - spec/support/data_store/base/set_state.rb
163
- - spec/support/data_store/base/with_notification_lock.rb
164
- - spec/support/data_store/base.rb
165
- - spec/support/database_cleaner.rb
166
- - spec/support/exception_helpers.rb
167
- - spec/support/light/runnable/color.rb
168
- - spec/support/light/runnable/run.rb
169
- - spec/support/light/runnable/state.rb
170
- - spec/support/light/runnable.rb
121
+ test_files: []
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
-
5
- module Stoplight
6
- # An interface to build Stoplight configuration. The builder is
7
- # immutable, so it's safe to pass an instance of this builder
8
- # across the code.
9
- #
10
- # @example
11
- # circuit_breaker = Stoplight('http_api')
12
- # .with_data_store(data_store)
13
- # .with_cool_off_time(60)
14
- # .with_threshold(5)
15
- # .with_window_size(3600)
16
- # .with_notifiers(notifiers)
17
- # .with_error_notifier(error_notifier) #=> <#Stoplight::Builder ..>
18
- #
19
- # It's safe to pass this +circuit_breaker+ around your code like this:
20
- #
21
- # def call(circuit_breaker)
22
- # circuit_breaker.run { call_api }
23
- # end
24
- #
25
- # @api private use +Stoplight()+ method instead
26
- class Builder
27
- include CircuitBreaker
28
- extend Forwardable
29
-
30
- def_delegator :build, :with_error_handler
31
- def_delegator :build, :with_fallback
32
- def_delegator :build, :color
33
- def_delegator :build, :name
34
- def_delegator :build, :state
35
- def_delegator :build, :run
36
- def_delegator :build, :lock
37
- def_delegator :build, :unlock
38
-
39
- class << self
40
- # @param settings [Hash]
41
- # @see +Stoplight::Configuration#initialize+
42
- # @return [Stoplight::Builder]
43
- def with(**settings)
44
- new Configuration.new(**settings)
45
- end
46
- end
47
-
48
- # @param [Stoplight::Configuration]
49
- def initialize(configuration)
50
- @configuration = configuration
51
- end
52
-
53
- # @return [Stoplight::Light]
54
- def build(&code)
55
- Light.new(configuration.name, configuration, &code)
56
- end
57
-
58
- # @param other [any]
59
- # @return [Boolean]
60
- def ==(other)
61
- other.is_a?(self.class) && configuration == other.configuration
62
- end
63
-
64
- private
65
-
66
- def reconfigure(configuration)
67
- self.class.new(configuration)
68
- end
69
- end
70
- end
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- # @abstract
5
- module CircuitBreaker
6
- include Configurable
7
-
8
- # Configures a custom proc that allows you to not to handle an error
9
- # with Stoplight.
10
- #
11
- # @example
12
- # light = Stoplight('example')
13
- # .with_error_handler do |error, handler|
14
- # raise error if error.is_a?(ActiveRecord::RecordNotFound)
15
- # handle.call(error)
16
- # end
17
- # light.run { User.find(123) }
18
- #
19
- # In the example above, the +ActiveRecord::RecordNotFound+ doesn't
20
- # move the circuit breaker into the red state.
21
- #
22
- # @yieldparam error [Exception]
23
- # @yieldparam handle [Proc]
24
- # @return [Stoplight::CircuitBreaker]
25
- def with_error_handler(&error_handler)
26
- raise NotImplementedError
27
- end
28
-
29
- # Configures light with the given fallback block
30
- #
31
- # @example
32
- # light = Stoplight('example')
33
- # light.with_fallback { |error| e.is_a?()ZeroDivisionError) ? 0 : nil }
34
- # light.run { 1 / 0} #=> 0
35
- #
36
- # @yieldparam error [Exception, nil]
37
- # @return [Stoplight::CircuitBreaker]
38
- def with_fallback(&fallback)
39
- raise NotImplementedError
40
- end
41
-
42
- # @return [String] one of +locked_green+, +locked_red+, and +unlocked+
43
- def state
44
- raise NotImplementedError
45
- end
46
-
47
- # @return [String] the light's name
48
- def name
49
- raise NotImplementedError
50
- end
51
-
52
- # Returns current color:
53
- # * +Stoplight::Color::GREEN+ -- circuit breaker is closed
54
- # * +Stoplight::Color::RED+ -- circuit breaker is open
55
- # * +Stoplight::Color::YELLOW+ -- circuit breaker is half-open
56
- #
57
- # @example
58
- # light = Stoplight('example')
59
- # light.color #=> Color::GREEN
60
- #
61
- # @return [String] returns current light color
62
- def color
63
- raise NotImplementedError
64
- end
65
-
66
- # Runs the given block of code with this circuit breaker
67
- #
68
- # @example
69
- # light = Stoplight('example')
70
- # light.run { 2/0 }
71
- #
72
- # @raise [Stoplight::Error::RedLight]
73
- # @return [any]
74
- def run(&code)
75
- raise NotImplementedError
76
- end
77
-
78
- # Locks light in either +State::LOCKED_RED+ or +State::LOCKED_GREEN+
79
- #
80
- # @example
81
- # light = Stoplight('example-locked')
82
- # light.lock(Stoplight::Color::RED)
83
- #
84
- # @param color [String] should be either +Color::RED+ or +Color::GREEN+
85
- # @return [Stoplight::CircuitBreaker] returns locked circuit breaker
86
- def lock(color)
87
- raise NotImplementedError
88
- end
89
-
90
- # Unlocks light and sets it's state to State::UNLOCKED
91
- #
92
- # @example
93
- # light = Stoplight('example-locked')
94
- # light.lock(Stoplight::Color::RED)
95
- # light.unlock
96
- #
97
- # @return [Stoplight::CircuitBreaker] returns unlocked circuit breaker
98
- def unlock
99
- raise NotImplementedError
100
- end
101
- end
102
- end