eppo-server-sdk 0.1.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/client.rb +153 -17
- data/lib/configuration_requestor.rb +22 -11
- data/lib/eppo_client.rb +13 -6
- data/lib/rules.rb +4 -3
- data/lib/shard.rb +2 -2
- data/lib/validation.rb +5 -3
- data/lib/variation_type.rb +39 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fd8f3093395da766b70f3e7cba84b6fc712f58c05092b3594ab53cb0d0f827d
|
4
|
+
data.tar.gz: f773c3f04448a9a63c2e495c07099d952df7579b87472f9cf281e22d69698127
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdf91e318a7a4ef6e8eeb90650b94f6ce712ca58b35fdf99e08e60a71c79ef91c769e0798e709c1b59da8c060bffcb765ff8e9afc06d58330ceab2155d71d26a
|
7
|
+
data.tar.gz: 22f8876c55dd41194a25bd9b31174f3cb84e8a760507afe72f5eb8a9f9859e8d32d4938b1a6c82147fafd011bcfd1df1f848bea818bd9d927bbd2051db657dd5
|
data/lib/client.rb
CHANGED
@@ -8,10 +8,13 @@ require 'custom_errors'
|
|
8
8
|
require 'rules'
|
9
9
|
require 'shard'
|
10
10
|
require 'validation'
|
11
|
+
require 'variation_type'
|
11
12
|
|
12
13
|
module EppoClient
|
13
14
|
# The main client singleton
|
15
|
+
# rubocop:disable Metrics/ClassLength
|
14
16
|
class Client
|
17
|
+
extend Gem::Deprecate
|
15
18
|
include Singleton
|
16
19
|
attr_accessor :config_requestor, :assignment_logger, :poller
|
17
20
|
|
@@ -19,24 +22,123 @@ module EppoClient
|
|
19
22
|
Client.instance
|
20
23
|
end
|
21
24
|
|
22
|
-
|
25
|
+
def get_string_assignment(
|
26
|
+
subject_key,
|
27
|
+
flag_key,
|
28
|
+
subject_attributes = {},
|
29
|
+
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
30
|
+
)
|
31
|
+
logger = Logger.new($stdout)
|
32
|
+
logger.level = log_level
|
33
|
+
assigned_variation = get_assignment_variation(
|
34
|
+
subject_key, flag_key, subject_attributes,
|
35
|
+
EppoClient::VariationType::STRING_TYPE, logger
|
36
|
+
)
|
37
|
+
assigned_variation&.typed_value
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_numeric_assignment(
|
41
|
+
subject_key,
|
42
|
+
flag_key,
|
43
|
+
subject_attributes = {},
|
44
|
+
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
45
|
+
)
|
46
|
+
logger = Logger.new($stdout)
|
47
|
+
logger.level = log_level
|
48
|
+
assigned_variation = get_assignment_variation(
|
49
|
+
subject_key, flag_key, subject_attributes,
|
50
|
+
EppoClient::VariationType::NUMERIC_TYPE, logger
|
51
|
+
)
|
52
|
+
assigned_variation&.typed_value
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_boolean_assignment(
|
56
|
+
subject_key,
|
57
|
+
flag_key,
|
58
|
+
subject_attributes = {},
|
59
|
+
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
60
|
+
)
|
61
|
+
logger = Logger.new($stdout)
|
62
|
+
logger.level = log_level
|
63
|
+
assigned_variation = get_assignment_variation(
|
64
|
+
subject_key, flag_key, subject_attributes,
|
65
|
+
EppoClient::VariationType::BOOLEAN_TYPE, logger
|
66
|
+
)
|
67
|
+
assigned_variation&.typed_value
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_parsed_json_assignment(
|
71
|
+
subject_key,
|
72
|
+
flag_key,
|
73
|
+
subject_attributes = {},
|
74
|
+
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
75
|
+
)
|
76
|
+
logger = Logger.new($stdout)
|
77
|
+
logger.level = log_level
|
78
|
+
assigned_variation = get_assignment_variation(
|
79
|
+
subject_key, flag_key, subject_attributes,
|
80
|
+
EppoClient::VariationType::JSON_TYPE, logger
|
81
|
+
)
|
82
|
+
assigned_variation&.typed_value
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_json_string_assignment(
|
86
|
+
subject_key,
|
87
|
+
flag_key,
|
88
|
+
subject_attributes = {},
|
89
|
+
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
90
|
+
)
|
91
|
+
logger = Logger.new($stdout)
|
92
|
+
logger.level = log_level
|
93
|
+
assigned_variation = get_assignment_variation(
|
94
|
+
subject_key, flag_key, subject_attributes,
|
95
|
+
EppoClient::VariationType::JSON_TYPE, logger
|
96
|
+
)
|
97
|
+
assigned_variation&.value
|
98
|
+
end
|
99
|
+
|
23
100
|
def get_assignment(
|
24
101
|
subject_key,
|
25
|
-
|
102
|
+
flag_key,
|
26
103
|
subject_attributes = {},
|
27
104
|
log_level = EppoClient::DEFAULT_LOGGER_LEVEL
|
28
105
|
)
|
29
106
|
logger = Logger.new($stdout)
|
30
107
|
logger.level = log_level
|
108
|
+
assigned_variation = get_assignment_variation(subject_key, flag_key,
|
109
|
+
subject_attributes, nil,
|
110
|
+
logger)
|
111
|
+
assigned_variation&.value
|
112
|
+
end
|
113
|
+
deprecate :get_assignment, 'the get_<typed>_assignment methods', 2024, 1
|
114
|
+
|
115
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
116
|
+
def get_assignment_variation(
|
117
|
+
subject_key,
|
118
|
+
flag_key,
|
119
|
+
subject_attributes,
|
120
|
+
expected_variation_type,
|
121
|
+
logger
|
122
|
+
)
|
31
123
|
EppoClient.validate_not_blank('subject_key', subject_key)
|
32
|
-
EppoClient.validate_not_blank('
|
33
|
-
experiment_config = @config_requestor.get_configuration(
|
124
|
+
EppoClient.validate_not_blank('flag_key', flag_key)
|
125
|
+
experiment_config = @config_requestor.get_configuration(flag_key)
|
34
126
|
override = get_subject_variation_override(experiment_config, subject_key)
|
35
|
-
|
127
|
+
unless override.nil?
|
128
|
+
unless expected_variation_type.nil?
|
129
|
+
variation_is_expected_type =
|
130
|
+
EppoClient::VariationType.expected_type?(
|
131
|
+
override, expected_variation_type
|
132
|
+
)
|
133
|
+
return nil unless variation_is_expected_type
|
134
|
+
end
|
135
|
+
return override
|
136
|
+
end
|
36
137
|
|
37
138
|
if experiment_config.nil? || experiment_config.enabled == false
|
38
139
|
logger.debug(
|
39
|
-
|
140
|
+
'[Eppo SDK] No assigned variation. No active experiment or flag for '\
|
141
|
+
"key: #{flag_key}"
|
40
142
|
)
|
41
143
|
return nil
|
42
144
|
end
|
@@ -44,7 +146,8 @@ module EppoClient
|
|
44
146
|
matched_rule = EppoClient.find_matching_rule(subject_attributes, experiment_config.rules)
|
45
147
|
if matched_rule.nil?
|
46
148
|
logger.debug(
|
47
|
-
|
149
|
+
'[Eppo SDK] No assigned variation. Subject attributes do not match '\
|
150
|
+
"targeting rules: #{subject_attributes}"
|
48
151
|
)
|
49
152
|
return nil
|
50
153
|
end
|
@@ -52,22 +155,41 @@ module EppoClient
|
|
52
155
|
allocation = experiment_config.allocations[matched_rule.allocation_key]
|
53
156
|
unless in_experiment_sample?(
|
54
157
|
subject_key,
|
55
|
-
|
158
|
+
flag_key,
|
56
159
|
experiment_config.subject_shards,
|
57
160
|
allocation.percent_exposure
|
58
161
|
)
|
59
162
|
logger.debug(
|
60
|
-
'[Eppo SDK] No assigned variation. Subject is not part of experiment
|
163
|
+
'[Eppo SDK] No assigned variation. Subject is not part of experiment'\
|
164
|
+
' sample population'
|
61
165
|
)
|
62
166
|
return nil
|
63
167
|
end
|
64
168
|
|
65
|
-
shard = EppoClient.get_shard(
|
66
|
-
|
169
|
+
shard = EppoClient.get_shard(
|
170
|
+
"assignment-#{subject_key}-#{flag_key}",
|
171
|
+
experiment_config.subject_shards
|
172
|
+
)
|
173
|
+
assigned_variation = allocation.variations.find do |var|
|
174
|
+
var.shard_range.shard_in_range?(shard)
|
175
|
+
end
|
176
|
+
|
177
|
+
assigned_variation_value_to_log = nil
|
178
|
+
unless assigned_variation.nil?
|
179
|
+
assigned_variation_value_to_log = assigned_variation.value
|
180
|
+
unless expected_variation_type.nil?
|
181
|
+
variation_is_expected_type = EppoClient::VariationType.expected_type?(
|
182
|
+
assigned_variation, expected_variation_type
|
183
|
+
)
|
184
|
+
return nil unless variation_is_expected_type
|
185
|
+
end
|
186
|
+
end
|
67
187
|
|
68
188
|
assignment_event = {
|
69
|
-
"
|
70
|
-
"
|
189
|
+
"allocation": matched_rule.allocation_key,
|
190
|
+
"experiment": "#{flag_key}-#{matched_rule.allocation_key}",
|
191
|
+
"featureFlag": flag_key,
|
192
|
+
"variation": assigned_variation_value_to_log,
|
71
193
|
"subject": subject_key,
|
72
194
|
"timestamp": Time.now.utc.iso8601,
|
73
195
|
"subjectAttributes": subject_attributes
|
@@ -76,7 +198,7 @@ module EppoClient
|
|
76
198
|
begin
|
77
199
|
@assignment_logger.log_assignment(assignment_event)
|
78
200
|
rescue EppoClient::AssignmentLoggerError => e
|
79
|
-
#
|
201
|
+
# Error means log_assignment was not set up. This is okay to ignore.
|
80
202
|
rescue StandardError => e
|
81
203
|
logger.error("[Eppo SDK] Error logging assignment event: #{e}")
|
82
204
|
end
|
@@ -89,14 +211,28 @@ module EppoClient
|
|
89
211
|
@poller.stop
|
90
212
|
end
|
91
213
|
|
214
|
+
# rubocop:disable Metrics/MethodLength
|
92
215
|
def get_subject_variation_override(experiment_config, subject)
|
93
216
|
subject_hash = Digest::MD5.hexdigest(subject.to_s)
|
94
|
-
experiment_config&.overrides &&
|
217
|
+
if experiment_config&.overrides &&
|
218
|
+
experiment_config.overrides[subject_hash] &&
|
219
|
+
experiment_config.typed_overrides[subject_hash]
|
220
|
+
EppoClient::VariationDto.new(
|
221
|
+
'override',
|
222
|
+
experiment_config.overrides[subject_hash],
|
223
|
+
experiment_config.typed_overrides[subject_hash],
|
224
|
+
EppoClient::ShardRange.new(0, 1000)
|
225
|
+
)
|
226
|
+
end
|
95
227
|
end
|
228
|
+
# rubocop:enable Metrics/MethodLength
|
96
229
|
|
97
|
-
def in_experiment_sample?(subject, experiment_key, subject_shards,
|
98
|
-
|
230
|
+
def in_experiment_sample?(subject, experiment_key, subject_shards,
|
231
|
+
percent_exposure)
|
232
|
+
shard = EppoClient.get_shard("exposure-#{subject}-#{experiment_key}",
|
233
|
+
subject_shards)
|
99
234
|
shard <= percent_exposure * subject_shards
|
100
235
|
end
|
101
236
|
end
|
237
|
+
# rubocop:enable Metrics/ClassLength
|
102
238
|
end
|
@@ -6,11 +6,12 @@ require 'constants'
|
|
6
6
|
module EppoClient
|
7
7
|
# A class for the variation object
|
8
8
|
class VariationDto
|
9
|
-
attr_reader :name, :value, :shard_range
|
9
|
+
attr_reader :name, :value, :typed_value, :shard_range
|
10
10
|
|
11
|
-
def initialize(name, value, shard_range)
|
11
|
+
def initialize(name, value, typed_value, shard_range)
|
12
12
|
@name = name
|
13
13
|
@value = value
|
14
|
+
@typed_value = typed_value
|
14
15
|
@shard_range = shard_range
|
15
16
|
end
|
16
17
|
end
|
@@ -27,13 +28,15 @@ module EppoClient
|
|
27
28
|
|
28
29
|
# A class for the experiment configuration object
|
29
30
|
class ExperimentConfigurationDto
|
30
|
-
attr_reader :subject_shards, :enabled, :name, :overrides,
|
31
|
+
attr_reader :subject_shards, :enabled, :name, :overrides,
|
32
|
+
:typed_overrides, :rules, :allocations
|
31
33
|
|
32
34
|
def initialize(exp_config)
|
33
35
|
@subject_shards = exp_config['subjectShards']
|
34
36
|
@enabled = exp_config['enabled']
|
35
37
|
@name = exp_config['name'] || nil
|
36
38
|
@overrides = exp_config['overrides'] || {}
|
39
|
+
@typed_overrides = exp_config['typedOverrides'] || {}
|
37
40
|
@rules = exp_config['rules'] || []
|
38
41
|
@allocations = exp_config['allocations']
|
39
42
|
end
|
@@ -41,7 +44,6 @@ module EppoClient
|
|
41
44
|
|
42
45
|
# A class for getting exp configs from the local cache or API
|
43
46
|
class ExperimentConfigurationRequestor
|
44
|
-
|
45
47
|
attr_reader :config_store
|
46
48
|
|
47
49
|
def initialize(http_client, config_store)
|
@@ -50,7 +52,8 @@ module EppoClient
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def get_configuration(experiment_key)
|
53
|
-
@http_client.is_unauthorized && raise(EppoClient::UnauthorizedError,
|
55
|
+
@http_client.is_unauthorized && raise(EppoClient::UnauthorizedError,
|
56
|
+
'please check your API key')
|
54
57
|
@config_store.retrieve_configuration(experiment_key)
|
55
58
|
end
|
56
59
|
|
@@ -58,16 +61,19 @@ module EppoClient
|
|
58
61
|
def fetch_and_store_configurations
|
59
62
|
configs = {}
|
60
63
|
begin
|
61
|
-
exp_configs = @http_client.get(EppoClient::RAC_ENDPOINT).fetch(
|
64
|
+
exp_configs = @http_client.get(EppoClient::RAC_ENDPOINT).fetch(
|
65
|
+
'flags', {}
|
66
|
+
)
|
67
|
+
# rubocop: disable Metrics/BlockLength
|
62
68
|
exp_configs.each do |exp_key, exp_config|
|
63
69
|
exp_config['allocations'].each do |k, v|
|
64
70
|
exp_config['allocations'][k] = EppoClient::AllocationDto.new(
|
65
71
|
v['percentExposure'],
|
66
72
|
v['variations'].map do |var|
|
67
73
|
EppoClient::VariationDto.new(
|
68
|
-
var['name'],
|
69
|
-
var['
|
70
|
-
|
74
|
+
var['name'], var['value'], var['typedValue'],
|
75
|
+
EppoClient::ShardRange.new(var['shardRange']['start'],
|
76
|
+
var['shardRange']['end'])
|
71
77
|
)
|
72
78
|
end
|
73
79
|
)
|
@@ -84,11 +90,16 @@ module EppoClient
|
|
84
90
|
allocation_key: rule['allocationKey']
|
85
91
|
)
|
86
92
|
end
|
87
|
-
configs[exp_key] = EppoClient::ExperimentConfigurationDto.new(
|
93
|
+
configs[exp_key] = EppoClient::ExperimentConfigurationDto.new(
|
94
|
+
exp_config
|
95
|
+
)
|
88
96
|
end
|
97
|
+
# rubocop: enable Metrics/BlockLength
|
89
98
|
@config_store.assign_configurations(configs)
|
90
99
|
rescue EppoClient::HttpRequestError => e
|
91
|
-
Logger.new($stdout).error(
|
100
|
+
Logger.new($stdout).error(
|
101
|
+
"Error retrieving assignment configurations: #{e}"
|
102
|
+
)
|
92
103
|
end
|
93
104
|
configs
|
94
105
|
end
|
data/lib/eppo_client.rb
CHANGED
@@ -29,21 +29,28 @@ module EppoClient
|
|
29
29
|
end
|
30
30
|
# rubocop:enable Metrics/MethodLength
|
31
31
|
|
32
|
-
# rubocop:disable Metrics/MethodLength
|
32
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
33
33
|
def init(config)
|
34
34
|
config.validate
|
35
|
-
sdk_version = ParseGemspec::Specification.load(
|
35
|
+
sdk_version = ParseGemspec::Specification.load(
|
36
|
+
'eppo-server-sdk.gemspec'
|
37
|
+
).to_hash_object[:version]
|
36
38
|
sdk_params = EppoClient::SdkParams.new(config.api_key, 'ruby', sdk_version)
|
37
|
-
http_client = EppoClient::HttpClient.new(config.base_url,
|
38
|
-
|
39
|
+
http_client = EppoClient::HttpClient.new(config.base_url,
|
40
|
+
sdk_params.formatted)
|
41
|
+
config_store = EppoClient::ConfigurationStore.new(
|
42
|
+
EppoClient::MAX_CACHE_ENTRIES
|
43
|
+
)
|
39
44
|
config_store.lock.with_write_lock do
|
40
45
|
EppoClient.initialize_client(
|
41
|
-
EppoClient::ExperimentConfigurationRequestor.new(
|
46
|
+
EppoClient::ExperimentConfigurationRequestor.new(
|
47
|
+
http_client, config_store
|
48
|
+
),
|
42
49
|
config.assignment_logger
|
43
50
|
)
|
44
51
|
end
|
45
52
|
end
|
46
|
-
# rubocop:enable Metrics/MethodLength
|
53
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
47
54
|
|
48
55
|
module_function :init, :initialize_client
|
49
56
|
end
|
data/lib/rules.rb
CHANGED
@@ -47,7 +47,7 @@ module EppoClient
|
|
47
47
|
true
|
48
48
|
end
|
49
49
|
|
50
|
-
# rubocop:disable Metrics/AbcSize, Metrics/
|
50
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
51
51
|
def evaluate_condition(subject_attributes, condition)
|
52
52
|
subject_value = subject_attributes[condition.attribute]
|
53
53
|
return false if subject_value.nil?
|
@@ -63,7 +63,7 @@ module EppoClient
|
|
63
63
|
subject_value.is_a?(Numeric) && evaluate_numeric_condition(subject_value, condition)
|
64
64
|
end
|
65
65
|
end
|
66
|
-
# rubocop:enable Metrics/AbcSize, Metrics/
|
66
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
67
67
|
|
68
68
|
# rubocop:disable Metrics/MethodLength
|
69
69
|
def evaluate_numeric_condition(subject_value, condition)
|
@@ -82,5 +82,6 @@ module EppoClient
|
|
82
82
|
end
|
83
83
|
# rubocop:enable Metrics/MethodLength
|
84
84
|
|
85
|
-
module_function :find_matching_rule, :matches_rule, :evaluate_condition,
|
85
|
+
module_function :find_matching_rule, :matches_rule, :evaluate_condition,
|
86
|
+
:evaluate_numeric_condition
|
86
87
|
end
|
data/lib/shard.rb
CHANGED
@@ -18,6 +18,8 @@ module EppoClient
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
module_function
|
22
|
+
|
21
23
|
def get_shard(input, subject_shards)
|
22
24
|
hash_output = Digest::MD5.hexdigest(input)
|
23
25
|
# get the first 4 bytes of the md5 hex string and parse it using base 16
|
@@ -25,6 +27,4 @@ module EppoClient
|
|
25
27
|
int_from_hash = hash_output[0...8].to_i(16)
|
26
28
|
int_from_hash % subject_shards
|
27
29
|
end
|
28
|
-
|
29
|
-
module_function :get_shard
|
30
30
|
end
|
data/lib/validation.rb
CHANGED
@@ -4,9 +4,11 @@ require 'custom_errors'
|
|
4
4
|
|
5
5
|
# The helper module to validate keys
|
6
6
|
module EppoClient
|
7
|
+
module_function
|
8
|
+
|
7
9
|
def validate_not_blank(field_name, field_value)
|
8
|
-
(field_value.nil? || field_value == '') && raise(
|
10
|
+
(field_value.nil? || field_value == '') && raise(
|
11
|
+
EppoClient::InvalidValueError, "#{field_name} cannot be blank"
|
12
|
+
)
|
9
13
|
end
|
10
|
-
|
11
|
-
module_function :validate_not_blank
|
12
14
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module EppoClient
|
6
|
+
# The class for configuring the Eppo client singleton
|
7
|
+
module VariationType
|
8
|
+
STRING_TYPE = 'string'
|
9
|
+
NUMERIC_TYPE = 'numeric'
|
10
|
+
BOOLEAN_TYPE = 'boolean'
|
11
|
+
JSON_TYPE = 'json'
|
12
|
+
|
13
|
+
module_function
|
14
|
+
|
15
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
16
|
+
def expected_type?(assigned_variation, expected_variation_type)
|
17
|
+
case expected_variation_type
|
18
|
+
when STRING_TYPE
|
19
|
+
assigned_variation.typed_value.is_a?(String)
|
20
|
+
when NUMERIC_TYPE
|
21
|
+
assigned_variation.typed_value.is_a?(Numeric)
|
22
|
+
when BOOLEAN_TYPE
|
23
|
+
assigned_variation.typed_value.is_a?(TrueClass) ||
|
24
|
+
assigned_variation.typed_value.is_a?(FalseClass)
|
25
|
+
when JSON_TYPE
|
26
|
+
begin
|
27
|
+
parsed_json = JSON.parse(assigned_variation.value)
|
28
|
+
JSON.dump(assigned_variation.typed_value)
|
29
|
+
parsed_json == assigned_variation.typed_value
|
30
|
+
rescue JSON::JSONError
|
31
|
+
false
|
32
|
+
end
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
38
|
+
end
|
39
|
+
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: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eppo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -136,14 +136,14 @@ dependencies:
|
|
136
136
|
requirements:
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version:
|
139
|
+
version: 0.82.0
|
140
140
|
type: :development
|
141
141
|
prerelease: false
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version:
|
146
|
+
version: 0.82.0
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
148
|
name: webmock
|
149
149
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,6 +184,7 @@ files:
|
|
184
184
|
- lib/rules.rb
|
185
185
|
- lib/shard.rb
|
186
186
|
- lib/validation.rb
|
187
|
+
- lib/variation_type.rb
|
187
188
|
homepage: https://github.com/Eppo-exp/ruby-sdk
|
188
189
|
licenses:
|
189
190
|
- MIT
|
@@ -201,14 +202,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
201
202
|
requirements:
|
202
203
|
- - ">="
|
203
204
|
- !ruby/object:Gem::Version
|
204
|
-
version: 3.
|
205
|
+
version: 3.0.6
|
205
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
207
|
requirements:
|
207
208
|
- - ">="
|
208
209
|
- !ruby/object:Gem::Version
|
209
210
|
version: '0'
|
210
211
|
requirements: []
|
211
|
-
rubygems_version: 3.
|
212
|
+
rubygems_version: 3.4.6
|
212
213
|
signing_key:
|
213
214
|
specification_version: 4
|
214
215
|
summary: Eppo SDK for Ruby
|