Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
9b0052d
feat: add event system foundation for provider lifecycle
sameerank Dec 9, 2025
147a9c3
feat: add provider lifecycle interfaces and state management
sameerank Dec 10, 2025
2833a4a
fix: correct test expectation for init_with_timeout method signature
sameerank Dec 10, 2025
7f85a25
feat: implement async provider initialization with event-driven lifec…
sameerank Dec 10, 2025
45684bc
test: add OpenFeature specification requirement tests for provider li…
sameerank Dec 10, 2025
8c91379
style: remove excessive comments to match project conventions
sameerank Dec 11, 2025
821e71c
fix: address code review feedback
sameerank Dec 11, 2025
0ab5d30
refactor: remove ContextAwareStateHandler as it provides no additiona…
sameerank Dec 11, 2025
e4887f1
fix: address second round of Gemini code review feedback
sameerank Dec 11, 2025
6eefe78
fix: address third round of Gemini code review feedback
sameerank Dec 11, 2025
ce8d3f1
refactor: use dedicated ProviderEventDispatcher class
sameerank Dec 11, 2025
700ad79
refactor: remove EventAwareNoOpProvider and reorganize compatibility …
sameerank Dec 11, 2025
2fa7865
fix: address race conditions in provider lifecycle management
sameerank Dec 11, 2025
fc88010
refactor: improve API structure and fix nil provider handling
sameerank Dec 11, 2025
d22ca3c
refactor: improve error messages and init parameter handling
sameerank Dec 11, 2025
2611c40
refactor: improve parameter inspection and simplify event details
sameerank Dec 11, 2025
e6e176a
refactor: replace PROVIDER_FATAL string literals with constants
sameerank Dec 11, 2025
fb37e18
improve: enhance error handling by propagating original exception object
sameerank Dec 11, 2025
2d531a1
fix: prevent memory leaks in provider state registry
sameerank Dec 11, 2025
fc2efaf
fix: prevent race condition in async provider initialization
sameerank Dec 11, 2025
4cdc814
improve: enhance diagnostics and robustness of provider eventing system
sameerank Dec 11, 2025
f858bfb
fix: correct semantically incorrect original_error assignment
sameerank Dec 11, 2025
ca59659
fix: remove problematic provider reversion logic and dead code
sameerank Dec 11, 2025
e0e354c
fix: address code review feedback on exception handling and test stab…
sameerank Dec 11, 2025
8a13c74
fix: add thread-safety to set_provider_and_wait method
sameerank Dec 12, 2025
4f7c547
fix: resolve race condition in set_provider_and_wait
sameerank Dec 12, 2025
2be203e
perf: improve concurrency in set_provider_and_wait
sameerank Dec 12, 2025
ebe7621
refactor: improve error message clarity and code organization
sameerank Dec 12, 2025
bc37195
refactor: replace hash mapping with idiomatic case statement
sameerank Dec 12, 2025
3f0e659
test: achieve 100% test coverage with comprehensive edge case testing
sameerank Dec 12, 2025
feb988b
refactor: remove redundant comments and add OpenFeature spec reference
sameerank Dec 12, 2025
40ec89e
Merge remote-tracking branch 'origin/main' into feat/add-provider-eve…
sameerank Dec 12, 2025
b301cf9
style: apply Standard Ruby lint fixes for CI compliance
sameerank Dec 12, 2025
124b689
docs: update README with eventing documentation
sameerank Dec 12, 2025
19dd12f
refactor: improve API encapsulation by making testing utilities private
sameerank Dec 12, 2025
ba4a136
refactor: remove unnecessary StateHandler module to simplify PR
sameerank Dec 13, 2025
e5ba150
refactor: simplify provider initialization with explicit blocking con…
sameerank Dec 16, 2025
bfb771d
feat: implement client-level event handlers with domain scoping
sameerank Jan 5, 2026
31ddc6f
refactor: consolidate duplicate immediate handler methods
sameerank Jan 5, 2026
aee04ac
refactor: improve API design by marking internal methods private
sameerank Jan 6, 2026
4bc1557
feat: replace sleep-based synchronization with async coordination pat…
sameerank Jan 6, 2026
688c908
refactor: make attach/detach methods private in EventHandler
sameerank Jan 6, 2026
fc4ab55
refactor: improve mutex handling in set_provider_internal
sameerank Jan 6, 2026
d17fee9
fix: always emit PROVIDER_READY events regardless of EventHandler cap…
sameerank Jan 6, 2026
7fc6450
fix: PROVIDER_CONFIGURATION_CHANGED events must not update state per …
sameerank Jan 7, 2026
f78e5de
fix: move provider shutdown outside mutex and prevent race condition
sameerank Jan 8, 2026
e6dded1
refactor: inline EventToStateMapper into ProviderStateRegistry
sameerank Jan 8, 2026
e222491
refactor: update naming to better reflect responsibilities
sameerank Jan 9, 2026
9107058
fix: use GENERAL error code for provider initialization failures
sameerank Jan 9, 2026
0e9ad01
refactor: move Requirement 5.1.3 test to events specification
sameerank Jan 9, 2026
8fcf69a
docs: update README to reflect that original_error is always present
sameerank Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,14 @@ GEM
rubocop-performance (~> 1.20.2)
stringio (3.1.0)
strscan (3.1.0)
timecop (0.9.10)
unicode-display_width (2.5.0)

