eppo-server-sdk 0.1.0 → 0.2.2
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/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
|