openfeature-sdk 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 121bf1c0f068a672898b818d3241b8a62eb31bfc16b5c777064b509c05c75ffa
4
- data.tar.gz: e48cd05f98c6cc4c54697d716ea6c3fcee273b602de2fdf743a3bfb2b7cc65c1
3
+ metadata.gz: c91c19e8a0f7ef628c048224287a276713d704914142d4a2771bd74bf01ea68e
4
+ data.tar.gz: 565d980320e40d5a573c4710d6313f038c0a58350f6d5eb1564dcd5c85148853
5
5
  SHA512:
6
- metadata.gz: 60bea62f478cc58a7a5b4a5defc6b51fc1dc9af83770102ef49ad26d3604d25ceb76f2752edbfdec6d598c8ee0f7aaefb6d8ded5d437ab1e041c921438b5be71
7
- data.tar.gz: b13c80c92cbafe9902f445e9bbd170cec6ca4fd2a73c86f72bfaf7c09cd208ef75ba27b6f69f9a72d58a0f59bf1b65f44275abb27b18493029524661697d6f64
6
+ metadata.gz: 7d5dd24ccd3b4e61de24d7a46d02169ad7373d74776664fc0686db4d51dde98c8851c81e392043130f08a4dfd6cdb877a31c3a5ecb2d42c7bd2f5c92b1008fef
7
+ data.tar.gz: f8ed5ee65b6bee9dd1e36e1373248b12f3729a5ce5a22857c4212957246f7c3b150d48d5cea6e59a7e164d06e69bb53a5d90fc883924c85aaa4f6d64b15e4764
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.4.0"
2
+ ".": "0.5.0"
3
3
  }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.3
