diff --git a/README.md b/README.md index e65fd229..4bf76544 100644 --- a/README.md +++ b/README.md @@ -100,13 +100,14 @@ object = client.fetch_object_value(flag_key: 'object_value', default_value: { na | ------ | --------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. | | ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). | -| ⚠️ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. | +| ✅ | [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. | -| ⚠️ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | +| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | +| ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations for experimentation. | | ❌ | [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. | +| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. | Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌ @@ -200,15 +201,49 @@ bool_value = client.fetch_boolean_value( ### Hooks -Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on. - - +Hooks can be registered at the global, client, or flag invocation level. + +```ruby +# Define a hook +class MyHook + include OpenFeature::SDK::Hooks::Hook + + def before(hook_context:, hints:) + puts "Evaluating flag: #{hook_context.flag_key}" + nil + end + + def after(hook_context:, evaluation_details:, hints:) + puts "Flag #{hook_context.flag_key} evaluated to: #{evaluation_details.value}" + end + + def error(hook_context:, exception:, hints:) + puts "Error evaluating #{hook_context.flag_key}: #{exception.message}" + end + + def finally(hook_context:, evaluation_details:, hints:) + puts "Evaluation complete for #{hook_context.flag_key}" + end +end + +# Register at the API (global) level +OpenFeature::SDK.hooks << MyHook.new + +# Register at the client level +client = OpenFeature::SDK.build_client +client.hooks << MyHook.new - +# Register at the invocation level +client.fetch_boolean_value( + flag_key: "my-flag", + default_value: false, + hooks: [MyHook.new] +) +``` ### Logging @@ -276,19 +311,53 @@ OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, ### Shutdown -Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/149) to be worked on. - - +``` + +### Tracking + +The tracking API allows you to use OpenFeature abstractions and objects to associate user actions with feature flag evaluations. +This is essential for robust experimentation powered by feature flags. +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. + +```ruby +client = OpenFeature::SDK.build_client + +# Simple tracking event +client.track("checkout_completed") + +# With evaluation context +client.track( + "purchase", + evaluation_context: OpenFeature::SDK::EvaluationContext.new(targeting_key: "user-123") +) + +# With tracking event details (optional numeric value + custom fields) +details = OpenFeature::SDK::TrackingEventDetails.new( + value: 99.99, + plan: "premium", + currency: "USD" +) +client.track("subscription", tracking_event_details: details) +``` + +Note that some providers may not support tracking; if the provider does not implement a `track` method, the call is a no-op. +Check the documentation for your [provider](#providers) for more information. ### Transaction Context Propagation @@ -344,6 +413,12 @@ class MyProvider def fetch_object_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a hash value from provider source end + + # Optional: implement tracking support (spec 6.1.4) + # If not defined, Client#track is a no-op + def track(tracking_event_name, evaluation_context:, tracking_event_details:) + # Record a tracking event with your flag management system + end end ``` @@ -351,17 +426,29 @@ end ### Develop a hook -Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on. - - +Implement your own hook by including the `OpenFeature::SDK::Hooks::Hook` module. +You only need to define the stages you care about — unimplemented stages are no-ops by default. + +```ruby +class MyLoggingHook + include OpenFeature::SDK::Hooks::Hook - + def before(hook_context:, hints:) + puts "Evaluating #{hook_context.flag_key}" + nil # Return nil or an EvaluationContext to merge + end + + def after(hook_context:, evaluation_details:, hints:) + puts "Result: #{evaluation_details.value}" + end + + # error and finally are optional — only define what you need +end +``` - +> 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! ## ⭐️ Support the project