Skip to content

Commit a25d610

Browse files
josecolellaclaude
andauthored
docs: update README with hooks, shutdown, tracking, and hook development (#228)
Signed-off-by: Jose Colella <jose.colella@gusto.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5576fce commit a25d610

File tree

1 file changed

+108
-21
lines changed

1 file changed

+108
-21
lines changed

README.md

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,14 @@ object = client.fetch_object_value(flag_key: 'object_value', default_value: { na
100100
| ------ | --------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
101101
|| [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
102102
|| [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
103-
| ⚠️ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
103+
| | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
104104
|| [Logging](#logging) | Integrate with popular logging packages. |
105105
|| [Domains](#domains) | Logically bind clients with providers. |
106106
|| [Eventing](#eventing) | React to state changes in the provider or flag management system. |
107-
| ⚠️ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
107+
|| [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
108+
|| [Tracking](#tracking) | Associate user actions with feature flag evaluations for experimentation. |
108109
|| [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) |
109-
| ⚠️ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
110+
| | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
110111

111112
<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
112113

@@ -200,15 +201,49 @@ bool_value = client.fetch_boolean_value(
200201

201202
### Hooks
202203

203-
Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on.
204-
205-
<!-- [Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle.
204+
[Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle.
206205
Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=Ruby) for a complete list of available hooks.
207206
If the hook you're looking for hasn't been created yet, see the [develop a hook](#develop-a-hook) section to learn how to build it yourself.
208207

209-
Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level. -->
208+
Hooks can be registered at the global, client, or flag invocation level.
209+
210+
```ruby
211+
# Define a hook
212+
class MyHook
213+
include OpenFeature::SDK::Hooks::Hook
214+
215+
def before(hook_context:, hints:)
216+
puts "Evaluating flag: #{hook_context.flag_key}"
217+
nil
218+
end
219+
220+
def after(hook_context:, evaluation_details:, hints:)
221+
puts "Flag #{hook_context.flag_key} evaluated to: #{evaluation_details.value}"
222+
end
223+
224+
def error(hook_context:, exception:, hints:)
225+
puts "Error evaluating #{hook_context.flag_key}: #{exception.message}"
226+
end
227+
228+
def finally(hook_context:, evaluation_details:, hints:)
229+
puts "Evaluation complete for #{hook_context.flag_key}"
230+
end
231+
end
232+
233+
# Register at the API (global) level
234+
OpenFeature::SDK.hooks << MyHook.new
235+
236+
# Register at the client level
237+
client = OpenFeature::SDK.build_client
238+
client.hooks << MyHook.new
210239

211-
<!-- TODO: code example of setting hooks at all levels -->
240+
# Register at the invocation level
241+
client.fetch_boolean_value(
242+
flag_key: "my-flag",
243+
default_value: false,
244+
hooks: [MyHook.new]
245+
)
246+
```
212247

213248
### Logging
214249

@@ -276,19 +311,53 @@ OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY,
276311

277312
### Shutdown
278313

279-
Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/149) to be worked on.
280-
281-
<!-- TODO The OpenFeature API provides a close function to perform a cleanup of all registered providers.
314+
The OpenFeature API provides a `shutdown` method to perform cleanup of all registered providers.
282315
This should only be called when your application is in the process of shutting down.
283316

317+
```ruby
318+
# Shut down all registered providers and clear state
319+
OpenFeature::SDK.shutdown
320+
```
321+
322+
Individual providers can implement a `shutdown` method to perform cleanup:
323+
284324
```ruby
285325
class MyProvider
286326
def shutdown
287327
# Perform any shutdown/reclamation steps with flag management system here
288-
# Return value is ignored
289328
end
290329
end
291-
``` -->
330+
```
331+
332+
### Tracking
333+
334+
The tracking API allows you to use OpenFeature abstractions and objects to associate user actions with feature flag evaluations.
335+
This is essential for robust experimentation powered by feature flags.
336+
For example, a flag enhancing the appearance of a UI component might drive user engagement to a new feature; to test this hypothesis, telemetry collected by a [hook](#hooks) or [provider](#providers) can be associated with telemetry reported in the client's `track` function.
337+
338+
```ruby
339+
client = OpenFeature::SDK.build_client
340+
341+
# Simple tracking event
342+
client.track("checkout_completed")
343+
344+
# With evaluation context
345+
client.track(
346+
"purchase",
347+
evaluation_context: OpenFeature::SDK::EvaluationContext.new(targeting_key: "user-123")
348+
)
349+
350+
# With tracking event details (optional numeric value + custom fields)
351+
details = OpenFeature::SDK::TrackingEventDetails.new(
352+
value: 99.99,
353+
plan: "premium",
354+
currency: "USD"
355+
)
356+
client.track("subscription", tracking_event_details: details)
357+
```
358+
359+
Note that some providers may not support tracking; if the provider does not implement a `track` method, the call is a no-op.
360+
Check the documentation for your [provider](#providers) for more information.
292361

293362
### Transaction Context Propagation
294363

@@ -344,24 +413,42 @@ class MyProvider
344413
def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
345414
# Retrieve a hash value from provider source
346415
end
416+
417+
# Optional: implement tracking support (spec 6.1.4)
418+
# If not defined, Client#track is a no-op
419+
def track(tracking_event_name, evaluation_context:, tracking_event_details:)
420+
# Record a tracking event with your flag management system
421+
end
347422
end
348423
```
349424

350425
> Built a new provider? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&projects=&template=document-provider.yaml&title=%5BProvider%5D%3A+) so we can add it to the docs!
351426
352427
### Develop a hook
353428

354-
Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on.
355-
356-
<!-- To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
429+
To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
357430
This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/ruby-sdk-contrib) available under the OpenFeature organization.
358-
Implement your own hook by conforming to the `Hook interface`.
359-
To satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined.
360-
To avoid defining empty functions, make use of the `UnimplementedHook` struct (which already implements all the empty functions). -->
431+
Implement your own hook by including the `OpenFeature::SDK::Hooks::Hook` module.
432+
You only need to define the stages you care about — unimplemented stages are no-ops by default.
433+
434+
```ruby
435+
class MyLoggingHook
436+
include OpenFeature::SDK::Hooks::Hook
361437

362-
<!-- TODO: code example of hook implementation -->
438+
def before(hook_context:, hints:)
439+
puts "Evaluating #{hook_context.flag_key}"
440+
nil # Return nil or an EvaluationContext to merge
441+
end
442+
443+
def after(hook_context:, evaluation_details:, hints:)
444+
puts "Result: #{evaluation_details.value}"
445+
end
446+
447+
# error and finally are optional — only define what you need
448+
end
449+
```
363450

364-
<!-- > Built a new hook? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+) so we can add it to the docs! -->
451+
> Built a new hook? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+) so we can add it to the docs!
365452
366453
<!-- x-hide-in-docs-start -->
367454
## ⭐️ Support the project

0 commit comments

Comments
 (0)