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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/.ruby-version +1 -1
- data/.tool-versions +1 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +98 -0
- data/Gemfile.lock +7 -2
- data/LICENSE +1 -1
- data/README.md +306 -40
- data/lib/open_feature/sdk/api.rb +17 -1
- data/lib/open_feature/sdk/client.rb +47 -4
- data/lib/open_feature/sdk/configuration.rb +218 -6
- data/lib/open_feature/sdk/event_dispatcher.rb +80 -0
- data/lib/open_feature/sdk/provider/event_emitter.rb +37 -0
- data/lib/open_feature/sdk/provider/in_memory_provider.rb +9 -13
- data/lib/open_feature/sdk/provider.rb +11 -0
- data/lib/open_feature/sdk/provider_event.rb +25 -0
- data/lib/open_feature/sdk/provider_initialization_error.rb +33 -0
- data/lib/open_feature/sdk/provider_state.rb +27 -0
- data/lib/open_feature/sdk/provider_state_registry.rb +99 -0
- data/lib/open_feature/sdk/version.rb +1 -1
- data/release-please-config.json +5 -1
- data/renovate.json +4 -12
- metadata +25 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c91c19e8a0f7ef628c048224287a276713d704914142d4a2771bd74bf01ea68e
|
|
4
|
+
data.tar.gz: 565d980320e40d5a573c4710d6313f038c0a58350f6d5eb1564dcd5c85148853
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d5dd24ccd3b4e61de24d7a46d02169ad7373d74776664fc0686db4d51dde98c8851c81e392043130f08a4dfd6cdb877a31c3a5ecb2d42c7bd2f5c92b1008fef
|
|
7
|
+
data.tar.gz: f8ed5ee65b6bee9dd1e36e1373248b12f3729a5ce5a22857c4212957246f7c3b150d48d5cea6e59a7e164d06e69bb53a5d90fc883924c85aaa4f6d64b15e4764
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.4.8
|
data/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby 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
|
+
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.
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
202
|
+
### Hooks
|
|
90
203
|
|
|
91
|
-
|
|
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
|
-
|
|
214
|
+
### Logging
|
|
94
215
|
|
|
95
|
-
|
|
216
|
+
Coming Soon! [Issue available](https://github.com/open-feature/ruby-sdk/issues/148) to work on.
|
|
96
217
|
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
381
|
+
### Thanks to everyone who has already contributed
|
|
118
382
|
|
|
119
|
-
|
|
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
|
-
|
|
388
|
+
Made with [contrib.rocks](https://contrib.rocks).
|
|
389
|
+
<!-- x-hide-in-docs-end -->
|
data/lib/open_feature/sdk/api.rb
CHANGED
|
@@ -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
|