1
+ 3.4.8
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.3.3
1
+ ruby 3.4.8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/open-feature/ruby-sdk/compare/v0.4.1...v0.5.0) (2026-01-16)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * add provider eventing, remove setProvider timeout ([#207](https://github.com/open-feature/ruby-sdk/issues/207))
9
+
10
+ ### Features
11
+
12
+ * add provider eventing, remove setProvider timeout ([#207](https://github.com/open-feature/ruby-sdk/issues/207)) ([fffd615](https://github.com/open-feature/ruby-sdk/commit/fffd6155ff308c75220185de030c38eb6eeac7e8))
13
+
14
+ ## [0.4.1](https://github.com/open-feature/ruby-sdk/compare/v0.4.0...v0.4.1) (2025-11-03)
15
+
16
+
17
+ ### Features
18
+
19
+ * Add runtime type validation for default values in flag evaluation methods ([#194](https://github.com/open-feature/ruby-sdk/issues/194)) ([dc56c76](https://github.com/open-feature/ruby-sdk/commit/dc56c76f987c16b22a284513b7d0f383d38a0198))
20
+ * Add setProviderAndWait method for blocking provider initialization ([#200](https://github.com/open-feature/ruby-sdk/issues/200)) ([d92eabc](https://github.com/open-feature/ruby-sdk/commit/d92eabcb54335e21522cea0d6b10be3e842ce8e2))
21
+
3
22
  ## [0.4.0](https://github.com/open-feature/ruby-sdk/compare/v0.3.1...v0.4.0) (2024-06-13)
4
23
 
5
24
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,98 @@
1
+ # Contributing to the OpenFeature project
2
+
3
+ ## Development
4
+
5
+ You can contribute to this project from a Windows, macOS or Linux machine.
6
+
7
+ On all platforms, the minimum requirements are:
8
+
9
+ * Git client and command line tools.
10
+ * Ruby 3.0 or higher
11
+
12
+ ## Pull Request
13
+
14
+ All contributions to the OpenFeature project are welcome via GitHub pull requests.
15
+
16
+ To create a new PR, you will need to first fork the GitHub repository and clone upstream.
17
+
18
+ ```bash
19
+ git clone https://github.com/open-feature/ruby-sdk.git openfeature-ruby-sdk
20
+ ```
21
+
22
+ Navigate to the repository folder
23
+ ```bash
24
+ cd openfeature-ruby-sdk
25
+ ```
26
+
27
+ Add your fork as an origin
28
+ ```bash
29
+ git remote add fork https://github.com/YOUR_GITHUB_USERNAME/ruby-sdk.git
30
+ ```
31
+
32
+ To start working on a new feature or bugfix, create a new branch and start working on it.
33
+
34
+ ```bash
35
+ git checkout -b feat/NAME_OF_FEATURE
36
+ # Make your changes
37
+ git commit -s
38
+ git push fork feat/NAME_OF_FEATURE
39
+ ```
40
+
41
+ Open a pull request against the main ruby-sdk repository.
42
+
43
+ ### Running checks locally
44
+
45
+ #### Unit tests
46
+
47
+ To run unit tests and other checks:
48
+
49
+ ```bash
50
+ bundle exec rake
51
+ ```
52
+
53
+ ### How to Receive Comments
54
+
55
+ * If the PR is not ready for review, please mark it as
56
+ [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/).
57
+ * Make sure all required CI checks are clear.
58
+ * Submit small, focused PRs addressing a single concern/issue.
59
+ * Make sure the PR title reflects the contribution.
60
+ * Write a summary that helps understand the change.
61
+ * Include usage examples in the summary, where applicable.
62
+
63
+ ### How to Get PRs Merged
64
+
65
+ A PR is considered to be **ready to merge** when:
66
+
67
+ * Major feedbacks are resolved.
68
+ * It has been open for review for at least one working day. This gives people
69
+ reasonable time to review.
70
+ * Trivial change (typo, cosmetic, doc, etc.) doesn't have to wait for one day.
71
+ * Urgent fix can take exception as long as it has been actively communicated.
72
+
73
+ Any Maintainer can merge the PR once it is **ready to merge**. Note, that some
74
+ PRs may not be merged immediately if the repo is in the process of a release and
75
+ the maintainers decided to defer the PR to the next release train.
76
+
77
+ If a PR has been stuck (e.g. there are lots of debates and people couldn't agree
78
+ on each other), the owner should try to get people aligned by:
79
+
80
+ * Consolidating the perspectives and putting a summary in the PR. It is
81
+ recommended to add a link into the PR description, which points to a comment
82
+ with a summary in the PR conversation.
83
+ * Tagging subdomain experts (by looking at the change history) in the PR asking
84
+ for suggestion.
85
+ * Reaching out to more people on the [CNCF OpenFeature Slack channel](https://cloud-native.slack.com/archives/C0344AANLA1).
86
+ * Stepping back to see if it makes sense to narrow down the scope of the PR or
87
+ split it up.
88
+ * If none of the above worked and the PR has been stuck for more than 2 weeks,
89
+ the owner should bring it to the OpenFeatures [meeting](README.md#contributing).
90
+
91
+ ## Automated Changelog
92
+
93
+ Each time a release is published the changelogs will be generated automatically using `release-please`.
94
+
95
+ ## Design Choices
96
+
97
+ As with other OpenFeature SDKs, ruby-sdk follows the
98
+ [openfeature-specification](https://github.com/open-feature/spec).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openfeature-sdk (0.4.0)
4
+ openfeature-sdk (0.5.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -34,7 +34,8 @@ GEM
34
34
  regexp_parser (2.9.0)
35
35
  reline (0.5.0)
36
36
  io-console (~> 0.5)
37
- rexml (3.2.6)
37
+ rexml (3.3.6)
38
+ strscan
38
39
  rspec (3.12.0)
39
40
  rspec-core (~> 3.12.0)
40
41
  rspec-expectations (~> 3.12.0)
@@ -87,12 +88,15 @@ GEM
87
88
  lint_roller (~> 1.1)
88
89
  rubocop-performance (~> 1.20.2)
89
90
  stringio (3.1.0)
91
+ strscan (3.1.0)
92
+ timecop (0.9.10)
90
93
  unicode-display_width (2.5.0)
91
94
 
92
95
  PLATFORMS
93
96
  arm64-darwin-21
94
97
  arm64-darwin-22
95
98
  arm64-darwin-23
99
+ arm64-darwin-24
96
100
  x64-mingw-ucrt
97
101
  x64-mingw32
98
102
  x86_64-darwin-19
@@ -110,6 +114,7 @@ DEPENDENCIES
110
114
  simplecov-cobertura (~> 2.1.0)
111
115
  standard
112
116
  standard-performance
117
+ timecop (~> 0.9.10)
113
118
 
114
119
  BUNDLED WITH
115
120
  2.3.25
data/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright OpenFeature Maintainers
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -1,24 +1,48 @@
1
- # OpenFeature SDK for Ruby
2
-
3
- [![a](https://img.shields.io/badge/slack-%40cncf%2Fopenfeature-brightgreen?style=flat&logo=slack)](https://cloud-native.slack.com/archives/C0344AANLA1)
4
- [![v0.5.1](https://img.shields.io/static/v1?label=Specification&message=v0.5.1&color=yellow)](https://github.com/open-feature/spec/tree/v0.5.1)
5
- ![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=for-the-badge&logo=ruby&logoColor=white)
6
- ![Build](https://github.com/open-feature/openfeature-ruby/actions/workflows/main.yml/badge.svg?branch=main)
7
- ![Gem version](https://img.shields.io/gem/v/openfeature-sdk)
8
-
9
- This is the Ruby implementation of [OpenFeature](https://openfeature.dev), a vendor-agnostic abstraction library for evaluating feature flags.
10
-
11
- We support multiple data types for flags (numbers, strings, booleans, objects) as well as hooks, which can alter the lifecycle of a flag evaluation.
12
-
13
- ## Support Matrix
14
-
15
- | Ruby Version | OS |
1
+ <!-- markdownlint-disable MD033 -->
2
+ <!-- x-hide-in-docs-start -->
3
+ <p align="center">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg" />
6
+ <img align="center" alt="OpenFeature Logo" src="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg" />
7
+ </picture>
8
+ </p>
9
+
10
+ <h2 align="center">OpenFeature Ruby SDK</h2>
11
+
12
+ <!-- x-hide-in-docs-end -->
13
+ <!-- The 'github-badges' class is used in the docs -->
14
+ <p align="center" class="github-badges">
15
+ <a href="https://github.com/open-feature/spec/releases/tag/v0.8.0">
16
+ <img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.8.0&color=yellow&style=for-the-badge" />
17
+ </a>
18
+ <!-- x-release-please-start-version -->
19
+
20
+ <a href="https://github.com/open-feature/ruby-sdk/releases/tag/v0.5.0">
21
+ <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.5.0&color=blue&style=for-the-badge" />
22
+ </a>
23
+
24
+ <!-- x-release-please-end -->
25
+ <br/>
26
+ <a href="https://bestpractices.coreinfrastructure.org/projects/9337">
27
+ <img alt="CII Best Practices" src="https://bestpractices.coreinfrastructure.org/projects/9337/badge" />
28
+ </a>
29
+ </p>
30
+ <!-- x-hide-in-docs-start -->
31
+
32
+ [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.
33
+
34
+ <!-- x-hide-in-docs-end -->
35
+ ## 🚀 Quick start
36
+
37
+ ### Requirements
38
+
39
+ | Supported Ruby Version | OS |
16
40
  | ------------ | --------------------- |
17
41
  | Ruby 3.1.4 | Windows, MacOS, Linux |
18
42
  | Ruby 3.2.3 | Windows, MacOS, Linux |
19
43
  | Ruby 3.3.0 | Windows, MacOS, Linux |
20
44
 
21
- ## Installation
45
+ ### Install
22
46
 
23
47
  Install the gem and add to the application's Gemfile by executing:
24
48
 
@@ -32,7 +56,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
32
56
  gem install openfeature-sdk
33
57
  ```
34
58
 
35
- ## Usage
59
+ ### Usage
36
60
 
37
61
  ```ruby
38
62
  require 'open_feature/sdk'
@@ -48,33 +72,122 @@ OpenFeature::SDK.configure do |config|
48
72
  "flag2" => 1
49
73
  }
50
74
  ))
51
- # alternatively, you can bind multiple providers to different domains
52
- config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new, domain: "legacy_flags")
53
- # you can set a global evaluation context here
54
- config.evaluation_context = OpenFeature::SDK::EvaluationContext.new("host" => "myhost.com")
55
75
  end
56
76
 
57
77
  # Create a client
58
78
  client = OpenFeature::SDK.build_client
59
- # Create a client for a different domain, this will use the provider assigned to that domain
60
- legacy_flag_client = OpenFeature::SDK.build_client(domain: "legacy_flags")
61
- # Evaluation context can be set on a client as well
62
- client_with_context = OpenFeature::SDK.build_client(
63
- evaluation_context: OpenFeature::SDK::EvaluationContext.new("controller_name" => "admin")
64
- )
65
79
 
66
80
  # fetching boolean value feature flag
67
81
  bool_value = client.fetch_boolean_value(flag_key: 'boolean_flag', default_value: false)
68
82
 
83
+ # a details method is also available for more information about the flag evaluation
84
+ # see `ResolutionDetails` for more info
85
+ bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false)
86
+
69
87
  # fetching string value feature flag
70
- string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: false)
88
+ string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: 'default')
71
89
 
72
90
  # fetching number value feature flag
73
91
  float_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1.0)
74
92
  integer_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1)
75
93
 
76
94
  # get an object value
77
- object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON.dump({ name: 'object'}))
95
+ object = client.fetch_object_value(flag_key: 'object_value', default_value: { name: 'object'})
96
+ ```
97
+
98
+ ## 🌟 Features
99
+
100
+ | Status | Features | Description |
101
+ | ------ | --------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
102
+ | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
103
+ | ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
104
+ | ⚠️ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
105
+ | ❌ | [Logging](#logging) | Integrate with popular logging packages. |
106
+ | ✅ | [Domains](#domains) | Logically bind clients with providers. |
107
+ | ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
108
+ | ⚠️ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
109
+ | ❌ | [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) |
110
+ | ⚠️ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
111
+
112
+ <sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
113
+
114
+ ### Providers
115
+
116
+ [Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK.
117
+ 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.
118
+ 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.
119
+
120
+ Once you've added a provider as a dependency, it can be registered with OpenFeature like this:
121
+
122
+ ```ruby
123
+ OpenFeature::SDK.configure do |config|
124
+ # your provider of choice, which will be used as the default provider
125
+ config.set_provider(OpenFeature::SDK::Provider::InMemoryProvider.new(
126
+ {
127
+ "v2_enabled" => true,
128
+ }
129
+ ))
130
+ end
131
+ ```
132
+
133
+ #### Blocking Provider Registration
134
+
135
+ If you need to ensure that a provider is fully initialized before continuing, you can use `set_provider_and_wait`:
136
+
137
+ ```ruby
138
+ # Using the SDK directly
139
+ begin
140
+ OpenFeature::SDK.set_provider_and_wait(my_provider)
141
+ puts "Provider is ready!"
142
+ rescue OpenFeature::SDK::ProviderInitializationError => e
143
+ puts "Provider failed to initialize: #{e.message}"
144
+ puts "Error code: #{e.error_code}"
145
+ # original_error contains the underlying exception that caused the initialization failure
146
+ puts "Original error: #{e.original_error}"
147
+ end
148
+
149
+ # With custom timeout (default is 30 seconds)
150
+ OpenFeature::SDK.set_provider_and_wait(my_provider, timeout: 60)
151
+
152
+ # Domain-specific provider
153
+ OpenFeature::SDK.set_provider_and_wait(my_provider, domain: "feature-flags")
154
+
155
+ # Via configuration block
156
+ OpenFeature::SDK.configure do |config|
157
+ begin
158
+ config.set_provider_and_wait(my_provider)
159
+ rescue OpenFeature::SDK::ProviderInitializationError => e
160
+ # Handle initialization failure
161
+ end
162
+ end
163
+ ```
164
+
165
+ The `set_provider_and_wait` method:
166
+ - Waits for the provider's `init` method to complete successfully
167
+ - Raises `ProviderInitializationError` if initialization fails or times out
168
+ - Provides access to the provider instance, error code, and original exception for debugging
169
+ - The `original_error` field contains the underlying exception that caused the initialization failure
170
+ - Uses the same thread-safe provider switching as `set_provider`
171
+
172
+ In some situations, it may be beneficial to register multiple providers in the same application.
173
+ This is possible using [domains](#domains), which is covered in more detail below.
174
+
175
+ ### Targeting
176
+
177
+ 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.
178
+ In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting).
179
+ 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).
180
+
181
+ ```ruby
182
+ OpenFeature::SDK.configure do |config|
183
+ # you can set a global evaluation context here
184
+ config.evaluation_context = OpenFeature::SDK::EvaluationContext.new("host" => "myhost.com")
185
+ end
186
+
187
+ # Evaluation context can be set on a client as well
188
+ client_with_context = OpenFeature::SDK.build_client(
189
+ evaluation_context: OpenFeature::SDK::EvaluationContext.new("controller_name" => "admin")
190
+ )
78
191
 
79
192
  # Invocation evaluation context can also be passed in during flag evaluation.
80
193
  # During flag evaluation, invocation context takes precedence over client context
@@ -86,38 +199,191 @@ bool_value = client.fetch_boolean_value(
86
199
  )
87
200
  ```
88
201
 
89
- For complete documentation, visit: https://openfeature.dev/docs/category/concepts
202
+ ### Hooks
90
203
 
91
- ### Providers
204
+ Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on.
205
+
206
+ <!-- [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.
207
+ 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.
208
+ 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.
209
+
210
+ Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level. -->
211
+
212
+ <!-- TODO: code example of setting hooks at all levels -->
92
213
 
93
- Providers are the abstraction layer between OpenFeature and different flag management systems.
214
+ ### Logging
94
215
 
95
- The `NoOpProvider` is an example of a minimalist provider. The `InMemoryProvider` is a provider that can be initialized with flags and used to store flags in process. For complete documentation on the Provider interface, visit: https://openfeature.dev/specification/sections/providers.
216
+ Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/148) to work on.
96
217
 
97
- In addition to the `fetch_*` methods, providers can optionally implement lifecycle methods that are invoked when the underlying provider is switched out. For example:
218
+ <!-- TODO: talk about logging config and include a code example -->
219
+
220
+ ### Domains
221
+
222
+ Clients can be assigned to a domain. A domain is a logical identifier which can be used to associate clients with a particular provider.
223
+ If a domain has no associated provider, the default provider is used.
224
+
225
+ ```ruby
226
+ OpenFeature::SDK.configure do |config|
227
+ config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new, domain: "legacy_flags")
228
+ end
229
+
230
+ # Create a client for a different domain, this will use the provider assigned to that domain
231
+ legacy_flag_client = OpenFeature::SDK.build_client(domain: "legacy_flags")
232
+ ```
233
+
234
+ ### Eventing
235
+
236
+ 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.
237
+ Initialization events (`PROVIDER_READY` on success, `PROVIDER_ERROR` on failure) are dispatched for every provider.
238
+ Some providers support additional events, such as `PROVIDER_CONFIGURATION_CHANGED`.
239
+
240
+ Please refer to the documentation of the provider you're using to see what events are supported.
241
+
242
+ ```ruby
243
+ # Register event handlers at the API (global) level
244
+ ready_handler = ->(event_details) do
245
+ puts "Provider #{event_details[:provider].metadata.name} is ready!"
246
+ end
247
+
248
+ OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, ready_handler)
249
+
250
+ # The SDK automatically emits lifecycle events. Providers can emit additional spontaneous events
251
+ # using the EventEmitter mixin to signal internal state changes like configuration updates.
252
+ class MyEventAwareProvider
253
+ include OpenFeature::SDK::Provider::EventEmitter
254
+
255
+ def init(evaluation_context)
256
+ # Start background process to monitor for configuration changes
257
+ # Note: SDK automatically emits PROVIDER_READY when init completes successfully
258
+ start_background_process
259
+ end
260
+
261
+ def start_background_process
262
+ Thread.new do
263
+ # Monitor for configuration changes and emit events when they occur
264
+ if configuration_changed?
265
+ emit_event(
266
+ OpenFeature::SDK::ProviderEvent::PROVIDER_CONFIGURATION_CHANGED,
267
+ message: "Flag configuration updated"
268
+ )
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ # Remove specific handlers when no longer needed
275
+ OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY, ready_handler)
276
+ ```
277
+
278
+ ### Shutdown
279
+
280
+ Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/149) to be worked on.
281
+
282
+ <!-- TODO The OpenFeature API provides a close function to perform a cleanup of all registered providers.
283
+ This should only be called when your application is in the process of shutting down.
284
+
285
+ ```ruby
286
+ class MyProvider
287
+ def shutdown
288
+ # Perform any shutdown/reclamation steps with flag management system here
289
+ # Return value is ignored
290
+ end
291
+ end
292
+ ``` -->
293
+
294
+ ### Transaction Context Propagation
295
+
296
+ Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/150) to be worked on.
297
+
298
+ <!-- Transaction context is a container for transaction-specific evaluation context (e.g. user id, user agent, IP).
299
+ Transaction context can be set where specific data is available (e.g. an auth service or request handler) and by using the transaction context propagator it will automatically be applied to all flag evaluations within a transaction (e.g. a request or thread). -->
300
+
301
+ <!-- TODO: code example for global shutdown -->
302
+
303
+ ## Extending
304
+
305
+ ### Develop a provider
306
+
307
+ To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.
308
+ 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.
309
+ You’ll then need to write the provider by implementing the `Provider` duck.
98
310
 
99
311
  ```ruby
100
312
  class MyProvider
101
313
  def init
102
314
  # Perform any initialization steps with flag management system here
103
315
  # Return value is ignored
316
+ # **Note** The OpenFeature spec defines a lifecycle method called `initialize` to be called when a new provider is set.
317
+ # To avoid conflicting with the Ruby `initialize` method, this method should be named `init` when creating a provider.
104
318
  end
105
319
 
106
320
  def shutdown
107
321
  # Perform any shutdown/reclamation steps with flag management system here
108
322
  # Return value is ignored
109
323
  end
324
+
325
+ def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
326
+ # Retrieve a boolean value from provider source
327
+ end
328
+
329
+ def fetch_string_value(flag_key:, default_value:, evaluation_context: nil)
330
+ # Retrieve a string value from provider source
331
+ end
332
+
333
+ def fetch_number_value(flag_key:, default_value:, evaluation_context: nil)
334
+ # Retrieve a numeric value from provider source
335
+ end
336
+
337
+ def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil)
338
+ # Retrieve a integer value from provider source
339
+ end
340
+
341
+ def fetch_float_value(flag_key:, default_value:, evaluation_context: nil)
342
+ # Retrieve a float value from provider source
343
+ end
344
+
345
+ def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
346
+ # Retrieve a hash value from provider source
347
+ end
110
348
  end
111
349
  ```
112
350
 
113
- **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.
351
+ > 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!
352
+
353
+ ### Develop a hook
354
+
355
+ Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/52) to be worked on.
356
+
357
+ <!-- To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
358
+ 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.
359
+ Implement your own hook by conforming to the `Hook interface`.
360
+ To satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined.
361
+ To avoid defining empty functions, make use of the `UnimplementedHook` struct (which already implements all the empty functions). -->
362
+
363
+ <!-- TODO: code example of hook implementation -->
364
+
365
+ <!-- > 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! -->
366
+
367
+ <!-- x-hide-in-docs-start -->
368
+ ## ⭐️ Support the project
369
+
370
+ - Give this repo a ⭐️!
371
+ - Follow us on social media:
372
+ - Twitter: [@openfeature](https://twitter.com/openfeature)
373
+ - LinkedIn: [OpenFeature](https://www.linkedin.com/company/openfeature/)
374
+ - Join us on [Slack](https://cloud-native.slack.com/archives/C0344AANLA1)
375
+ - For more, check out our [community page](https://openfeature.dev/community/)
376
+
377
+ ## 🤝 Contributing
114
378
 
115
- ## Contributing
379
+ Interested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.
116
380
 
117
- See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the OpenFeature project.
381
+ ### Thanks to everyone who has already contributed
118
382
 
119
- Our community meetings are held regularly and open to everyone. Check the [OpenFeature community calendar](https://calendar.google.com/calendar/u/0?cid=MHVhN2kxaGl2NWRoMThiMjd0b2FoNjM2NDRAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) for specific dates and for the Zoom meeting links.
383
+ <a href="https://github.com/open-feature/ruby-sdk/graphs/contributors">
384
+ <img src="https://contrib.rocks/image?repo=open-feature/ruby-sdk" alt="Pictures of the folks who have contributed to the project" />
385
+ </a>
120
386
 
121
- ## License
122
387
 
123
- [Apache License 2.0](LICENSE)
388
+ Made with [contrib.rocks](https://contrib.rocks).
389
+ <!-- x-hide-in-docs-end -->
@@ -32,7 +32,7 @@ module OpenFeature
32
32
  include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1
33
33
  extend Forwardable
34
34
 
35
- def_delegators :configuration, :provider, :set_provider, :hooks, :evaluation_context
35
+ def_delegators :configuration, :provider, :set_provider, :set_provider_and_wait, :hooks, :evaluation_context
36
36
 
37
37
  def configuration
38
38
  @configuration ||= Configuration.new
@@ -51,6 +51,22 @@ module OpenFeature
51
51
  rescue
52
52
  Client.new(provider: Provider::NoOpProvider.new, evaluation_context:)
53
53
  end
54
+
55
+ def add_handler(event_type, handler)
56
+ configuration.add_handler(event_type, handler)
57
+ end
58
+
59
+ def remove_handler(event_type, handler)
60
+ configuration.remove_handler(event_type, handler)
61
+ end
62
+
63
+ def logger
64
+ configuration.logger
65
+ end
66
+
67
+ def logger=(new_logger)
68
+ configuration.logger = new_logger
69
+ end
54
70
  end
55
71
  end
56
72
  end