openfeature-sdk 0.5.1 → 0.6.1
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 +23 -0
- data/CLAUDE.md +26 -47
- data/Gemfile.lock +58 -43
- data/README.md +112 -26
- data/lib/open_feature/sdk/api.rb +5 -0
- data/lib/open_feature/sdk/client.rb +39 -0
- data/lib/open_feature/sdk/client_metadata.rb +2 -0
- data/lib/open_feature/sdk/configuration.rb +27 -8
- data/lib/open_feature/sdk/evaluation_context.rb +2 -0
- data/lib/open_feature/sdk/evaluation_context_builder.rb +2 -0
- data/lib/open_feature/sdk/evaluation_details.rb +2 -0
- data/lib/open_feature/sdk/hooks/hook_context.rb +8 -0
- data/lib/open_feature/sdk/provider/error_code.rb +2 -0
- data/lib/open_feature/sdk/provider/in_memory_provider.rb +24 -5
- data/lib/open_feature/sdk/provider/provider_metadata.rb +2 -0
- data/lib/open_feature/sdk/provider/reason.rb +2 -0
- data/lib/open_feature/sdk/provider/resolution_details.rb +16 -1
- data/lib/open_feature/sdk/provider.rb +2 -0
- data/lib/open_feature/sdk/provider_state_registry.rb +21 -3
- data/lib/open_feature/sdk/tracking_event_details.rb +23 -0
- data/lib/open_feature/sdk/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac3b2793c1b6453e2525dcc04d7646fd4e5d61a87e6ade8b257754cf9b9c991c
|
|
4
|
+
data.tar.gz: c5aeedfe23851ef99e26024bf1db5f4ba38d7f669b9ea6b3e1e5f63d0693c1a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9aa41845c24248cf6eec381f4531fbfc301196ea748f3b3189333c4879c64a2dc1eda8a935e3514291dcbb5100c1668260e1b87bec837952e1eb1777ae083501
|
|
7
|
+
data.tar.gz: c162a21b0a5b4ee91f132e676440b84cf7862afccd689416f97e9b41f9284bf8b86de5050a1c32a3c98096ec39074cbb0ae1dcbe0d85d4db905d447f3baccd3d
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.1
|
data/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby
|
|
1
|
+
ruby 4.0.1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.1](https://github.com/open-feature/ruby-sdk/compare/v0.6.0...v0.6.1) (2026-03-05)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add flag metadata defaulting and immutability ([#221](https://github.com/open-feature/ruby-sdk/issues/221)) ([a300fc5](https://github.com/open-feature/ruby-sdk/commit/a300fc559293169f22eb1ce26f738cdee664cd26))
|
|
9
|
+
* add hook data per-hook mutable state ([#222](https://github.com/open-feature/ruby-sdk/issues/222)) ([28518a0](https://github.com/open-feature/ruby-sdk/commit/28518a0e08143d167b9d34c86e57a583fe5ee0de))
|
|
10
|
+
* add InMemoryProvider context callbacks and event emission ([#224](https://github.com/open-feature/ruby-sdk/issues/224)) ([0a148f6](https://github.com/open-feature/ruby-sdk/commit/0a148f66abc815fc2ec9fd70027075125dbd504a))
|
|
11
|
+
* add shutdown API, provider status, and status short-circuit ([#223](https://github.com/open-feature/ruby-sdk/issues/223)) ([f9c32ad](https://github.com/open-feature/ruby-sdk/commit/f9c32ad1b467af25697423a542bc568597f39743))
|
|
12
|
+
* implement Tracking API (spec section 6) ([#227](https://github.com/open-feature/ruby-sdk/issues/227)) ([5576fce](https://github.com/open-feature/ruby-sdk/commit/5576fce1c3bcf6e7510d8957c7e40e85c4b83b6f))
|
|
13
|
+
* populate event details payload with error_code and message ([#225](https://github.com/open-feature/ruby-sdk/issues/225)) ([a185003](https://github.com/open-feature/ruby-sdk/commit/a185003dc09a69b2dda1fe569d1f82c45979cdad))
|
|
14
|
+
|
|
15
|
+
## [0.6.0](https://github.com/open-feature/ruby-sdk/compare/v0.5.1...v0.6.0) (2026-03-05)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### ⚠ BREAKING CHANGES
|
|
19
|
+
|
|
20
|
+
* add Ruby 4.0 support, require minimum Ruby 3.4 ([#217](https://github.com/open-feature/ruby-sdk/issues/217))
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* add Ruby 4.0 support, require minimum Ruby 3.4 ([#217](https://github.com/open-feature/ruby-sdk/issues/217)) ([f38ba40](https://github.com/open-feature/ruby-sdk/commit/f38ba40b31beb650ba475c631947fc7969e476fa))
|
|
25
|
+
|
|
3
26
|
## [0.5.1](https://github.com/open-feature/ruby-sdk/compare/v0.5.0...v0.5.1) (2026-03-04)
|
|
4
27
|
|
|
5
28
|
|
data/CLAUDE.md
CHANGED
|
@@ -2,71 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
OpenFeature Ruby
|
|
7
|
+
This is the official OpenFeature SDK for Ruby — an implementation of the [OpenFeature specification](https://openfeature.dev) providing a vendor-agnostic API for feature flag evaluation. Published as the `openfeature-sdk` gem. Requires Ruby >= 3.1.
|
|
8
8
|
|
|
9
9
|
## Commands
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
bundle
|
|
11
|
+
- **Run all tests:** `bundle exec rspec`
|
|
12
|
+
- **Run a single test file:** `bundle exec rspec spec/open_feature/sdk/client_spec.rb`
|
|
13
|
+
- **Run a specific test by line:** `bundle exec rspec spec/open_feature/sdk/client_spec.rb:43`
|
|
14
|
+
- **Lint:** `bundle exec standardrb`
|
|
15
|
+
- **Lint with autofix:** `bundle exec standardrb --fix`
|
|
16
|
+
- **Default rake (tests + lint):** `bundle exec rake`
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
bundle exec rake
|
|
18
|
+
Note: Linting uses [Standard Ruby](https://github.com/standardrb/standard) (configured via the `standard` gem), which enforces double-quoted strings and its own opinionated style. There is no `.rubocop.yml` — Standard manages RuboCop configuration internally. Do not use `bundle exec rubocop` directly as a stale RuboCop server may apply different rules; always use `bundle exec standardrb`.
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
bundle exec rspec
|
|
20
|
-
|
|
21
|
-
# Run a single test file
|
|
22
|
-
bundle exec rspec spec/open_feature/sdk/client_spec.rb
|
|
23
|
-
|
|
24
|
-
# Run a specific test by line number
|
|
25
|
-
bundle exec rspec spec/open_feature/sdk/client_spec.rb:40
|
|
26
|
-
|
|
27
|
-
# Lint (StandardRB with performance plugin)
|
|
28
|
-
bundle exec rake standard
|
|
20
|
+
## Architecture
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
bundle exec standardrb --fix
|
|
32
|
-
```
|
|
22
|
+
### Entry point and API singleton
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
`OpenFeature::SDK` (in `lib/open_feature/sdk.rb`) delegates all method calls to `API.instance` via `method_missing`. `API` is a Singleton that holds a `Configuration` object and builds `Client` instances.
|
|
35
25
|
|
|
36
|
-
|
|
26
|
+
### Provider duck type
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
Providers are not subclasses — they follow a duck type interface. Any object implementing `fetch_boolean_value`, `fetch_string_value`, `fetch_number_value`, `fetch_integer_value`, `fetch_float_value`, and `fetch_object_value` (all accepting `flag_key:`, `default_value:`, `evaluation_context:`) works as a provider. Each method must return a `ResolutionDetails` struct. Two built-in providers exist: `NoOpProvider` (default) and `InMemoryProvider` (for testing). Providers may optionally implement `init(evaluation_context)`, `shutdown`, and `metadata`.
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
- **Configuration** (`lib/open_feature/sdk/configuration.rb`) — Thread-safe provider storage. Handles provider lifecycle (init/shutdown), domain-scoped provider mapping, and event dispatching. Uses Mutex for all shared state.
|
|
42
|
-
- **Client** (`lib/open_feature/sdk/client.rb`) — Flag evaluation interface. Uses `class_eval` metaprogramming to generate 12 typed methods: `fetch_{boolean,string,number,integer,float,object}_value` and `fetch_*_details` variants. Merges evaluation contexts (API + client + invocation).
|
|
43
|
-
- **EvaluationContext** (`lib/open_feature/sdk/evaluation_context.rb`) — Key-value targeting data with a special `targeting_key`. Supports merging with precedence: invocation > client > API.
|
|
30
|
+
### Client dynamic method generation
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
`Client` uses `class_eval` to metaprogram `fetch_<type>_value` and `fetch_<type>_details` methods from `RESULT_TYPE` and `SUFFIXES` arrays. This generates 12 public methods (6 types × 2 suffixes).
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
- **EventEmitter** (`lib/open_feature/sdk/provider/event_emitter.rb`) — Mixin that providers include to emit lifecycle events.
|
|
49
|
-
- **Built-in providers**: `NoOpProvider` (default), `InMemoryProvider` (testing/examples).
|
|
50
|
-
- **Provider states**: `NOT_READY → READY`, with `ERROR`, `FATAL`, `STALE` transitions. Tracked per-instance via `ProviderStateRegistry` using `object_id`.
|
|
51
|
-
- **Initialization modes**: `set_provider` (async, background thread) or `set_provider_and_wait` (sync, raises `ProviderInitializationError` on failure).
|
|
34
|
+
### Evaluation context merging
|
|
52
35
|
|
|
53
|
-
|
|
36
|
+
`EvaluationContextBuilder` merges three layers of context with this precedence: invocation > client > API (global). Context is a hash-like object with a special `targeting_key` field.
|
|
54
37
|
|
|
55
|
-
|
|
56
|
-
- **ProviderEvent** constants: `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_STALE`, `PROVIDER_CONFIGURATION_CHANGED`.
|
|
38
|
+
### Provider eventing
|
|
57
39
|
|
|
58
|
-
|
|
40
|
+
`Configuration` manages provider lifecycle events (READY, ERROR, STALE, CONFIGURATION_CHANGED). Providers can emit spontaneous events by including `Provider::EventEmitter`. Event handlers can be registered at API level (global) or client level (domain-scoped). `ProviderStateRegistry` tracks provider states; `EventDispatcher` manages handler registration and invocation.
|
|
59
41
|
|
|
60
|
-
|
|
61
|
-
- `spec/specification/` — OpenFeature spec compliance tests, organized by requirement number (e.g., "Requirement 1.1.1")
|
|
62
|
-
- `spec/open_feature/` — Unit tests for individual components
|
|
42
|
+
### Domain-based provider binding
|
|
63
43
|
|
|
64
|
-
|
|
44
|
+
Providers can be registered for specific domains. `Configuration#provider(domain:)` resolves domain-specific providers, falling back to the default (nil-domain) provider. Clients are built with an optional `domain:` that binds them to a specific provider.
|
|
65
45
|
|
|
66
46
|
## Conventions
|
|
67
47
|
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
- **Structs for DTOs**: `EvaluationDetails`, `ResolutionDetails`, `ClientMetadata`, `ProviderMetadata` are `Struct`-based
|
|
48
|
+
- All `.rb` files must have `# frozen_string_literal: true` as the first line.
|
|
49
|
+
- Tests live under `spec/` and mirror the `lib/` structure. `spec/specification/` contains tests mapped to OpenFeature spec requirements.
|
|
50
|
+
- Always sign git commits using the `-S` flag.
|
|
51
|
+
- Always include DCO sign-off in commits using the `-s` flag (i.e., `git commit -s -S`). This adds a `Signed-off-by` trailer required by the project's CI.
|
data/Gemfile.lock
CHANGED
|
@@ -1,41 +1,51 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
openfeature-sdk (0.
|
|
4
|
+
openfeature-sdk (0.6.1)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
ast (2.4.
|
|
10
|
-
|
|
9
|
+
ast (2.4.3)
|
|
10
|
+
date (3.5.1)
|
|
11
|
+
debug (1.11.1)
|
|
11
12
|
irb (~> 1.10)
|
|
12
13
|
reline (>= 0.3.8)
|
|
13
|
-
diff-lcs (1.
|
|
14
|
-
docile (1.4.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
diff-lcs (1.6.2)
|
|
15
|
+
docile (1.4.1)
|
|
16
|
+
erb (6.0.2)
|
|
17
|
+
io-console (0.8.2)
|
|
18
|
+
irb (1.17.0)
|
|
19
|
+
pp (>= 0.6.0)
|
|
20
|
+
prism (>= 1.3.0)
|
|
21
|
+
rdoc (>= 4.0.0)
|
|
18
22
|
reline (>= 0.4.2)
|
|
19
|
-
json (2.
|
|
20
|
-
language_server-protocol (3.17.0.
|
|
23
|
+
json (2.18.1)
|
|
24
|
+
language_server-protocol (3.17.0.5)
|
|
21
25
|
lint_roller (1.1.0)
|
|
22
|
-
markly (0.
|
|
23
|
-
parallel (1.
|
|
24
|
-
parser (3.3.
|
|
26
|
+
markly (0.15.2)
|
|
27
|
+
parallel (1.27.0)
|
|
28
|
+
parser (3.3.10.2)
|
|
25
29
|
ast (~> 2.4.1)
|
|
26
30
|
racc
|
|
27
|
-
|
|
31
|
+
pp (0.6.3)
|
|
32
|
+
prettyprint
|
|
33
|
+
prettyprint (0.2.0)
|
|
34
|
+
prism (1.9.0)
|
|
35
|
+
psych (5.3.1)
|
|
36
|
+
date
|
|
28
37
|
stringio
|
|
29
|
-
racc (1.
|
|
38
|
+
racc (1.8.1)
|
|
30
39
|
rainbow (3.1.1)
|
|
31
|
-
rake (13.1
|
|
32
|
-
rdoc (
|
|
40
|
+
rake (13.3.1)
|
|
41
|
+
rdoc (7.2.0)
|
|
42
|
+
erb
|
|
33
43
|
psych (>= 4.0.0)
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
tsort
|
|
45
|
+
regexp_parser (2.11.3)
|
|
46
|
+
reline (0.6.3)
|
|
36
47
|
io-console (~> 0.5)
|
|
37
|
-
rexml (3.
|
|
38
|
-
strscan
|
|
48
|
+
rexml (3.4.4)
|
|
39
49
|
rspec (3.12.0)
|
|
40
50
|
rspec-core (~> 3.12.0)
|
|
41
51
|
rspec-expectations (~> 3.12.0)
|
|
@@ -49,54 +59,59 @@ GEM
|
|
|
49
59
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
50
60
|
rspec-support (~> 3.12.0)
|
|
51
61
|
rspec-support (3.12.2)
|
|
52
|
-
rubocop (1.
|
|
62
|
+
rubocop (1.84.2)
|
|
53
63
|
json (~> 2.3)
|
|
54
|
-
language_server-protocol (
|
|
64
|
+
language_server-protocol (~> 3.17.0.2)
|
|
65
|
+
lint_roller (~> 1.1.0)
|
|
55
66
|
parallel (~> 1.10)
|
|
56
67
|
parser (>= 3.3.0.2)
|
|
57
68
|
rainbow (>= 2.2.2, < 4.0)
|
|
58
|
-
regexp_parser (>=
|
|
59
|
-
|
|
60
|
-
rubocop-ast (>= 1.31.1, < 2.0)
|
|
69
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
70
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
61
71
|
ruby-progressbar (~> 1.7)
|
|
62
|
-
unicode-display_width (>= 2.4.0, <
|
|
63
|
-
rubocop-ast (1.
|
|
64
|
-
parser (>= 3.3.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
73
|
+
rubocop-ast (1.49.0)
|
|
74
|
+
parser (>= 3.3.7.2)
|
|
75
|
+
prism (~> 1.7)
|
|
76
|
+
rubocop-performance (1.26.1)
|
|
77
|
+
lint_roller (~> 1.1)
|
|
78
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
79
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
68
80
|
ruby-progressbar (1.13.0)
|
|
69
81
|
simplecov (0.22.0)
|
|
70
82
|
docile (~> 1.1)
|
|
71
83
|
simplecov-html (~> 0.11)
|
|
72
84
|
simplecov_json_formatter (~> 0.1)
|
|
73
|
-
simplecov-cobertura (
|
|
85
|
+
simplecov-cobertura (3.1.0)
|
|
74
86
|
rexml
|
|
75
87
|
simplecov (~> 0.19)
|
|
76
|
-
simplecov-html (0.
|
|
88
|
+
simplecov-html (0.13.2)
|
|
77
89
|
simplecov_json_formatter (0.1.4)
|
|
78
|
-
standard (1.
|
|
90
|
+
standard (1.54.0)
|
|
79
91
|
language_server-protocol (~> 3.17.0.2)
|
|
80
92
|
lint_roller (~> 1.0)
|
|
81
|
-
rubocop (~> 1.
|
|
93
|
+
rubocop (~> 1.84.0)
|
|
82
94
|
standard-custom (~> 1.0.0)
|
|
83
|
-
standard-performance (~> 1.
|
|
95
|
+
standard-performance (~> 1.8)
|
|
84
96
|
standard-custom (1.0.2)
|
|
85
97
|
lint_roller (~> 1.0)
|
|
86
98
|
rubocop (~> 1.50)
|
|
87
|
-
standard-performance (1.
|
|
99
|
+
standard-performance (1.9.0)
|
|
88
100
|
lint_roller (~> 1.1)
|
|
89
|
-
rubocop-performance (~> 1.
|
|
90
|
-
stringio (3.
|
|
91
|
-
strscan (3.1.0)
|
|
101
|
+
rubocop-performance (~> 1.26.0)
|
|
102
|
+
stringio (3.2.0)
|
|
92
103
|
timecop (0.9.10)
|
|
93
|
-
|
|
104
|
+
tsort (0.2.0)
|
|
105
|
+
unicode-display_width (3.2.0)
|
|
106
|
+
unicode-emoji (~> 4.1)
|
|
107
|
+
unicode-emoji (4.2.0)
|
|
94
108
|
|
|
95
109
|
PLATFORMS
|
|
96
110
|
arm64-darwin-21
|
|
97
111
|
arm64-darwin-22
|
|
98
112
|
arm64-darwin-23
|
|
99
113
|
arm64-darwin-24
|
|
114
|
+
arm64-darwin-25
|
|
100
115
|
x64-mingw-ucrt
|
|
101
116
|
x64-mingw32
|
|
102
117
|
x86_64-darwin-19
|
|
@@ -111,7 +126,7 @@ DEPENDENCIES
|
|
|
111
126
|
rake (~> 13.0)
|
|
112
127
|
rspec (~> 3.12.0)
|
|
113
128
|
simplecov (~> 0.22.0)
|
|
114
|
-
simplecov-cobertura (~>
|
|
129
|
+
simplecov-cobertura (~> 3.0)
|
|
115
130
|
standard
|
|
116
131
|
standard-performance
|
|
117
132
|
timecop (~> 0.9.10)
|
data/README.md
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
</a>
|
|
18
18
|
<!-- x-release-please-start-version -->
|
|
19
19
|
|
|
20
|
-
<a href="https://github.com/open-feature/ruby-sdk/releases/tag/v0.
|
|
21
|
-
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.
|
|
20
|
+
<a href="https://github.com/open-feature/ruby-sdk/releases/tag/v0.6.1">
|
|
21
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.6.1&color=blue&style=for-the-badge" />
|
|
22
22
|
</a>
|
|
23
23
|
|
|
24
24
|
<!-- x-release-please-end -->
|
|
@@ -38,9 +38,8 @@
|
|
|
38
38
|
|
|
39
39
|
| Supported Ruby Version | OS |
|
|
40
40
|
| ------------ | --------------------- |
|
|
41
|
-
| Ruby 3.
|
|
42
|
-
| Ruby
|
|
43
|
-
| Ruby 3.3.0 | Windows, MacOS, Linux |
|
|
41
|
+
| Ruby 3.4.x | Windows, MacOS, Linux |
|
|
42
|
+
| Ruby 4.0.x | Windows, MacOS, Linux |
|
|
44
43
|
|
|
45
44
|
### Install
|
|
46
45
|
|
|
@@ -101,13 +100,14 @@ object = client.fetch_object_value(flag_key: 'object_value', default_value: { na
|
|
|
101
100
|
| ------ | --------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
102
101
|
| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
|
|
103
102
|
| ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
|
|
104
|
-
|
|
|
103
|
+
| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
|
|
105
104
|
| ❌ | [Logging](#logging) | Integrate with popular logging packages. |
|
|
106
105
|
| ✅ | [Domains](#domains) | Logically bind clients with providers. |
|
|
107
106
|
| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
|
|
108
|
-
|
|
|
107
|
+
| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
|
|
108
|
+
| ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations for experimentation. |
|
|
109
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
|
-
|
|
|
110
|
+
| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
|
|
111
111
|
|
|
112
112
|
<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
|
|
113
113
|
|
|
@@ -201,15 +201,49 @@ bool_value = client.fetch_boolean_value(
|
|
|
201
201
|
|
|
202
202
|
### Hooks
|
|
203
203
|
|
|
204
|
-
|
|
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.
|
|
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.
|
|
207
205
|
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
206
|
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
207
|
|
|
210
|
-
|
|
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
|
|
211
239
|
|
|
212
|
-
|
|
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
|
+
```
|
|
213
247
|
|
|
214
248
|
### Logging
|
|
215
249
|
|
|
@@ -277,19 +311,53 @@ OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_READY,
|
|
|
277
311
|
|
|
278
312
|
### Shutdown
|
|
279
313
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
<!-- 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.
|
|
283
315
|
This should only be called when your application is in the process of shutting down.
|
|
284
316
|
|
|
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
|
+
|
|
285
324
|
```ruby
|
|
286
325
|
class MyProvider
|
|
287
326
|
def shutdown
|
|
288
327
|
# Perform any shutdown/reclamation steps with flag management system here
|
|
289
|
-
# Return value is ignored
|
|
290
328
|
end
|
|
291
329
|
end
|
|
292
|
-
```
|
|
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.
|
|
293
361
|
|
|
294
362
|
### Transaction Context Propagation
|
|
295
363
|
|
|
@@ -345,6 +413,12 @@ class MyProvider
|
|
|
345
413
|
def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
|
|
346
414
|
# Retrieve a hash value from provider source
|
|
347
415
|
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
|
|
348
422
|
end
|
|
349
423
|
```
|
|
350
424
|
|
|
@@ -352,17 +426,29 @@ end
|
|
|
352
426
|
|
|
353
427
|
### Develop a hook
|
|
354
428
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
<!-- 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.
|
|
358
430
|
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
|
|
360
|
-
|
|
361
|
-
|
|
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
|
|
362
437
|
|
|
363
|
-
|
|
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
|
+
```
|
|
364
450
|
|
|
365
|
-
|
|
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!
|
|
366
452
|
|
|
367
453
|
<!-- x-hide-in-docs-start -->
|
|
368
454
|
## ⭐️ Support the project
|
data/lib/open_feature/sdk/api.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative "evaluation_details"
|
|
|
10
10
|
require_relative "client_metadata"
|
|
11
11
|
require_relative "hooks"
|
|
12
12
|
require_relative "client"
|
|
13
|
+
require_relative "tracking_event_details"
|
|
13
14
|
require_relative "provider"
|
|
14
15
|
|
|
15
16
|
module OpenFeature
|
|
@@ -68,6 +69,10 @@ module OpenFeature
|
|
|
68
69
|
def logger=(new_logger)
|
|
69
70
|
configuration.logger = new_logger
|
|
70
71
|
end
|
|
72
|
+
|
|
73
|
+
def shutdown
|
|
74
|
+
configuration.shutdown
|
|
75
|
+
end
|
|
71
76
|
end
|
|
72
77
|
end
|
|
73
78
|
end
|
|
@@ -28,6 +28,10 @@ module OpenFeature
|
|
|
28
28
|
@hooks = []
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def provider_status
|
|
32
|
+
OpenFeature::SDK.configuration.provider_state(@provider)
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
def add_handler(event_type, handler = nil, &block)
|
|
32
36
|
actual_handler = handler || block
|
|
33
37
|
OpenFeature::SDK.configuration.add_client_handler(self, event_type, actual_handler)
|
|
@@ -38,6 +42,22 @@ module OpenFeature
|
|
|
38
42
|
OpenFeature::SDK.configuration.remove_client_handler(self, event_type, actual_handler)
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
# Tracking API (spec 6.1.1.1) — dynamic-context paradigm
|
|
46
|
+
#
|
|
47
|
+
# Records a tracking event. If the provider does not implement
|
|
48
|
+
# tracking, this is a no-op (spec 6.1.4).
|
|
49
|
+
def track(tracking_event_name, evaluation_context: nil, tracking_event_details: nil)
|
|
50
|
+
return unless @provider.respond_to?(:track)
|
|
51
|
+
|
|
52
|
+
built_context = EvaluationContextBuilder.new.call(
|
|
53
|
+
api_context: OpenFeature::SDK.evaluation_context,
|
|
54
|
+
client_context: self.evaluation_context,
|
|
55
|
+
invocation_context: evaluation_context
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@provider.track(tracking_event_name, evaluation_context: built_context, tracking_event_details: tracking_event_details)
|
|
59
|
+
end
|
|
60
|
+
|
|
41
61
|
RESULT_TYPE.each do |result_type|
|
|
42
62
|
SUFFIXES.each do |suffix|
|
|
43
63
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
@@ -54,6 +74,18 @@ module OpenFeature
|
|
|
54
74
|
def fetch_details(type:, flag_key:, default_value:, evaluation_context: nil, invocation_hooks: [], hook_hints: nil)
|
|
55
75
|
validate_default_value_type(type, default_value)
|
|
56
76
|
|
|
77
|
+
if OpenFeature::SDK.configuration.provider_tracked?(@provider)
|
|
78
|
+
error_code = short_circuit_error_code(provider_status)
|
|
79
|
+
if error_code
|
|
80
|
+
resolution = Provider::ResolutionDetails.new(
|
|
81
|
+
value: default_value,
|
|
82
|
+
error_code: error_code,
|
|
83
|
+
reason: Provider::Reason::ERROR
|
|
84
|
+
)
|
|
85
|
+
return EvaluationDetails.new(flag_key: flag_key, resolution_details: resolution)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
57
89
|
built_context = EvaluationContextBuilder.new.call(
|
|
58
90
|
api_context: OpenFeature::SDK.evaluation_context,
|
|
59
91
|
client_context: self.evaluation_context,
|
|
@@ -109,6 +141,13 @@ module OpenFeature
|
|
|
109
141
|
EvaluationDetails.new(flag_key: flag_key, resolution_details: resolution_details)
|
|
110
142
|
end
|
|
111
143
|
|
|
144
|
+
def short_circuit_error_code(state)
|
|
145
|
+
case state
|
|
146
|
+
when ProviderState::NOT_READY then Provider::ErrorCode::PROVIDER_NOT_READY
|
|
147
|
+
when ProviderState::FATAL then Provider::ErrorCode::PROVIDER_FATAL
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
112
151
|
def validate_default_value_type(type, default_value)
|
|
113
152
|
expected_classes = TYPE_CLASS_MAP[type]
|
|
114
153
|
unless expected_classes.any? { |klass| default_value.is_a?(klass) }
|
|
@@ -76,6 +76,26 @@ module OpenFeature
|
|
|
76
76
|
set_provider_internal(provider, domain: domain, wait_for_init: true)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
def provider_state(provider)
|
|
80
|
+
@provider_state_registry.get_state(provider)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def provider_tracked?(provider)
|
|
84
|
+
@provider_state_registry.tracked?(provider)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def shutdown
|
|
88
|
+
providers_to_shutdown = @provider_mutex.synchronize { @providers.values.uniq }
|
|
89
|
+
|
|
90
|
+
providers_to_shutdown.each do |prov|
|
|
91
|
+
prov.shutdown if prov.respond_to?(:shutdown)
|
|
92
|
+
rescue => e
|
|
93
|
+
@logger&.warn("Error shutting down provider #{prov&.class&.name || "unknown"}: #{e.message}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
reset
|
|
97
|
+
end
|
|
98
|
+
|
|
79
99
|
private
|
|
80
100
|
|
|
81
101
|
def reset
|
|
@@ -171,10 +191,6 @@ module OpenFeature
|
|
|
171
191
|
run_handlers_for_provider(provider, event_type, event_details)
|
|
172
192
|
end
|
|
173
193
|
|
|
174
|
-
def provider_state(provider)
|
|
175
|
-
@provider_state_registry.get_state(provider)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
194
|
private
|
|
179
195
|
|
|
180
196
|
def extract_provider_name(provider)
|
|
@@ -219,8 +235,7 @@ module OpenFeature
|
|
|
219
235
|
provider_state = @provider_state_registry.get_state(provider)
|
|
220
236
|
|
|
221
237
|
if event_type == status_to_event[provider_state]
|
|
222
|
-
|
|
223
|
-
event_details = {provider_name: provider_name}
|
|
238
|
+
event_details = build_event_details(provider)
|
|
224
239
|
|
|
225
240
|
begin
|
|
226
241
|
handler.call(event_details)
|
|
@@ -237,8 +252,7 @@ module OpenFeature
|
|
|
237
252
|
provider_state = @provider_state_registry.get_state(client_provider)
|
|
238
253
|
|
|
239
254
|
if event_type == status_to_event[provider_state]
|
|
240
|
-
|
|
241
|
-
event_details = {provider_name: provider_name}
|
|
255
|
+
event_details = build_event_details(client_provider)
|
|
242
256
|
|
|
243
257
|
begin
|
|
244
258
|
handler.call(event_details)
|
|
@@ -248,6 +262,11 @@ module OpenFeature
|
|
|
248
262
|
end
|
|
249
263
|
end
|
|
250
264
|
end
|
|
265
|
+
|
|
266
|
+
def build_event_details(provider)
|
|
267
|
+
stored_details = @provider_state_registry.get_details(provider)
|
|
268
|
+
{provider_name: extract_provider_name(provider)}.merge(stored_details)
|
|
269
|
+
end
|
|
251
270
|
end
|
|
252
271
|
end
|
|
253
272
|
end
|
|
@@ -22,6 +22,14 @@ module OpenFeature
|
|
|
22
22
|
@evaluation_context = evaluation_context
|
|
23
23
|
@client_metadata = client_metadata
|
|
24
24
|
@provider_metadata = provider_metadata
|
|
25
|
+
@hook_data = {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns a mutable hash scoped to the given hook instance.
|
|
29
|
+
# The same hash is returned across all hook stages (before, after, error, finally),
|
|
30
|
+
# allowing hooks to share state across their lifecycle (spec 4.1.5, 4.6.1).
|
|
31
|
+
def hook_data_for(hook)
|
|
32
|
+
@hook_data[hook.object_id] ||= {}
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module OpenFeature
|
|
2
4
|
module SDK
|
|
3
5
|
module Provider
|
|
4
|
-
# TODO: Add evaluation context support
|
|
5
6
|
class InMemoryProvider
|
|
7
|
+
include Provider::EventEmitter
|
|
8
|
+
|
|
6
9
|
NAME = "In-memory Provider"
|
|
7
10
|
|
|
8
11
|
attr_reader :metadata
|
|
@@ -22,7 +25,12 @@ module OpenFeature
|
|
|
22
25
|
|
|
23
26
|
def add_flag(flag_key:, value:)
|
|
24
27
|
flags[flag_key] = value
|
|
25
|
-
|
|
28
|
+
emit_provider_changed([flag_key])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update_flags(new_flags)
|
|
32
|
+
@flags = new_flags.dup
|
|
33
|
+
emit_provider_changed(new_flags.keys)
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
|
|
@@ -54,13 +62,24 @@ module OpenFeature
|
|
|
54
62
|
attr_reader :flags
|
|
55
63
|
|
|
56
64
|
def fetch_value(flag_key:, default_value:, evaluation_context:)
|
|
57
|
-
|
|
65
|
+
raw_value = flags[flag_key]
|
|
58
66
|
|
|
59
|
-
if
|
|
67
|
+
if raw_value.nil?
|
|
60
68
|
return ResolutionDetails.new(value: default_value, error_code: ErrorCode::FLAG_NOT_FOUND, reason: Reason::ERROR)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
|
-
|
|
71
|
+
if raw_value.respond_to?(:call)
|
|
72
|
+
value = raw_value.call(evaluation_context)
|
|
73
|
+
ResolutionDetails.new(value: value, reason: Reason::TARGETING_MATCH)
|
|
74
|
+
else
|
|
75
|
+
ResolutionDetails.new(value: raw_value, reason: Reason::STATIC)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def emit_provider_changed(flag_keys)
|
|
80
|
+
return unless configuration_attached?
|
|
81
|
+
|
|
82
|
+
emit_event(ProviderEvent::PROVIDER_CONFIGURATION_CHANGED, flags_changed: flag_keys)
|
|
64
83
|
end
|
|
65
84
|
end
|
|
66
85
|
end
|
|
@@ -1,7 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module OpenFeature
|
|
2
4
|
module SDK
|
|
3
5
|
module Provider
|
|
4
|
-
|
|
6
|
+
EMPTY_FLAG_METADATA = {}.freeze
|
|
7
|
+
|
|
8
|
+
ResolutionDetails = Struct.new(:value, :reason, :variant, :error_code, :error_message, :flag_metadata, keyword_init: true) do
|
|
9
|
+
def flag_metadata
|
|
10
|
+
raw = self[:flag_metadata]
|
|
11
|
+
if raw.nil?
|
|
12
|
+
EMPTY_FLAG_METADATA
|
|
13
|
+
elsif raw.frozen?
|
|
14
|
+
raw
|
|
15
|
+
else
|
|
16
|
+
raw.dup.freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
5
20
|
end
|
|
6
21
|
end
|
|
7
22
|
end
|
|
@@ -17,7 +17,7 @@ module OpenFeature
|
|
|
17
17
|
return unless provider
|
|
18
18
|
|
|
19
19
|
@mutex.synchronize do
|
|
20
|
-
@states[provider.object_id] = state
|
|
20
|
+
@states[provider.object_id] = {state: state, details: {}}
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ module OpenFeature
|
|
|
29
29
|
# Only update state if the event should cause a state change
|
|
30
30
|
if new_state
|
|
31
31
|
@mutex.synchronize do
|
|
32
|
-
@states[provider.object_id] = new_state
|
|
32
|
+
@states[provider.object_id] = {state: new_state, details: event_details || {}}
|
|
33
33
|
end
|
|
34
34
|
new_state
|
|
35
35
|
else
|
|
@@ -42,7 +42,17 @@ module OpenFeature
|
|
|
42
42
|
return ProviderState::NOT_READY unless provider
|
|
43
43
|
|
|
44
44
|
@mutex.synchronize do
|
|
45
|
-
@states[provider.object_id]
|
|
45
|
+
entry = @states[provider.object_id]
|
|
46
|
+
entry ? entry[:state] : ProviderState::NOT_READY
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_details(provider)
|
|
51
|
+
return {} unless provider
|
|
52
|
+
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
entry = @states[provider.object_id]
|
|
55
|
+
entry ? entry[:details] : {}
|
|
46
56
|
end
|
|
47
57
|
end
|
|
48
58
|
|
|
@@ -54,6 +64,14 @@ module OpenFeature
|
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
|
|
67
|
+
def tracked?(provider)
|
|
68
|
+
return false unless provider
|
|
69
|
+
|
|
70
|
+
@mutex.synchronize do
|
|
71
|
+
@states.key?(provider.object_id)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
57
75
|
def ready?(provider)
|
|
58
76
|
get_state(provider) == ProviderState::READY
|
|
59
77
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenFeature
|
|
4
|
+
module SDK
|
|
5
|
+
# Represents tracking event details per spec section 6.2.
|
|
6
|
+
#
|
|
7
|
+
# Requirement 6.2.1: MUST define an optional numeric value.
|
|
8
|
+
# Requirement 6.2.2: MUST support custom fields (string keys,
|
|
9
|
+
# boolean/string/number/structure values).
|
|
10
|
+
class TrackingEventDetails
|
|
11
|
+
attr_reader :value, :fields
|
|
12
|
+
|
|
13
|
+
def initialize(value: nil, **fields)
|
|
14
|
+
if !value.nil? && !value.is_a?(Numeric)
|
|
15
|
+
raise ArgumentError, "Tracking event value must be Numeric, got #{value.class}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@value = value
|
|
19
|
+
@fields = fields.transform_keys(&:to_s)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openfeature-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenFeature Authors
|
|
@@ -113,14 +113,14 @@ dependencies:
|
|
|
113
113
|
requirements:
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version:
|
|
116
|
+
version: '3.0'
|
|
117
117
|
type: :development
|
|
118
118
|
prerelease: false
|
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
120
|
requirements:
|
|
121
121
|
- - "~>"
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
|
-
version:
|
|
123
|
+
version: '3.0'
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
125
|
name: timecop
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -185,6 +185,7 @@ files:
|
|
|
185
185
|
- lib/open_feature/sdk/provider_initialization_error.rb
|
|
186
186
|
- lib/open_feature/sdk/provider_state.rb
|
|
187
187
|
- lib/open_feature/sdk/provider_state_registry.rb
|
|
188
|
+
- lib/open_feature/sdk/tracking_event_details.rb
|
|
188
189
|
- lib/open_feature/sdk/version.rb
|
|
189
190
|
- release-please-config.json
|
|
190
191
|
- renovate.json
|
|
@@ -204,14 +205,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
204
205
|
requirements:
|
|
205
206
|
- - ">="
|
|
206
207
|
- !ruby/object:Gem::Version
|
|
207
|
-
version: '3.
|
|
208
|
+
version: '3.4'
|
|
208
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
210
|
requirements:
|
|
210
211
|
- - ">="
|
|
211
212
|
- !ruby/object:Gem::Version
|
|
212
213
|
version: '0'
|
|
213
214
|
requirements: []
|
|
214
|
-
rubygems_version:
|
|
215
|
+
rubygems_version: 4.0.3
|
|
215
216
|
specification_version: 4
|
|
216
217
|
summary: OpenFeature SDK for Ruby
|
|
217
218
|
test_files: []
|