Skip to content

Commit 4e492cc

Browse files
committed
refactor: consolidate duplicate immediate handler methods
- Replace run_immediate_handler_for_api and run_immediate_handler with single unified method - Update method signature to require explicit client parameter (nil for API-level) - Fix test isolation by clearing provider state in client event handler tests - Update test expectations to account for immediate handler execution - Remove unused default parameters from helper methods
1 parent bfb771d commit 4e492cc

File tree

3 files changed

+85
-31
lines changed

3 files changed

+85
-31
lines changed

lib/open_feature/sdk/configuration.rb

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def logger=(new_logger)
4242

4343
def add_handler(event_type, handler)
4444
@event_emitter.add_handler(event_type, handler)
45+
run_immediate_handler(event_type, handler, nil)
4546
end
4647

4748
def remove_handler(event_type, handler)
@@ -61,7 +62,7 @@ def add_client_handler(client, event_type, handler)
6162
@client_handlers[client][event_type] << handler
6263
end
6364

64-
run_immediate_handler(client, event_type, handler)
65+
run_immediate_handler(event_type, handler, client)
6566
end
6667

6768
def remove_client_handler(client, event_type, handler)
@@ -191,30 +192,48 @@ def run_handlers_for_provider(provider, event_type, event_details)
191192
end
192193
end
193194

194-
def run_immediate_handler(client, event_type, handler)
195+
def run_immediate_handler(event_type, handler, client)
195196
status_to_event = {
196197
ProviderState::READY => ProviderEvent::PROVIDER_READY,
197198
ProviderState::ERROR => ProviderEvent::PROVIDER_ERROR,
198199
ProviderState::FATAL => ProviderEvent::PROVIDER_ERROR,
199200
ProviderState::STALE => ProviderEvent::PROVIDER_STALE
200201
}
201202

202-
# Get provider for specific domain, but don't fall back to default unless domain is nil
203-
client_provider = @providers[client.metadata.domain]
204-
client_provider ||= @providers[nil] if client.metadata.domain.nil?
205-
return unless client_provider
203+
if client.nil?
204+
# API-level handler: check all providers
205+
@providers.each do |domain, provider|
206+
next unless provider
206207

207-
provider_state = @provider_state_registry.get_state(client_provider)
208+
provider_state = @provider_state_registry.get_state(provider)
208209

209-
# Only run if the event type matches the current provider status
210-
if event_type == status_to_event[provider_state]
211-
provider_name = extract_provider_name(client_provider)
212-
event_details = {provider_name: provider_name}
210+
if event_type == status_to_event[provider_state]
211+
provider_name = extract_provider_name(provider)
212+
event_details = {provider_name: provider_name}
213213

214-
begin
215-
handler.call(event_details)
216-
rescue => e
217-
@logger&.error("Immediate client event handler failed: #{e.message}")
214+
begin
215+
handler.call(event_details)
216+
rescue => e
217+
@logger&.error("Immediate API event handler failed: #{e.message}")
218+
end
219+
end
220+
end
221+
else
222+
# Client-level handler: check specific provider only
223+
client_provider = provider(domain: client.metadata.domain)
224+
return unless client_provider
225+
226+
provider_state = @provider_state_registry.get_state(client_provider)
227+
228+
if event_type == status_to_event[provider_state]
229+
provider_name = extract_provider_name(client_provider)
230+
event_details = {provider_name: provider_name}
231+
232+
begin
233+
handler.call(event_details)
234+
rescue => e
235+
@logger&.error("Immediate client event handler failed: #{e.message}")
236+
end
218237
end
219238
end
220239
end

spec/open_feature/sdk/client_event_handlers_spec.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
end
2222

2323
it "allows clients to add event handlers scoped to their domain" do
24+
# Clear providers to ensure clean state - no default provider should exist
25+
OpenFeature::SDK.configuration.instance_variable_set(:@providers, {})
26+
OpenFeature::SDK.configuration.instance_variable_get(:@provider_state_registry).instance_variable_set(:@states, {})
27+
2428
events_received = []
2529

2630
# Create client with specific domain
@@ -42,6 +46,10 @@
4246
end
4347

4448
it "does not trigger handlers for other domains" do
49+
# Clear providers to ensure clean state - no default provider should exist
50+
OpenFeature::SDK.configuration.instance_variable_set(:@providers, {})
51+
OpenFeature::SDK.configuration.instance_variable_get(:@provider_state_registry).instance_variable_set(:@states, {})
52+
4553
events_received = []
4654