PLATFORMS
arm64-darwin-21
arm64-darwin-22
arm64-darwin-23
arm64-darwin-24
x64-mingw-ucrt
x64-mingw32
x86_64-darwin-19
Expand All @@ -112,6 +114,7 @@ DEPENDENCIES
simplecov-cobertura (~> 2.1.0)
standard
standard-performance
timecop (~> 0.9.10)

BUNDLED WITH
2.3.25
50 changes: 42 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ object = client.fetch_object_value(flag_key: 'object_value', default_value: { na
| ⚠️ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
| ❌ | [Logging](#logging) | Integrate with popular logging packages. |
| ✅ | [Domains](#domains) | Logically bind clients with providers. |
| | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
| | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
| ⚠️ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
| ❌ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) |
| ⚠️ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
Expand Down Expand Up @@ -142,6 +142,7 @@ begin
rescue OpenFeature::SDK::ProviderInitializationError => e
puts "Provider failed to initialize: #{e.message}"
puts "Error code: #{e.error_code}"
# original_error contains the underlying exception that caused the initialization failure
puts "Original error: #{e.original_error}"
end

Expand All @@ -163,8 +164,9 @@ end

The `set_provider_and_wait` method:
- Waits for the provider's `init` method to complete successfully
- Raises `ProviderInitializationError` with `PROVIDER_FATAL` error code if initialization fails or times out
- Provides access to the original error, provider instance, and error code for debugging
- Raises `ProviderInitializationError` if initialization fails or times out
- Provides access to the provider instance, error code, and original exception for debugging
- The `original_error` field contains the underlying exception that caused the initialization failure
- Uses the same thread-safe provider switching as `set_provider`

In some situations, it may be beneficial to register multiple providers in the same application.
Expand Down Expand Up @@ -231,15 +233,47 @@ legacy_flag_client = OpenFeature::SDK.build_client(domain: "legacy_flags")

### Eventing

Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/51) to be worked on.

<!-- Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions.
Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions.
Initialization events (`PROVIDER_READY` on success, `PROVIDER_ERROR` on failure) are dispatched for every provider.
Some providers support additional events, such as `PROVIDER_CONFIGURATION_CHANGED`.

Please refer to the documentation of the provider you're using to see what events are supported. -->
Please refer to the documentation of the provider you're using to see what events are supported.

```ruby
# Register event handlers at the API (global) level
ready_handler = ->(event_details) do
puts "Provider #{event_details[:provider].metadata.name} is ready!"
end

OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, ready_handler)

# The SDK automatically emits lifecycle events. Providers can emit additional spontaneous events
# using the EventEmitter mixin to signal internal state changes like configuration updates.
class MyEventAwareProvider
include OpenFeature::SDK::Provider::EventEmitter

<!-- TODO: code example of a PROVIDER_CONFIGURATION_CHANGED event for the client and a PROVIDER_STALE event for the API -->
def init(evaluation_context)
# Start background process to monitor for configuration changes
# Note: SDK automatically emits PROVIDER_READY when init completes successfully
start_background_process
end

def start_background_process
Thread.new do
# Monitor for configuration changes and emit events when they occur
if configuration_changed?
emit_event(
OpenFeature::SDK::ProviderEvent::PROVIDER_CONFIGURATION_CHANGED,
message: "Flag configuration updated"
)
end
end
end
end

# Remove specific handlers when no longer needed
OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, ready_handler)
```

### Shutdown

Expand Down
16 changes: 16 additions & 0 deletions lib/open_feature/sdk/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ def build_client(domain: nil, evaluation_context: nil)
rescue
Client.new(provider: Provider::NoOpProvider.new, evaluation_context:)
end

def add_handler(event_type, handler)
configuration.add_handler(event_type, handler)
end

def remove_handler(event_type, handler)
configuration.remove_handler(event_type, handler)
end

def logger
configuration.logger
end

def logger=(new_logger)
configuration.logger = new_logger
end
end
end
end
10 changes: 10 additions & 0 deletions lib/open_feature/sdk/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ def initialize(provider:, domain: nil, evaluation_context: nil)
@hooks = []
end

def add_handler(event_type, handler = nil, &block)
actual_handler = handler || block
OpenFeature::SDK.configuration.add_client_handler(self, event_type, actual_handler)
end

def remove_handler(event_type, handler = nil, &block)
actual_handler = handler || block
OpenFeature::SDK.configuration.remove_client_handler(self, event_type, actual_handler)
end

RESULT_TYPE.each do |result_type|
SUFFIXES.each do |suffix|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
Expand Down
Loading
Loading