eppo-server-sdk 3.2.2-arm-linux → 3.2.4-arm-linux
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/README.md +27 -0
- data/lib/eppo_client/3.0/eppo_client.so +0 -0
- data/lib/eppo_client/3.1/eppo_client.so +0 -0
- data/lib/eppo_client/3.2/eppo_client.so +0 -0
- data/lib/eppo_client/3.3/eppo_client.so +0 -0
- data/lib/eppo_client/assignment_logger.rb +16 -0
- data/lib/eppo_client/client.rb +193 -0
- data/lib/eppo_client/config.rb +28 -0
- data/lib/eppo_client/custom_errors.rb +17 -0
- data/lib/eppo_client/validation.rb +14 -0
- data/lib/eppo_client/version.rb +6 -0
- data/lib/eppo_client.rb +15 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21c9f998369dd0eaff97be827e7093399f3376aceec698422c6c411cfde08224
|
4
|
+
data.tar.gz: fd5341173146830de48c03718b2e37233667e887b74f85afc3ee9a2537dd7980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6907bd7a8158343b0e8b0fe896a42040b8543e93b646dc301a1d613165a155cabfdc14c8775032247ba54e9a33fcf792b1be3865f8df4a38b07d38aa37efd18c
|
7
|
+
data.tar.gz: add1bdc2edb2ec7db0406dcde5ce22256cf13dc7f44f9e2367a5530ea84af39e941223d4347bb64a3b424494fd48f876c8fa4ec7c001b5b2e5d16f57c247c4c4
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Eppo SDK for Ruby
|
2
|
+
|
3
|
+
## Getting Started
|
4
|
+
|
5
|
+
Refer to our [SDK documentation](https://docs.geteppo.com/feature-flags/sdks/ruby) for how to install and use the SDK.
|
6
|
+
|
7
|
+
## Supported Ruby Versions
|
8
|
+
This version of the SDK is compatible with Ruby 3.0.6 and above.
|
9
|
+
|
10
|
+
# Contributing
|
11
|
+
|
12
|
+
## Testing with local version of `eppo_core`
|
13
|
+
|
14
|
+
To run build and tests against a local version of `eppo_core`, you should instruct Cargo to look for it at the local path.
|
15
|
+
|
16
|
+
Add the following to `.cargo/config.toml` file (relative to `ruby-sdk`):
|
17
|
+
```toml
|
18
|
+
[patch.crates-io]
|
19
|
+
eppo_core = { path = '../eppo_core' }
|
20
|
+
```
|
21
|
+
|
22
|
+
Make sure you remove the override before updating `Cargo.lock`. Otherwise, the lock file will be missing `eppo_core` checksum and will be unsuitable for release. (CI will warn you if you do this accidentally.)
|
23
|
+
|
24
|
+
## Releasing
|
25
|
+
|
26
|
+
* Bump versions in `ruby-sdk/lib/eppo_client/version.rb` and `ruby-sdk/ext/eppo_client/Cargo.toml`.
|
27
|
+
* Run `cargo update --workspace --verbose` to update `Cargo.lock` and `Gemfile.lock`.
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "custom_errors"
|
4
|
+
|
5
|
+
module EppoClient
|
6
|
+
# The base assignment logger class to override
|
7
|
+
class AssignmentLogger
|
8
|
+
def log_assignment(_assignment_event)
|
9
|
+
raise(EppoClient::AssignmentLoggerError, "log_assignment has not been set up")
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_bandit_action(_assignment_event)
|
13
|
+
raise(EppoClient::AssignmentLoggerError, "log_bandit_action has not been set up")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require "logger"
|
5
|
+
|
6
|
+
require_relative "config"
|
7
|
+
require_relative "eppo_client"
|
8
|
+
|
9
|
+
module EppoClient
|
10
|
+
# The main client singleton
|
11
|
+
class Client
|
12
|
+
include Singleton
|
13
|
+
attr_accessor :assignment_logger
|
14
|
+
|
15
|
+
def init(config)
|
16
|
+
config.validate
|
17
|
+
|
18
|
+
if !@core.nil? then
|
19
|
+
STDERR.puts "Eppo Warning: multiple initialization of the client"
|
20
|
+
@core.shutdown
|
21
|
+
end
|
22
|
+
|
23
|
+
@assignment_logger = config.assignment_logger
|
24
|
+
@core = EppoClient::Core::Client.new(config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def configuration
|
28
|
+
@core.configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
def configuration=(configuration)
|
32
|
+
@core.configuration = configuration
|
33
|
+
end
|
34
|
+
|
35
|
+
def shutdown
|
36
|
+
@core.shutdown
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_string_assignment(flag_key, subject_key, subject_attributes, default_value)
|
40
|
+
get_assignment_inner(flag_key, subject_key, subject_attributes, "STRING", default_value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_numeric_assignment(flag_key, subject_key, subject_attributes, default_value)
|
44
|
+
get_assignment_inner(flag_key, subject_key, subject_attributes, "NUMERIC", default_value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_integer_assignment(flag_key, subject_key, subject_attributes, default_value)
|
48
|
+
get_assignment_inner(flag_key, subject_key, subject_attributes, "INTEGER", default_value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_boolean_assignment(flag_key, subject_key, subject_attributes, default_value)
|
52
|
+
get_assignment_inner(flag_key, subject_key, subject_attributes, "BOOLEAN", default_value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_json_assignment(flag_key, subject_key, subject_attributes, default_value)
|
56
|
+
get_assignment_inner(flag_key, subject_key, subject_attributes, "JSON", default_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_string_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
60
|
+
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "STRING", default_value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_numeric_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
64
|
+
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "NUMERIC", default_value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_integer_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
68
|
+
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "INTEGER", default_value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_boolean_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
72
|
+
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "BOOLEAN", default_value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_json_assignment_details(flag_key, subject_key, subject_attributes, default_value)
|
76
|
+
get_assignment_details_inner(flag_key, subject_key, subject_attributes, "JSON", default_value)
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_bandit_action(flag_key, subject_key, subject_attributes, actions, default_variation)
|
80
|
+
attributes = coerce_context_attributes(subject_attributes)
|
81
|
+
actions = actions.to_h { |action, attributes| [action, coerce_context_attributes(attributes)] }
|
82
|
+
result = @core.get_bandit_action(flag_key, subject_key, attributes, actions, default_variation)
|
83
|
+
|
84
|
+
log_assignment(result[:assignment_event])
|
85
|
+
log_bandit_action(result[:bandit_event])
|
86
|
+
|
87
|
+
return {:variation => result[:variation], :action => result[:action]}
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_bandit_action_details(flag_key, subject_key, subject_attributes, actions, default_variation)
|
91
|
+
attributes = coerce_context_attributes(subject_attributes)
|
92
|
+
actions = actions.to_h { |action, attributes| [action, coerce_context_attributes(attributes)] }
|
93
|
+
result, details = @core.get_bandit_action_details(flag_key, subject_key, attributes, actions, default_variation)
|
94
|
+
|
95
|
+
log_assignment(result[:assignment_event])
|
96
|
+
log_bandit_action(result[:bandit_event])
|
97
|
+
|
98
|
+
return {
|
99
|
+
:variation => result[:variation],
|
100
|
+
:action => result[:action],
|
101
|
+
:evaluationDetails => details
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# rubocop:disable Metrics/MethodLength
|
108
|
+
def get_assignment_inner(flag_key, subject_key, subject_attributes, expected_type, default_value)
|
109
|
+
logger = Logger.new($stdout)
|
110
|
+
begin
|
111
|
+
assignment = @core.get_assignment(flag_key, subject_key, subject_attributes, expected_type)
|
112
|
+
if not assignment then
|
113
|
+
return default_value
|
114
|
+
end
|
115
|
+
|
116
|
+
log_assignment(assignment[:event])
|
117
|
+
|
118
|
+
return assignment[:value][:value]
|
119
|
+
rescue StandardError => error
|
120
|
+
logger.debug("[Eppo SDK] Failed to get assignment: #{error}")
|
121
|
+
|
122
|
+
# TODO: non-graceful mode?
|
123
|
+
default_value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
# rubocop:enable Metrics/MethodLength
|
127
|
+
|
128
|
+
# rubocop:disable Metrics/MethodLength
|
129
|
+
def get_assignment_details_inner(flag_key, subject_key, subject_attributes, expected_type, default_value)
|
130
|
+
result, event = @core.get_assignment_details(flag_key, subject_key, subject_attributes, expected_type)
|
131
|
+
log_assignment(event)
|
132
|
+
|
133
|
+
if not result[:variation] then
|
134
|
+
result[:variation] = default_value
|
135
|
+
else
|
136
|
+
# unwrap from AssignmentValue to untyped value
|
137
|
+
result[:variation] = result[:variation][:value]
|
138
|
+
end
|
139
|
+
|
140
|
+
return result
|
141
|
+
end
|
142
|
+
# rubocop:enable Metrics/MethodLength
|
143
|
+
|
144
|
+
def log_assignment(event)
|
145
|
+
if not event then return end
|
146
|
+
|
147
|
+
# Because rust's AssignmentEvent has a #[flatten] extra_logging
|
148
|
+
# field, serde_magnus serializes it as a normal HashMap with
|
149
|
+
# string keys.
|
150
|
+
#
|
151
|
+
# Convert keys to symbols here, so that logger sees symbol-keyed
|
152
|
+
# events for both flag assignment and bandit actions.
|
153
|
+
event = event.to_h { |key, value| [key.to_sym, value]}
|
154
|
+
|
155
|
+
begin
|
156
|
+
@assignment_logger.log_assignment(event)
|
157
|
+
rescue EppoClient::AssignmentLoggerError
|
158
|
+
# Error means log_assignment was not set up. This is okay to ignore.
|
159
|
+
rescue StandardError => error
|
160
|
+
logger = Logger.new($stdout)
|
161
|
+
logger.error("[Eppo SDK] Error logging assignment event: #{error}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def log_bandit_action(event)
|
166
|
+
if not event then return end
|
167
|
+
|
168
|
+
begin
|
169
|
+
@assignment_logger.log_bandit_action(event)
|
170
|
+
rescue EppoClient::AssignmentLoggerError
|
171
|
+
# Error means log_assignment was not set up. This is okay to ignore.
|
172
|
+
rescue StandardError => error
|
173
|
+
logger = Logger.new($stdout)
|
174
|
+
logger.error("[Eppo SDK] Error logging bandit action event: #{error}")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def coerce_context_attributes(attributes)
|
179
|
+
numeric_attributes = attributes[:numeric_attributes] || attributes["numericAttributes"]
|
180
|
+
categorical_attributes = attributes[:categorical_attributes] || attributes["categoricalAttributes"]
|
181
|
+
if numeric_attributes || categorical_attributes then
|
182
|
+
{
|
183
|
+
numericAttributes: numeric_attributes.to_h do |key, value|
|
184
|
+
value.is_a?(Numeric) ? [key, value] : [nil, nil]
|
185
|
+
end.compact,
|
186
|
+
categoricalAttributes: categorical_attributes.to_h do |key, value|
|
187
|
+
value.nil? ? [nil, nil] : [key, value.to_s]
|
188
|
+
end.compact,
|
189
|
+
}
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "validation"
|
4
|
+
require_relative "assignment_logger"
|
5
|
+
|
6
|
+
module EppoClient
|
7
|
+
# The class for configuring the Eppo client singleton
|
8
|
+
class Config
|
9
|
+
attr_reader :api_key, :assignment_logger, :base_url, :poll_interval_seconds, :poll_jitter_seconds
|
10
|
+
|
11
|
+
def initialize(api_key, assignment_logger: AssignmentLogger.new, base_url: EppoClient::Core::DEFAULT_BASE_URL, poll_interval_seconds: EppoClient::Core::DEFAULT_POLL_INTERVAL_SECONDS, poll_jitter_seconds: EppoClient::Core::DEFAULT_POLL_JITTER_SECONDS, initial_configuration: nil)
|
12
|
+
@api_key = api_key
|
13
|
+
@assignment_logger = assignment_logger
|
14
|
+
@base_url = base_url
|
15
|
+
@poll_interval_seconds = poll_interval_seconds
|
16
|
+
@poll_jitter_seconds = poll_jitter_seconds
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
EppoClient.validate_not_blank("api_key", @api_key)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Hide instance variables (specifically api_key) from logs
|
24
|
+
def inspect
|
25
|
+
"#<EppoClient::Config:#{object_id}>"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EppoClient
|
4
|
+
# A custom error class for AssignmentLogger
|
5
|
+
class AssignmentLoggerError < StandardError
|
6
|
+
def initialize(message)
|
7
|
+
super("AssignmentLoggerError: #{message}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# A custom error class for invalid values
|
12
|
+
class InvalidValueError < StandardError
|
13
|
+
def initialize(message)
|
14
|
+
super("InvalidValueError: #{message}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "custom_errors"
|
4
|
+
|
5
|
+
# The helper module to validate keys
|
6
|
+
module EppoClient
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def validate_not_blank(field_name, field_value)
|
10
|
+
(field_value.nil? || field_value == "") && raise(
|
11
|
+
EppoClient::InvalidValueError, "#{field_name} cannot be blank"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
data/lib/eppo_client.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "eppo_client/client"
|
4
|
+
require_relative "eppo_client/version"
|
5
|
+
|
6
|
+
# EppoClient is the main module for initializing the Eppo client.
|
7
|
+
# It provides a method to initialize the client with a given configuration.
|
8
|
+
module EppoClient
|
9
|
+
def init(config)
|
10
|
+
client = EppoClient::Client.instance
|
11
|
+
client.init(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
module_function :init
|
15
|
+
end
|
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.2.
|
4
|
+
version: 3.2.4
|
5
5
|
platform: arm-linux
|
6
6
|
authors:
|
7
7
|
- Eppo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -17,10 +17,18 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- README.md
|
21
|
+
- lib/eppo_client.rb
|
20
22
|
- lib/eppo_client/3.0/eppo_client.so
|
21
23
|
- lib/eppo_client/3.1/eppo_client.so
|
22
24
|
- lib/eppo_client/3.2/eppo_client.so
|
23
25
|
- lib/eppo_client/3.3/eppo_client.so
|
26
|
+
- lib/eppo_client/assignment_logger.rb
|
27
|
+
- lib/eppo_client/client.rb
|
28
|
+
- lib/eppo_client/config.rb
|
29
|
+
- lib/eppo_client/custom_errors.rb
|
30
|
+
- lib/eppo_client/validation.rb
|
31
|
+
- lib/eppo_client/version.rb
|
24
32
|
homepage: https://github.com/Eppo-exp/ruby-sdk
|
25
33
|
licenses:
|
26
34
|
- MIT
|