4755
# Create client with specific domain
@@ -60,6 +68,10 @@
6068
end
6169

6270
it "allows removal of client event handlers" do
71+
# Clear providers to ensure clean state - no default provider should exist
72+
OpenFeature::SDK.configuration.instance_variable_set(:@providers, {})
73+
OpenFeature::SDK.configuration.instance_variable_get(:@provider_state_registry).instance_variable_set(:@states, {})
74+
6375
events_received = []
6476

6577
client = OpenFeature::SDK.build_client(domain: "test_domain")
@@ -79,6 +91,10 @@
7991
end
8092

8193
it "supports block syntax for handlers" do
94+
# Clear providers to ensure clean state - no default provider should exist
95+
OpenFeature::SDK.configuration.instance_variable_set(:@providers, {})
96+
OpenFeature::SDK.configuration.instance_variable_get(:@provider_state_registry).instance_variable_set(:@states, {})
97+
8298
events_received = []
8399

84100
client = OpenFeature::SDK.build_client(domain: "test_domain")
@@ -113,7 +129,7 @@
113129

114130
private
115131

116-
def test_provider(name = "TestProvider")
132+
def test_provider(name)
117133
Class.new do
118134
include OpenFeature::SDK::Provider::EventHandler
119135

spec/specification/events_spec.rb

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
RSpec.describe "OpenFeature Specification: Events" do
77
before(:each) do
8-
# Reset to default provider
98
OpenFeature::SDK.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new)
109
end
1110

@@ -220,14 +219,16 @@ def shutdown
220219
OpenFeature::SDK.set_provider(provider1)
221220
sleep(0.1)
222221

223-
expect(handler_call_count).to eq(1)
222+
# Should be 2: 1 immediate (NoOpProvider is READY by set_provider in before(:each)) + 1 from InMemoryProvider
223+
expect(handler_call_count).to eq(2)
224224

225225
# Set second provider - handler should still be active
226226
provider2 = OpenFeature::SDK::Provider::NoOpProvider.new
227227
OpenFeature::SDK.set_provider(provider2)
228228
sleep(0.1)
229229

230-
expect(handler_call_count).to eq(2)
230+
# Should be 3: 1 immediate + 2 from provider changes
231+
expect(handler_call_count).to eq(3)
231232

232233
# Cleanup
233234
OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, handler)
@@ -256,22 +257,40 @@ def shutdown
256257
end
257258

258259
context "Requirement 5.3.3" do
259-
specify "Handlers attached after the provider is already in the associated state, MUST run immediately" do
260-
events_received = []
260+
describe "Handlers attached after the provider is already in the associated state, MUST run immediately" do
261+
context "API-level handlers" do
262+
specify "PROVIDER_READY handler runs immediately when provider is already READY" do
263+
events_received = []
261264

262-
# Set up provider and get it into READY state first
263-
provider = OpenFeature::SDK::Provider::InMemoryProvider.new
264-
OpenFeature::SDK.set_provider_and_wait(provider, domain: "immediate_test")
265+
provider = OpenFeature::SDK::Provider::InMemoryProvider.new
266+
OpenFeature::SDK.set_provider_and_wait(provider)
265267

266-
# Now create client and add handler - should run immediately since provider is READY
267-
client = OpenFeature::SDK.build_client(domain: "immediate_test")
268-
client.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY) do |event|
269-
events_received << event
268+
handler = ->(event) { events_received << event }
269+
OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, handler)
270+
271+
expect(events_received).to have_attributes(size: 1)
272+
expect(events_received[0][:provider_name]).to eq("In-memory Provider")
273+
274+
OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, handler)
275+
end
270276
end
271277

272-
# Handler should have executed immediately, no additional events needed
273-
expect(events_received).to have_attributes(size: 1)
274-
expect(events_received[0][:provider_name]).to eq("In-memory Provider")
278+
context "Client-level handlers" do
279+
specify "PROVIDER_READY handler runs immediately when provider is already READY" do
280+
events_received = []
281+
282+
provider = OpenFeature::SDK::Provider::InMemoryProvider.new
283+
OpenFeature::SDK.set_provider_and_wait(provider, domain: "immediate_test")
284+
285+
client = OpenFeature::SDK.build_client(domain: "immediate_test")
286+
client.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY) do |event|
287+
events_received << event
288+
end
289+
290+
expect(events_received).to have_attributes(size: 1)
291+
expect(events_received[0][:provider_name]).to eq("In-memory Provider")
292+
end
293+
end
275294
end
276295
end
277296
end

0 commit comments

Comments
 (0)