open-feature / ruby-sdk

Ruby implementation of the OpenFeature SDK
https://openfeature.dev
Apache License 2.0
21 stars 7 forks source link
openfeature ruby sdk

OpenFeature Logo

OpenFeature Ruby SDK

Specification Release
CII Best Practices

[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool or in-house solution. ## 🚀 Quick start ### Requirements | Supported Ruby Version | OS | | ------------ | --------------------- | | Ruby 3.1.4 | Windows, MacOS, Linux | | Ruby 3.2.3 | Windows, MacOS, Linux | | Ruby 3.3.0 | Windows, MacOS, Linux | ### Install Install the gem and add to the application's Gemfile by executing: ```sh bundle add openfeature-sdk ``` If bundler is not being used to manage dependencies, install the gem by executing: ```sh gem install openfeature-sdk ``` ### Usage ```ruby require 'open_feature/sdk' require 'json' # For JSON.dump # API Initialization and configuration OpenFeature::SDK.configure do |config| # your provider of choice, which will be used as the default provider config.set_provider(OpenFeature::SDK::Provider::InMemoryProvider.new( { "flag1" => true, "flag2" => 1 } )) end # Create a client client = OpenFeature::SDK.build_client # fetching boolean value feature flag bool_value = client.fetch_boolean_value(flag_key: 'boolean_flag', default_value: false) # a details method is also available for more information about the flag evaluation # see `ResolutionDetails` for more info bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false) == # fetching string value feature flag string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: false) # fetching number value feature flag float_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1.0) integer_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1) # get an object value object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON.dump({ name: 'object'})) ``` ## 🌟 Features | Status | Features | Description | | ------ | --------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | ✅ | [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. | | ❌ | [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. | | ❌ | [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. | Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌ ### Providers [Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK. Look [here](https://openfeature.dev/ecosystem?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=Ruby) for a complete list of available providers. If the provider you're looking for hasn't been created yet, see the [develop a provider](#develop-a-provider) section to learn how to build it yourself. Once you've added a provider as a dependency, it can be registered with OpenFeature like this: ```ruby OpenFeature::SDK.configure do |config| # your provider of choice, which will be used as the default provider config.set_provider(OpenFeature::SDK::Provider::InMemoryProvider.new( { "v2_enabled" => true, } )) end ``` In some situations, it may be beneficial to register multiple providers in the same application. This is possible using [domains](#domains), which is covered in more detail below. ### Targeting Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location. In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting). If the flag management system you're using supports targeting, you can provide the input data using the [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). ```ruby OpenFeature::SDK.configure do |config| # you can set a global evaluation context here config.evaluation_context = OpenFeature::SDK::EvaluationContext.new("host" => "myhost.com") end # Evaluation context can be set on a client as well client_with_context = OpenFeature::SDK.build_client( evaluation_context: OpenFeature::SDK::EvaluationContext.new("controller_name" => "admin") ) # Invocation evaluation context can also be passed in during flag evaluation. # During flag evaluation, invocation context takes precedence over client context # which takes precedence over API (aka global) context. bool_value = client.fetch_boolean_value( flag_key: 'boolean_flag', default_value: false, evaluation_context: OpenFeature::SDK::EvaluationContext.new("is_friday" => true) ) ``` ### Hooks Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on. ### Logging Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/148) to work on. ### Domains Clients can be assigned to a domain. A domain is a logical identifier which can be used to associate clients with a particular provider. If a domain has no associated provider, the default provider is used. ```ruby OpenFeature::SDK.configure do |config| config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new, domain: "legacy_flags") end # Create a client for a different domain, this will use the provider assigned to that domain 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. ### Shutdown Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/149) to be worked on. ### Transaction Context Propagation Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/150) to be worked on. ## Extending ### Develop a provider To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. 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. You’ll then need to write the provider by implementing the `Provider` duck. ```ruby class MyProvider def init # Perform any initialization steps with flag management system here # Return value is ignored # **Note** The OpenFeature spec defines a lifecycle method called `initialize` to be called when a new provider is set. # To avoid conflicting with the Ruby `initialize` method, this method should be named `init` when creating a provider. end def shutdown # Perform any shutdown/reclamation steps with flag management system here # Return value is ignored end def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a boolean value from provider source end def fetch_string_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a string value from provider source end def fetch_number_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a numeric value from provider source end def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a integer value from provider source end def fetch_float_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a float value from provider source end def fetch_object_value(flag_key:, default_value:, evaluation_context: nil) # Retrieve a hash value from provider source end end ``` > 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! ### Develop a hook Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on. ## ⭐️ Support the project - Give this repo a ⭐️! - Follow us on social media: - Twitter: [@openfeature](https://twitter.com/openfeature) - LinkedIn: [OpenFeature](https://www.linkedin.com/company/openfeature/) - Join us on [Slack](https://cloud-native.slack.com/archives/C0344AANLA1) - For more, check out our [community page](https://openfeature.dev/community/) ## 🤝 Contributing Interested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide. ### Thanks to everyone who has already contributed Pictures of the folks who have contributed to the project Made with [contrib.rocks](https://contrib.rocks).