eppo-server-sdk 3.5.1 → 3.7.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/Cargo.lock +10 -9
- data/ext/eppo_client/Cargo.toml +3 -2
- data/ext/eppo_client/src/client.rs +27 -0
- data/ext/eppo_client/src/lib.rs +4 -0
- data/lib/eppo_client/client.rb +162 -3
- data/lib/eppo_client/version.rb +1 -1
- data/lib/eppo_client.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed54b4025fdfa3a664fc17bfa16f370680b206f1f59322f73008b1ac747b8f1c
|
4
|
+
data.tar.gz: 50c0af964af522fa426435d740e4f545706cdf308007475fd11b6ee016bc61f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b00e011ec1ab5875f802db649f0205d06989a2e4f1a9acadc31afd014e32825ab1868f1f64791c5bd9e1c85bc27afca5580a6d752be0836e114a2b3a0fd4ef3b
|
7
|
+
data.tar.gz: 59977570627cfbc0897a16897657fe66560bb9af0a62d20af9e1a5896d558d8cb189ece6a8cbe5d5fe0ceded6652f86b4fccebf9a63c4c67c632266dafdee76d
|
data/Cargo.lock
CHANGED
@@ -335,7 +335,7 @@ dependencies = [
|
|
335
335
|
|
336
336
|
[[package]]
|
337
337
|
name = "eppo_client"
|
338
|
-
version = "3.
|
338
|
+
version = "3.7.0"
|
339
339
|
dependencies = [
|
340
340
|
"env_logger",
|
341
341
|
"eppo_core",
|
@@ -345,13 +345,14 @@ dependencies = [
|
|
345
345
|
"serde",
|
346
346
|
"serde_json",
|
347
347
|
"serde_magnus",
|
348
|
+
"tokio",
|
348
349
|
]
|
349
350
|
|
350
351
|
[[package]]
|
351
352
|
name = "eppo_core"
|
352
|
-
version = "9.
|
353
|
+
version = "9.2.0"
|
353
354
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
354
|
-
checksum = "
|
355
|
+
checksum = "9221e1c149196a97d292a5b6f14cdc87ef54b2c5f83334df3c24a3b0780a6ac7"
|
355
356
|
dependencies = [
|
356
357
|
"base64",
|
357
358
|
"chrono",
|
@@ -889,9 +890,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|
889
890
|
|
890
891
|
[[package]]
|
891
892
|
name = "libc"
|
892
|
-
version = "0.2.
|
893
|
+
version = "0.2.171"
|
893
894
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
894
|
-
checksum = "
|
895
|
+
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
895
896
|
|
896
897
|
[[package]]
|
897
898
|
name = "libloading"
|
@@ -1785,9 +1786,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|
1785
1786
|
|
1786
1787
|
[[package]]
|
1787
1788
|
name = "tokio"
|
1788
|
-
version = "1.
|
1789
|
+
version = "1.44.1"
|
1789
1790
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1790
|
-
checksum = "
|
1791
|
+
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
|
1791
1792
|
dependencies = [
|
1792
1793
|
"backtrace",
|
1793
1794
|
"bytes",
|
@@ -1801,9 +1802,9 @@ dependencies = [
|
|
1801
1802
|
|
1802
1803
|
[[package]]
|
1803
1804
|
name = "tokio-macros"
|
1804
|
-
version = "2.
|
1805
|
+
version = "2.5.0"
|
1805
1806
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1806
|
-
checksum = "
|
1807
|
+
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
1807
1808
|
dependencies = [
|
1808
1809
|
"proc-macro2",
|
1809
1810
|
"quote",
|
data/ext/eppo_client/Cargo.toml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
[package]
|
2
2
|
name = "eppo_client"
|
3
3
|
# TODO: this version and lib/eppo_client/version.rb should be in sync
|
4
|
-
version = "3.
|
4
|
+
version = "3.7.0"
|
5
5
|
edition = "2021"
|
6
6
|
license = "MIT"
|
7
7
|
publish = false
|
@@ -12,10 +12,11 @@ crate-type = ["cdylib"]
|
|
12
12
|
|
13
13
|
[dependencies]
|
14
14
|
env_logger = { version = "0.11.3", features = ["unstable-kv"] }
|
15
|
-
eppo_core = { version = "=9.
|
15
|
+
eppo_core = { version = "=9.2.0", features = ["magnus", "event_ingestion"] }
|
16
16
|
log = { version = "0.4.21", features = ["kv_serde"] }
|
17
17
|
magnus = { version = "0.7.1" }
|
18
18
|
serde = { version = "1.0.203", features = ["derive"] }
|
19
19
|
serde_magnus = "0.9.0"
|
20
20
|
rb-sys = "0.9.102"
|
21
21
|
serde_json = "1.0.128"
|
22
|
+
tokio = { version = "1.44.1", default-features = false, features = ["time"] }
|
@@ -238,6 +238,33 @@ impl Client {
|
|
238
238
|
serde_magnus::serialize(&result)
|
239
239
|
}
|
240
240
|
|
241
|
+
pub fn wait_for_initialization(&self, timeout_secs: f64) {
|
242
|
+
log::info!(target: "eppo", "waiting for initialization");
|
243
|
+
let thread = self.background_thread.borrow();
|
244
|
+
let Some(thread) = thread.as_ref() else {
|
245
|
+
log::warn!(target: "eppo", "failed to wait for initialization: background thread is not running");
|
246
|
+
return;
|
247
|
+
};
|
248
|
+
let Some(poller) = &self.configuration_poller else {
|
249
|
+
log::warn!(target: "eppo", "failed to wait for initialization: configuration poller has not been started");
|
250
|
+
return;
|
251
|
+
};
|
252
|
+
|
253
|
+
let _ = thread
|
254
|
+
.runtime()
|
255
|
+
.async_runtime
|
256
|
+
.block_on(async {
|
257
|
+
tokio::time::timeout(
|
258
|
+
Duration::from_secs_f64(timeout_secs),
|
259
|
+
poller.wait_for_configuration(),
|
260
|
+
)
|
261
|
+
.await
|
262
|
+
})
|
263
|
+
.inspect_err(|err| {
|
264
|
+
log::warn!(target: "eppo", "failed to wait for initialization: {err:?}");
|
265
|
+
});
|
266
|
+
}
|
267
|
+
|
241
268
|
pub fn get_configuration(&self) -> Option<Configuration> {
|
242
269
|
self.configuration_store
|
243
270
|
.get_configuration()
|
data/ext/eppo_client/src/lib.rs
CHANGED
@@ -29,6 +29,10 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
29
29
|
method!(Client::get_bandit_action_details, 5),
|
30
30
|
)?;
|
31
31
|
core_client.define_method("track", method!(Client::track, 2))?;
|
32
|
+
core_client.define_method(
|
33
|
+
"wait_for_initialization",
|
34
|
+
method!(Client::wait_for_initialization, 1),
|
35
|
+
)?;
|
32
36
|
core_client.define_method("configuration", method!(Client::get_configuration, 0))?;
|
33
37
|
core_client.define_method("configuration=", method!(Client::set_configuration, 1))?;
|
34
38
|
core_client.define_method("shutdown", method!(Client::shutdown, 0))?;
|
data/lib/eppo_client/client.rb
CHANGED
@@ -31,64 +31,210 @@ module EppoClient
|
|
31
31
|
@core = EppoClient::Core::Client.new(config)
|
32
32
|
end
|
33
33
|
|
34
|
+
##
|
35
|
+
# Waits for client to fetch configuration and get ready to serve
|
36
|
+
# assignments.
|
37
|
+
#
|
38
|
+
# This method blocks the current thread until configuration is
|
39
|
+
# successfully fetched or +timeout+ seconds passed.
|
40
|
+
#
|
41
|
+
# Note: this method returns immediately if configuration poller
|
42
|
+
# has been disabled.
|
43
|
+
#
|
44
|
+
# @param timeout [Numeric] Maximum time to wait in seconds
|
45
|
+
# @return [nil]
|
46
|
+
def wait_for_initialization(timeout=1)
|
47
|
+
return unless @core
|
48
|
+
@core.wait_for_initialization(timeout)
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns the currently active configuration.
|
34
53
|
def configuration
|
35
54
|
@core.configuration
|
36
55
|
end
|
37
56
|
|
57
|
+
##
|
58
|
+
# Sets the currently active configuration.
|
38
59
|
def configuration=(configuration)
|
39
60
|
@core.configuration = configuration
|
40
61
|
end
|
41
62
|
|
63
|
+
##
|
64
|
+
# Prepare the client for shutdown.
|
65
|
+
#
|
66
|
+
# This method stops the configuration poller and any other background threads.
|
67
|
+
#
|
68
|
+
# @return [nil]
|
42
69
|
def shutdown
|
43
70
|
@core.shutdown
|
44
71
|
end
|
45
72
|
|
46
|
-
|
47
|
-
#
|
73
|
+
##
|
74
|
+
# Tracks an arbitrary event. Events must have a type and a payload.
|
75
|
+
#
|
76
|
+
# @note This method is considered unstable and may change in future versions.
|
77
|
+
#
|
78
|
+
# @param event_type [String] The type of the event to track.
|
79
|
+
# @param payload [Hash] The payload of the event to track.
|
48
80
|
def unstable_track(event_type, payload)
|
49
81
|
@core.track(event_type, payload)
|
50
82
|
end
|
51
83
|
|
84
|
+
##
|
85
|
+
# Returns a string assignment for the given flag key and subject.
|
86
|
+
#
|
87
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
88
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
89
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
90
|
+
# @param default_value [String] The default value to return if the flag is not found or no assignment can be made.
|
91
|
+
# @return [String] The assignment for the given flag key and subject.
|
52
92
|
def get_string_assignment(flag_key, subject_key, subject_attributes, default_value)
|
53
93
|
get_assignment_inner(flag_key, subject_key, subject_attributes, "STRING", default_value)
|
54
94
|
end
|
55
95
|
|
96
|
+
##
|
97
|
+
# Returns a numeric assignment for the given flag key and subject.
|
98
|
+
#
|
99
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
100
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
101
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
102
|
+
# @param default_value [Numeric] The default value to return if the flag is not found or no assignment can be made.
|
103
|
+
# @return [Numeric] The assignment for the given flag key and subject.
|
56
104
|
def get_numeric_assignment(flag_key, subject_key, subject_attributes, default_value)
|
57
105
|
get_assignment_inner(flag_key, subject_key, subject_attributes, "NUMERIC", default_value)
|
58
106
|
end
|
59
107
|
|
108
|
+
##
|
109
|
+
# Returns an integer assignment for the given flag key and subject.
|
110
|
+
#
|
111
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
112
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
113
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
114
|
+
# @param default_value [Integer] The default value to return if the flag is not found or no assignment can be made.
|
115
|
+
# @return [Integer] The assignment for the given flag key and subject.
|
60
116
|
def get_integer_assignment(flag_key, subject_key, subject_attributes, default_value)
|
61
117
|
get_assignment_inner(flag_key, subject_key, subject_attributes, "INTEGER", default_value)
|
62
118
|
end
|
63
119
|
|
120
|
+
##
|
121
|
+
# Returns a boolean assignment for the given flag key and subject.
|
122
|
+
#
|
123
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
124
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
125
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
126
|
+
# @param default_value [Boolean] The default value to return if the flag is not found or no assignment can be made.
|
127
|
+
# @return [Boolean] The assignment for the given flag key and subject.
|
64
128
|
def get_boolean_assignment(flag_key, subject_key, subject_attributes, default_value)
|
65
129
|
get_assignment_inner(flag_key, subject_key, subject_attributes, "BOOLEAN", default_value)
|
66
130
|
end
|
67
131
|
|
132
|
+
##
|
133
|
+
# Returns a JSON assignment for the given flag key and subject.
|
134
|
+
#
|
135
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
136
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
137
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
138
|
+
# @param default_value [Hash] The default value to return if the flag is not found or no assignment can be made.
|
139
|
+
# @return [Hash] The assignment for the given flag key and subject.
|
68
140
|
def get_json_assignment(flag_key, subject_key, subject_attributes, default_value)
|
69
141
|
get_assignment_inner(flag_key, subject_key, subject_attributes, "JSON", default_value)
|
70
142
|
end
|
71
143
|
|
144
|
+
##
|
145
|
+
# Returns detailed information about a string assignment for the given flag key and subject.
|
146
|
+
#
|
147
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
148
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
149
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
150
|
+
# unstable and may change between SDK versions.
|
151
|
+
#
|
152
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
153
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
154
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
155
|
+
# @param default_value [String] The default value to return if the flag is not found or no assignment can be made.
|
156
|
+
# @return [Hash] A hash containing {:variation => assigned_value, :action => nil, :evaluationDetails => {detailed_evaluation_info}}
|
72
157
|
def get_string_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
73
158
|
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "STRING", default_value)
|
74
159
|
end
|
75
160
|
|
161
|
+
##
|
162
|
+
# Returns detailed information about a numeric assignment for the given flag key and subject.
|
163
|
+
#
|
164
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
165
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
166
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
167
|
+
# unstable.
|
168
|
+
#
|
169
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
170
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
171
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
172
|
+
# @param default_value [Numeric] The default value to return if the flag is not found or no assignment can be made.
|
173
|
+
# @return [Hash] A hash containing {:variation => assigned_value, :action => nil, :evaluationDetails => {detailed_evaluation_info}}
|
76
174
|
def get_numeric_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
77
175
|
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "NUMERIC", default_value)
|
78
176
|
end
|
79
177
|
|
178
|
+
##
|
179
|
+
# Returns detailed information about an integer assignment for the given flag key and subject.
|
180
|
+
#
|
181
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
182
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
183
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
184
|
+
# unstable and may change between SDK versions.
|
185
|
+
#
|
186
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
187
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
188
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
189
|
+
# @param default_value [Integer] The default value to return if the flag is not found or no assignment can be made.
|
190
|
+
# @return [Hash] A hash containing {:variation => assigned_value, :action => nil, :evaluationDetails => {detailed_evaluation_info}}
|
80
191
|
def get_integer_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
81
192
|
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "INTEGER", default_value)
|
82
193
|
end
|
83
194
|
|
195
|
+
##
|
196
|
+
# Returns detailed information about a boolean assignment for the given flag key and subject.
|
197
|
+
#
|
198
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
199
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
200
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
201
|
+
# unstable and may change between SDK versions.
|
202
|
+
#
|
203
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
204
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
205
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
206
|
+
# @param default_value [Boolean] The default value to return if the flag is not found or no assignment can be made.
|
207
|
+
# @return [Hash] A hash containing {:variation => assigned_value, :action => nil, :evaluationDetails => {detailed_evaluation_info}}
|
84
208
|
def get_boolean_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
85
209
|
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "BOOLEAN", default_value)
|
86
210
|
end
|
87
211
|
|
212
|
+
##
|
213
|
+
# Returns detailed information about a JSON assignment for the given flag key and subject.
|
214
|
+
#
|
215
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
216
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
217
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
218
|
+
# unstable and may change between SDK versions.
|
219
|
+
#
|
220
|
+
# @param flag_key [String] The key of the flag to get an assignment for.
|
221
|
+
# @param subject_key [String] The key of the subject to get an assignment for.
|
222
|
+
# @param subject_attributes [Hash] The attributes of the subject to get an assignment for.
|
223
|
+
# @param default_value [Hash] The default value to return if the flag is not found or no assignment can be made.
|
224
|
+
# @return [Hash] A hash containing {:variation => assigned_value, :action => nil, :evaluationDetails => {detailed_evaluation_info}}
|
88
225
|
def get_json_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
89
226
|
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "JSON", default_value)
|
90
227
|
end
|
91
228
|
|
229
|
+
##
|
230
|
+
# Returns a bandit action based on the flag key, subject, and available actions.
|
231
|
+
#
|
232
|
+
# @param flag_key [String] The key of the flag to get an action for.
|
233
|
+
# @param subject_key [String] The key of the subject to get an action for.
|
234
|
+
# @param subject_attributes [Hash] The attributes of the subject.
|
235
|
+
# @param actions [Hash] A map of available actions and their attributes.
|
236
|
+
# @param default_variation [String] The default variation to return if no assignment can be made.
|
237
|
+
# @return [Hash] A hash containing the assigned variation and action.
|
92
238
|
def get_bandit_action(flag_key, subject_key, subject_attributes, actions, default_variation)
|
93
239
|
attributes = coerce_context_attributes(subject_attributes)
|
94
240
|
actions = actions.to_h { |action, attributes| [action, coerce_context_attributes(attributes)] }
|
@@ -103,6 +249,20 @@ module EppoClient
|
|
103
249
|
}
|
104
250
|
end
|
105
251
|
|
252
|
+
##
|
253
|
+
# Returns detailed information about a bandit action based on the flag key, subject, and available actions.
|
254
|
+
#
|
255
|
+
# @note This method is intended for debugging purposes and is discouraged from use in
|
256
|
+
# production. It is a couple of times slower than the non-detail methods. The evaluation
|
257
|
+
# details format is primarily designed for human consumption (debugging) and is therefore
|
258
|
+
# unstable and may change between SDK versions.
|
259
|
+
#
|
260
|
+
# @param flag_key [String] The key of the flag to get an action for.
|
261
|
+
# @param subject_key [String] The key of the subject to get an action for.
|
262
|
+
# @param subject_attributes [Hash] The attributes of the subject.
|
263
|
+
# @param actions [Hash] A map of available actions and their attributes.
|
264
|
+
# @param default_variation [String] The default variation to return if no assignment can be made.
|
265
|
+
# @return [Hash] A hash containing {:variation => assigned_variation, :action => assigned_action, :evaluationDetails => {detailed_evaluation_info}}
|
106
266
|
def get_bandit_action_details(flag_key, subject_key, subject_attributes, actions, default_variation)
|
107
267
|
attributes = coerce_context_attributes(subject_attributes)
|
108
268
|
actions = actions.to_h { |action, attributes| [action, coerce_context_attributes(attributes)] }
|
@@ -120,7 +280,6 @@ module EppoClient
|
|
120
280
|
|
121
281
|
private
|
122
282
|
|
123
|
-
# rubocop:disable Metrics/MethodLength
|
124
283
|
def get_assignment_inner(flag_key, subject_key, subject_attributes, expected_type, default_value)
|
125
284
|
logger = Logger.new($stdout)
|
126
285
|
begin
|
data/lib/eppo_client/version.rb
CHANGED
data/lib/eppo_client.rb
CHANGED
@@ -6,6 +6,14 @@ require_relative "eppo_client/version"
|
|
6
6
|
# EppoClient is the main module for initializing the Eppo client.
|
7
7
|
# It provides a method to initialize the client with a given configuration.
|
8
8
|
module EppoClient
|
9
|
+
##
|
10
|
+
# Initializes the Eppo client singleton.
|
11
|
+
#
|
12
|
+
# @note The client returned by this method may still be in the process of initializing.
|
13
|
+
# Use the `#wait_for_initialization` method to wait for the client to be ready.
|
14
|
+
#
|
15
|
+
# @param config [EppoClient::Config] The configuration for the client.
|
16
|
+
# @return [EppoClient::Client] The client.
|
9
17
|
def init(config)
|
10
18
|
client = EppoClient::Client.instance
|
11
19
|
client.init(config)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eppo-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eppo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rb_sys
|