splitclient-rb 8.3.1 → 8.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.txt +8 -0
- data/LICENSE +1 -1
- data/lib/splitclient-rb/cache/filter/bloom_filter.rb +10 -6
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +4 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +4 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +42 -0
- data/lib/splitclient-rb/clients/split_client.rb +28 -21
- data/lib/splitclient-rb/engine/api/client.rb +3 -1
- data/lib/splitclient-rb/engine/api/splits.rb +4 -3
- data/lib/splitclient-rb/engine/auth_api_client.rb +1 -1
- data/lib/splitclient-rb/engine/common/impressions_manager.rb +24 -27
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +18 -0
- data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +41 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +4 -4
- data/lib/splitclient-rb/helpers/repository_helper.rb +7 -0
- data/lib/splitclient-rb/managers/split_manager.rb +2 -1
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_factory.rb +2 -13
- data/lib/splitclient-rb/split_logger.rb +8 -0
- data/lib/splitclient-rb/sse/notification_manager_keeper.rb +2 -2
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +8 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49270eca968d09db0947f123ef98071ebf93abe96fd70cca06ae9775fc79c334
|
4
|
+
data.tar.gz: 7205c421419bd3ab6da82eeb00361ca008d7d936eb2cc3bf83b404967f7615eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e79b5ea033863981678658a28a6444caef7564d38aad5d4a3b22d60ac569d5574b6cfba0e46a71e590a89b22eabea3ab8103f23320ce2101d96b106712e6eeb
|
7
|
+
data.tar.gz: 2f1d582dc82f396cc50400252b38155924677dc593c0a3524c2080b3d1bb93b781ee66563b2939ac064bd080296ad4797fcb0021030fa8827c799934e8bf11a7
|
data/CHANGES.txt
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
CHANGES
|
2
2
|
|
3
|
+
8.5.0 (Jan 17, 2025)
|
4
|
+
- Fixed high cpu usage when unique keys are cleared every 24 hours.
|
5
|
+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs.
|
6
|
+
|
7
|
+
8.4.0 (May 3, 2024)
|
8
|
+
- Fixed issue preventing Impressopns and Events posting if client.destroy is called before the post threads started
|
9
|
+
- Added support for targeting rules based on semantic versions (https://semver.org/).
|
10
|
+
|
3
11
|
8.3.1 (Mar 22, 2024)
|
4
12
|
- Fixed ruby process hanging due to failed thread.join command, when calling destroy and a http request still active.
|
5
13
|
- Fixed streaming notification parser. Issue ref: https://github.com/splitio/ruby-client/issues/511
|
data/LICENSE
CHANGED
@@ -8,8 +8,8 @@ module SplitIoClient
|
|
8
8
|
class BloomFilter
|
9
9
|
def initialize(capacity, false_positive_probability = 0.001)
|
10
10
|
@capacity = capacity.round
|
11
|
-
m = best_m(capacity, false_positive_probability)
|
12
|
-
|
11
|
+
@m = best_m(capacity, false_positive_probability)
|
12
|
+
reset_filter
|
13
13
|
@k = best_k(capacity)
|
14
14
|
end
|
15
15
|
|
@@ -17,22 +17,26 @@ module SplitIoClient
|
|
17
17
|
return false if contains?(string)
|
18
18
|
|
19
19
|
positions = hashes(string)
|
20
|
-
|
21
20
|
positions.each { |position| @ba[position] = 1 }
|
22
21
|
|
23
22
|
true
|
24
23
|
end
|
25
|
-
|
24
|
+
|
26
25
|
def contains?(string)
|
27
26
|
!hashes(string).any? { |ea| @ba[ea] == 0 }
|
28
27
|
end
|
29
28
|
|
30
29
|
def clear
|
31
|
-
@ba
|
30
|
+
@ba = nil
|
31
|
+
reset_filter
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
private
|
35
35
|
|
36
|
+
def reset_filter
|
37
|
+
@ba = BitArray.new(@m.round)
|
38
|
+
end
|
39
|
+
|
36
40
|
# m is the required number of bits in the array
|
37
41
|
def best_m(capacity, false_positive_probability)
|
38
42
|
-(capacity * Math.log(false_positive_probability)) / (Math.log(2) ** 2)
|
@@ -5,6 +5,32 @@ module SplitIoClient
|
|
5
5
|
module Repositories
|
6
6
|
class SplitsRepository < Repository
|
7
7
|
attr_reader :adapter
|
8
|
+
DEFAULT_CONDITIONS_TEMPLATE = [{
|
9
|
+
conditionType: "ROLLOUT",
|
10
|
+
matcherGroup: {
|
11
|
+
combiner: "AND",
|
12
|
+
matchers: [
|
13
|
+
{
|
14
|
+
keySelector: nil,
|
15
|
+
matcherType: "ALL_KEYS",
|
16
|
+
negate: false,
|
17
|
+
userDefinedSegmentMatcherData: nil,
|
18
|
+
whitelistMatcherData: nil,
|
19
|
+
unaryNumericMatcherData: nil,
|
20
|
+
betweenMatcherData: nil,
|
21
|
+
dependencyMatcherData: nil,
|
22
|
+
booleanMatcherData: nil,
|
23
|
+
stringMatcherData: nil
|
24
|
+
}]
|
25
|
+
},
|
26
|
+
partitions: [
|
27
|
+
{
|
28
|
+
treatment: "control",
|
29
|
+
size: 100
|
30
|
+
}
|
31
|
+
],
|
32
|
+
label: "targeting rule type unsupported by sdk"
|
33
|
+
}]
|
8
34
|
|
9
35
|
def initialize(config, flag_sets_repository, flag_set_filter)
|
10
36
|
super(config)
|
@@ -155,6 +181,10 @@ module SplitIoClient
|
|
155
181
|
remove_from_flag_sets(existing_split)
|
156
182
|
end
|
157
183
|
|
184
|
+
if check_undefined_matcher(split)
|
185
|
+
@config.logger.warn("Feature Flag #{split[:name]} has undefined matcher, setting conditions to default template.")
|
186
|
+
split[:conditions] = SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE
|
187
|
+
end
|
158
188
|
if !split[:sets].nil?
|
159
189
|
for flag_set in split[:sets]
|
160
190
|
if !@flag_sets.flag_set_exist?(flag_set)
|
@@ -170,6 +200,18 @@ module SplitIoClient
|
|
170
200
|
@adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
|
171
201
|
end
|
172
202
|
|
203
|
+
def check_undefined_matcher(split)
|
204
|
+
for condition in split[:conditions]
|
205
|
+
for matcher in condition[:matcherGroup][:matchers]
|
206
|
+
if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}")
|
207
|
+
@config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{split[:name]}")
|
208
|
+
return true
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
return false
|
213
|
+
end
|
214
|
+
|
173
215
|
def remove_feature_flag(split)
|
174
216
|
decrease_tt_name_count(split[:trafficTypeName])
|
175
217
|
remove_from_flag_sets(split)
|
@@ -96,7 +96,16 @@ module SplitIoClient
|
|
96
96
|
|
97
97
|
def destroy
|
98
98
|
@config.logger.info('Split client shutdown started...') if @config.debug_enabled
|
99
|
-
|
99
|
+
if !@config.cache_adapter.is_a?(SplitIoClient::Cache::Adapters::RedisAdapter) && @config.impressions_mode != :none &&
|
100
|
+
(!@impressions_repository.empty? || !@events_repository.empty?)
|
101
|
+
@config.logger.debug("Impressions and/or Events cache is not empty")
|
102
|
+
# Adding small delay to ensure sender threads are fully running
|
103
|
+
sleep(0.1)
|
104
|
+
if !@config.threads.key?(:impressions_sender) || !@config.threads.key?(:events_sender)
|
105
|
+
@config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.")
|
106
|
+
@config.threads[:start_sdk].join(5) if @config.threads.key?(:start_sdk)
|
107
|
+
end
|
108
|
+
end
|
100
109
|
@config.threads.select { |name, thread| name.to_s.end_with? 'sender' }.values.each do |thread|
|
101
110
|
thread.raise(SplitIoClient::SDKShutdownException)
|
102
111
|
thread.join
|
@@ -258,7 +267,7 @@ module SplitIoClient
|
|
258
267
|
to_return = Hash.new
|
259
268
|
sanitized_feature_flag_names.each {|name|
|
260
269
|
to_return[name.to_sym] = control_treatment_with_config
|
261
|
-
impressions << @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ label
|
270
|
+
impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }), :disabled => false }
|
262
271
|
}
|
263
272
|
@impressions_manager.track(impressions)
|
264
273
|
return to_return
|
@@ -269,7 +278,6 @@ module SplitIoClient
|
|
269
278
|
valid_feature_flag_names << feature_flag_name unless feature_flag_name.nil?
|
270
279
|
}
|
271
280
|
start = Time.now
|
272
|
-
impressions_total = []
|
273
281
|
|
274
282
|
feature_flags = @splits_repository.splits(valid_feature_flag_names)
|
275
283
|
treatments = Hash.new
|
@@ -282,15 +290,14 @@ module SplitIoClient
|
|
282
290
|
next
|
283
291
|
end
|
284
292
|
treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method)
|
285
|
-
impressions_total.concat(impressions) unless impressions.nil?
|
286
293
|
treatments[key] =
|
287
294
|
{
|
288
295
|
treatment: treatments_labels_change_numbers[:treatment],
|
289
296
|
config: treatments_labels_change_numbers[:config]
|
290
297
|
}
|
298
|
+
@impressions_manager.track(impressions) unless impressions.empty?
|
291
299
|
end
|
292
300
|
record_latency(calling_method, start)
|
293
|
-
@impressions_manager.track(impressions_total) unless impressions_total.empty?
|
294
301
|
|
295
302
|
treatments.merge(invalid_treatments)
|
296
303
|
end
|
@@ -323,50 +330,50 @@ module SplitIoClient
|
|
323
330
|
end
|
324
331
|
|
325
332
|
feature_flag = @splits_repository.get_split(feature_flag_name)
|
326
|
-
treatments,
|
333
|
+
treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple)
|
327
334
|
|
328
|
-
@impressions_manager.track(
|
335
|
+
@impressions_manager.track(impressions_decorator) unless impressions_decorator.nil?
|
329
336
|
treatments
|
330
337
|
end
|
331
338
|
|
332
339
|
def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false)
|
333
|
-
|
340
|
+
impressions_decorator = []
|
334
341
|
begin
|
335
342
|
start = Time.now
|
336
343
|
if feature_flag.nil? && ready?
|
337
344
|
@config.logger.warn("#{calling_method}: you passed #{feature_flag_name} that " \
|
338
345
|
'does not exist in this environment, please double check what feature flags exist in the Split user interface')
|
339
|
-
return parsed_treatment(control_treatment.merge({ label
|
346
|
+
return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::NOT_FOUND }), multiple), nil
|
340
347
|
end
|
341
|
-
|
348
|
+
|
342
349
|
if !feature_flag.nil? && ready?
|
343
|
-
@evaluator.evaluate_feature_flag(
|
350
|
+
treatment_data = @evaluator.evaluate_feature_flag(
|
344
351
|
{ bucketing_key: bucketing_key, matching_key: matching_key }, feature_flag, attributes
|
345
352
|
)
|
353
|
+
impressions_disabled = feature_flag[:impressionsDisabled]
|
346
354
|
else
|
347
355
|
@config.logger.error("#{calling_method}: the SDK is not ready, results may be incorrect for feature flag #{feature_flag_name}. Make sure to wait for SDK readiness before using this method.")
|
348
|
-
control_treatment.merge({ label
|
356
|
+
treatment_data = control_treatment.merge({ :label => Engine::Models::Label::NOT_READY })
|
357
|
+
impressions_disabled = false
|
349
358
|
end
|
350
359
|
|
351
360
|
record_latency(calling_method, start)
|
352
|
-
|
353
|
-
|
361
|
+
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }), :disabled => impressions_disabled }
|
362
|
+
impressions_decorator << impression_decorator unless impression_decorator.nil?
|
354
363
|
rescue StandardError => e
|
355
364
|
@config.log_found_exception(__method__.to_s, e)
|
356
|
-
|
357
365
|
record_exception(calling_method)
|
366
|
+
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }), :disabled => false }
|
367
|
+
impressions_decorator << impression_decorator unless impression_decorator.nil?
|
358
368
|
|
359
|
-
|
360
|
-
impressions << impression unless impression.nil?
|
361
|
-
|
362
|
-
return parsed_treatment(control_treatment.merge({ label: Engine::Models::Label::EXCEPTION }), multiple), impressions
|
369
|
+
return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::EXCEPTION }), multiple), impressions_decorator
|
363
370
|
end
|
364
371
|
|
365
|
-
return parsed_treatment(treatment_data, multiple),
|
372
|
+
return parsed_treatment(treatment_data, multiple), impressions_decorator
|
366
373
|
end
|
367
374
|
|
368
375
|
def control_treatment
|
369
|
-
{ treatment
|
376
|
+
{ :treatment => Engine::Models::Treatment::CONTROL }
|
370
377
|
end
|
371
378
|
|
372
379
|
def control_treatment_with_config
|
@@ -10,6 +10,7 @@ module SplitIoClient
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def get_api(url, api_key, params = {}, cache_control_headers = false)
|
13
|
+
api_client.options.params_encoder.sort_params = false
|
13
14
|
api_client.get(url, params) do |req|
|
14
15
|
req.headers = common_headers(api_key).merge('Accept-Encoding' => 'gzip')
|
15
16
|
req.headers = req.headers.merge('Cache-Control' => 'no-cache') if cache_control_headers
|
@@ -29,7 +30,7 @@ module SplitIoClient
|
|
29
30
|
req.headers = common_headers(api_key)
|
30
31
|
.merge('Content-Type' => 'application/json')
|
31
32
|
.merge(headers)
|
32
|
-
|
33
|
+
|
33
34
|
machine_ip = @config.machine_ip
|
34
35
|
machine_name = @config.machine_name
|
35
36
|
|
@@ -55,6 +56,7 @@ module SplitIoClient
|
|
55
56
|
@api_client ||= Faraday.new do |builder|
|
56
57
|
builder.use SplitIoClient::FaradayMiddleware::Gzip
|
57
58
|
builder.adapter :net_http_persistent
|
59
|
+
builder.options.params_encoder = Faraday::FlatParamsEncoder
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
@@ -4,6 +4,7 @@ module SplitIoClient
|
|
4
4
|
module Api
|
5
5
|
# Retrieves split definitions from the Split Backend
|
6
6
|
class Splits < Client
|
7
|
+
|
7
8
|
def initialize(api_key, config, telemetry_runtime_producer)
|
8
9
|
super(config)
|
9
10
|
@api_key = api_key
|
@@ -11,12 +12,12 @@ module SplitIoClient
|
|
11
12
|
@flag_sets_filter = @config.flag_sets_filter
|
12
13
|
end
|
13
14
|
|
14
|
-
def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil
|
15
|
+
def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
|
15
16
|
start = Time.now
|
16
17
|
|
17
|
-
params = { since: since }
|
18
|
-
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
|
18
|
+
params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since }
|
19
19
|
params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
|
20
|
+
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
|
20
21
|
@config.logger.debug("Fetching from splitChanges with #{params}: ")
|
21
22
|
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
|
22
23
|
if response.status == 414
|
@@ -14,7 +14,7 @@ module SplitIoClient
|
|
14
14
|
|
15
15
|
def authenticate(api_key)
|
16
16
|
start = Time.now
|
17
|
-
response = @api_client.get_api(@config.auth_service_url, api_key)
|
17
|
+
response = @api_client.get_api("#{@config.auth_service_url}?s=#{SplitIoClient::Spec::FeatureFlags::SPEC_VERSION}", api_key)
|
18
18
|
|
19
19
|
return process_success(response, start) if response.success?
|
20
20
|
|
@@ -18,19 +18,16 @@ module SplitIoClient
|
|
18
18
|
@unique_keys_tracker = unique_keys_tracker
|
19
19
|
end
|
20
20
|
|
21
|
-
def build_impression(matching_key, bucketing_key, split_name,
|
22
|
-
impression_data = impression_data(matching_key, bucketing_key, split_name,
|
23
|
-
|
21
|
+
def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {})
|
22
|
+
impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time])
|
24
23
|
begin
|
25
|
-
|
26
|
-
when :debug # In DEBUG mode we should calculate the pt only.
|
27
|
-
impression_data[:pt] = @impression_observer.test_and_set(impression_data)
|
28
|
-
when :none # In NONE mode we should track the total amount of evaluations and the unique keys.
|
24
|
+
if @config.impressions_mode == :none || impressions_disabled
|
29
25
|
@impression_counter.inc(split_name, impression_data[:m])
|
30
26
|
@unique_keys_tracker.track(split_name, matching_key)
|
27
|
+
elsif @config.impressions_mode == :debug # In DEBUG mode we should calculate the pt only.
|
28
|
+
impression_data[:pt] = @impression_observer.test_and_set(impression_data)
|
31
29
|
else # In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions.
|
32
30
|
impression_data[:pt] = @impression_observer.test_and_set(impression_data)
|
33
|
-
|
34
31
|
@impression_counter.inc(split_name, impression_data[:m]) unless impression_data[:pt].nil?
|
35
32
|
end
|
36
33
|
rescue StandardError => e
|
@@ -40,24 +37,25 @@ module SplitIoClient
|
|
40
37
|
impression(impression_data, params[:attributes])
|
41
38
|
end
|
42
39
|
|
43
|
-
def track(
|
44
|
-
return if
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
40
|
+
def track(impressions_decorator)
|
41
|
+
return if impressions_decorator.empty?
|
42
|
+
|
43
|
+
impressions_decorator.each do |impression_decorator|
|
44
|
+
impression_router.add_bulk([impression_decorator[:impression]])
|
45
|
+
stats = { dropped: 0, queued: 0, dedupe: 0 }
|
46
|
+
begin
|
47
|
+
next if @config.impressions_mode == :none || impression_decorator[:disabled]
|
48
|
+
|
49
|
+
if @config.impressions_mode == :debug
|
50
|
+
track_debug_mode([impression_decorator[:impression]], stats)
|
51
|
+
else
|
52
|
+
track_optimized_mode([impression_decorator[:impression]], stats)
|
53
|
+
end
|
54
|
+
rescue StandardError => e
|
55
|
+
@config.log_found_exception(__method__.to_s, e)
|
56
|
+
ensure
|
57
|
+
record_stats(stats)
|
55
58
|
end
|
56
|
-
rescue StandardError => e
|
57
|
-
@config.log_found_exception(__method__.to_s, e)
|
58
|
-
ensure
|
59
|
-
record_stats(stats)
|
60
|
-
impression_router.add_bulk(impressions)
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -126,11 +124,10 @@ module SplitIoClient
|
|
126
124
|
|
127
125
|
def track_optimized_mode(impressions, stats)
|
128
126
|
optimized_impressions = impressions.select { |imp| should_queue_impression?(imp[:i]) }
|
129
|
-
|
127
|
+
stats[:dedupe] = impressions.length - optimized_impressions.length
|
130
128
|
return if optimized_impressions.empty?
|
131
129
|
|
132
130
|
stats[:dropped] = @impressions_repository.add_bulk(optimized_impressions)
|
133
|
-
stats[:dedupe] = impressions.length - optimized_impressions.length
|
134
131
|
stats[:queued] = optimized_impressions.length - stats[:dropped]
|
135
132
|
end
|
136
133
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class BetweenSemverMatcher < Matcher
|
5
|
+
MATCHER_TYPE = 'BETWEEN_SEMVER'
|
6
|
+
|
7
|
+
attr_reader :attribute
|
8
|
+
|
9
|
+
def initialize(attribute, start_value, end_value, logger, validator)
|
10
|
+
super(logger)
|
11
|
+
@validator = validator
|
12
|
+
@attribute = attribute
|
13
|
+
@semver_start = SplitIoClient::Semver.build(start_value, logger)
|
14
|
+
@semver_end = SplitIoClient::Semver.build(end_value, logger)
|
15
|
+
@logger = logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def match?(args)
|
19
|
+
return false unless verify_semver_arg?(args, 'BetweenSemverMatcher')
|
20
|
+
|
21
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
22
|
+
if value_to_match.nil? || @semver_start.nil? || @semver_end.nil?
|
23
|
+
@logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type')
|
24
|
+
return false
|
25
|
+
|
26
|
+
end
|
27
|
+
matches = ([0, -1].include?(@semver_start.compare(value_to_match)) &&
|
28
|
+
[0, 1].include?(@semver_end.compare(value_to_match)))
|
29
|
+
@logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}")
|
30
|
+
matches
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class EqualToSemverMatcher < Matcher
|
5
|
+
MATCHER_TYPE = 'EQUAL_TO_SEMVER'
|
6
|
+
|
7
|
+
attr_reader :attribute
|
8
|
+
|
9
|
+
def initialize(attribute, string_value, logger, validator)
|
10
|
+
super(logger)
|
11
|
+
@validator = validator
|
12
|
+
@attribute = attribute
|
13
|
+
@semver = SplitIoClient::Semver.build(string_value, logger)
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def match?(args)
|
18
|
+
return false unless verify_semver_arg?(args, 'EqualsToSemverMatcher')
|
19
|
+
|
20
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
21
|
+
return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
|
22
|
+
|
23
|
+
matches = (@semver.version == value_to_match.version)
|
24
|
+
@logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
|
25
|
+
matches
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class GreaterThanOrEqualToSemverMatcher < Matcher
|
5
|
+
MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
|
6
|
+
|
7
|
+
attr_reader :attribute
|
8
|
+
|
9
|
+
def initialize(attribute, string_value, logger, validator)
|
10
|
+
super(logger)
|
11
|
+
@validator = validator
|
12
|
+
@attribute = attribute
|
13
|
+
@semver = SplitIoClient::Semver.build(string_value, logger)
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def match?(args)
|
18
|
+
return false unless verify_semver_arg?(args, 'GreaterThanOrEqualsToSemverMatcher')
|
19
|
+
|
20
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
21
|
+
return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
|
22
|
+
|
23
|
+
matches = [0, 1].include?(value_to_match.compare(@semver))
|
24
|
+
@logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
|
25
|
+
matches
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class InListSemverMatcher < Matcher
|
5
|
+
MATCHER_TYPE = 'IN_LIST_SEMVER'
|
6
|
+
|
7
|
+
attr_reader :attribute
|
8
|
+
|
9
|
+
def initialize(attribute, list_value, logger, validator)
|
10
|
+
super(logger)
|
11
|
+
@validator = validator
|
12
|
+
@attribute = attribute
|
13
|
+
@semver_list = []
|
14
|
+
|
15
|
+
list_value.map do |item|
|
16
|
+
version = SplitIoClient::Semver.build(item, logger)
|
17
|
+
@semver_list << version unless version.nil?
|
18
|
+
end
|
19
|
+
@logger = logger
|
20
|
+
end
|
21
|
+
|
22
|
+
def match?(args)
|
23
|
+
return false if @semver_list.empty? || !verify_semver_arg?(args, 'InListSemverMatcher')
|
24
|
+
|
25
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
26
|
+
if value_to_match.nil?
|
27
|
+
@logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type')
|
28
|
+
return false
|
29
|
+
|
30
|
+
end
|
31
|
+
matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true }
|
32
|
+
@logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}")
|
33
|
+
matches
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class LessThanOrEqualToSemverMatcher < Matcher
|
5
|
+
MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
|
6
|
+
|
7
|
+
attr_reader :attribute
|
8
|
+
|
9
|
+
def initialize(attribute, string_value, logger, validator)
|
10
|
+
super(logger)
|
11
|
+
@validator = validator
|
12
|
+
@attribute = attribute
|
13
|
+
@semver = SplitIoClient::Semver.build(string_value, logger)
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def match?(args)
|
18
|
+
return false unless verify_semver_arg?(args, 'LessThanOrEqualsToSemverMatcher')
|
19
|
+
|
20
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
21
|
+
return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
|
22
|
+
|
23
|
+
matches = [0, -1].include?(value_to_match.compare(@semver))
|
24
|
+
@logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
|
25
|
+
matches
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -30,5 +30,23 @@ module SplitIoClient
|
|
30
30
|
def string_type?
|
31
31
|
false
|
32
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def verify_semver_arg?(args, matcher_name)
|
37
|
+
@logger.debug("[#{matcher_name}] evaluating value and attributes.")
|
38
|
+
return false unless @validator.valid_matcher_arguments(args)
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_semver_value_to_match(value_to_match, matcher_spec_name)
|
44
|
+
if value_to_match.nil? || @semver.nil?
|
45
|
+
@logger.error("stringMatcherData is required for #{matcher_spec_name} matcher type")
|
46
|
+
return false
|
47
|
+
|
48
|
+
end
|
49
|
+
true
|
50
|
+
end
|
33
51
|
end
|
34
52
|
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
class Semver
|
5
|
+
METADATA_DELIMITER = '+'
|
6
|
+
PRE_RELEASE_DELIMITER = '-'
|
7
|
+
VALUE_DELIMITER = '.'
|
8
|
+
|
9
|
+
attr_reader :major, :minor, :patch, :pre_release, :is_stable, :version
|
10
|
+
|
11
|
+
def initialize(version)
|
12
|
+
@major = 0
|
13
|
+
@minor = 0
|
14
|
+
@patch = 0
|
15
|
+
@pre_release = []
|
16
|
+
@is_stable = false
|
17
|
+
@version = ''
|
18
|
+
@metadata = ''
|
19
|
+
parse(version)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Class builder
|
24
|
+
#
|
25
|
+
# @param version [String] raw version as read from splitChanges response.
|
26
|
+
#
|
27
|
+
# @return [type] Semver instance
|
28
|
+
def self.build(version, logger)
|
29
|
+
new(version)
|
30
|
+
rescue NoMethodError => e
|
31
|
+
logger.error("Failed to parse Semver data, incorrect data type: #{e}")
|
32
|
+
nil
|
33
|
+
rescue StandardError => e
|
34
|
+
logger.error("Failed to parse Semver data: #{e}")
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Check if there is any metadata characters in version.
|
40
|
+
#
|
41
|
+
# @return [type] String semver without the metadata
|
42
|
+
#
|
43
|
+
def remove_metadata_if_exists(old_version)
|
44
|
+
index = old_version.index(METADATA_DELIMITER)
|
45
|
+
return old_version if index.nil?
|
46
|
+
|
47
|
+
@metadata = old_version[index + 1, old_version.length]
|
48
|
+
old_version[0, index]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Compare the current Semver object to a given Semver object, return:
|
52
|
+
# 0: if self == passed
|
53
|
+
# 1: if self > passed
|
54
|
+
# -1: if self < passed
|
55
|
+
#
|
56
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
57
|
+
#
|
58
|
+
# @returns [Integer] based on comparison
|
59
|
+
def compare(to_compare)
|
60
|
+
return 0 if @version == to_compare.version
|
61
|
+
|
62
|
+
# Compare major, minor, and patch versions numerically
|
63
|
+
result = compare_attributes(to_compare)
|
64
|
+
return result if result != 0
|
65
|
+
|
66
|
+
# Compare pre-release versions lexically
|
67
|
+
compare_pre_release(to_compare)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def integer?(value)
|
73
|
+
!!value.match(/^(\d)+$/)
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Parse the string in version to update the other internal variables
|
78
|
+
#
|
79
|
+
def parse(old_version)
|
80
|
+
without_metadata = remove_metadata_if_exists(old_version)
|
81
|
+
|
82
|
+
index = without_metadata.index(PRE_RELEASE_DELIMITER)
|
83
|
+
if index.nil?
|
84
|
+
@is_stable = true
|
85
|
+
else
|
86
|
+
pre_release_data = without_metadata[index + 1..-1]
|
87
|
+
without_metadata = without_metadata[0, index]
|
88
|
+
@pre_release = pre_release_data.split(VALUE_DELIMITER)
|
89
|
+
end
|
90
|
+
assign_major_minor_and_patch(without_metadata)
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Set the major, minor and patch internal variables based on string passed.
|
95
|
+
#
|
96
|
+
# @param version [String] raw version containing major.minor.patch numbers.
|
97
|
+
def assign_major_minor_and_patch(version)
|
98
|
+
parts = version.split(VALUE_DELIMITER)
|
99
|
+
if parts.length != 3 ||
|
100
|
+
!(integer?(parts[0]) &&
|
101
|
+
integer?(parts[1]) &&
|
102
|
+
integer?(parts[2]))
|
103
|
+
raise "Unable to convert to Semver, incorrect format: #{version}"
|
104
|
+
end
|
105
|
+
|
106
|
+
@major = parts[0].to_i
|
107
|
+
@minor = parts[1].to_i
|
108
|
+
@patch = parts[2].to_i
|
109
|
+
@version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}"
|
110
|
+
@version += parse_pre_release
|
111
|
+
@version += "#{METADATA_DELIMITER}#{@metadata}" unless @metadata.empty?
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_pre_release
|
115
|
+
return '' if @pre_release.empty?
|
116
|
+
|
117
|
+
pre_parsed = []
|
118
|
+
@pre_release.each do |pre_digit|
|
119
|
+
pre_digit = pre_digit.to_i if integer?(pre_digit)
|
120
|
+
pre_parsed << pre_digit
|
121
|
+
end
|
122
|
+
"#{PRE_RELEASE_DELIMITER}#{pre_parsed.join('.')}"
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Compare 2 variables and return int as follows:
|
127
|
+
# 0: if var1 == var2
|
128
|
+
# 1: if var1 > var2
|
129
|
+
# -1: if var1 < var2
|
130
|
+
#
|
131
|
+
# @param var1 [type] String/Integer object that accept ==, < or > operators
|
132
|
+
# @param var2 [type] String/Integer object that accept ==, < or > operators
|
133
|
+
#
|
134
|
+
# @returns [Integer] based on comparison
|
135
|
+
def compare_vars(var1, var2)
|
136
|
+
return 0 if var1 == var2
|
137
|
+
|
138
|
+
return 1 if var1 > var2
|
139
|
+
|
140
|
+
-1
|
141
|
+
end
|
142
|
+
|
143
|
+
# Compare the current Semver object's major, minor, patch and is_stable attributes to a given Semver object, return:
|
144
|
+
# 0: if self == passed
|
145
|
+
# 1: if self > passed
|
146
|
+
# -1: if self < passed
|
147
|
+
#
|
148
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
149
|
+
#
|
150
|
+
# @returns [Integer] based on comparison
|
151
|
+
def compare_attributes(to_compare)
|
152
|
+
result = compare_vars(@major, to_compare.major)
|
153
|
+
return result if result != 0
|
154
|
+
|
155
|
+
result = compare_vars(@minor, to_compare.minor)
|
156
|
+
return result if result != 0
|
157
|
+
|
158
|
+
result = compare_vars(@patch, to_compare.patch)
|
159
|
+
return result if result != 0
|
160
|
+
|
161
|
+
return -1 if !@is_stable && to_compare.is_stable
|
162
|
+
|
163
|
+
return 1 if @is_stable && !to_compare.is_stable
|
164
|
+
|
165
|
+
0
|
166
|
+
end
|
167
|
+
|
168
|
+
# Compare the current Semver object's pre_release attribute to a given Semver object, return:
|
169
|
+
# 0: if self == passed
|
170
|
+
# 1: if self > passed
|
171
|
+
# -1: if self < passed
|
172
|
+
#
|
173
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
174
|
+
#
|
175
|
+
# @returns [Integer] based on comparison
|
176
|
+
def compare_pre_release(to_compare)
|
177
|
+
min_length = get_pre_min_length(to_compare)
|
178
|
+
0.upto(min_length - 1) do |i|
|
179
|
+
next if @pre_release[i] == to_compare.pre_release[i]
|
180
|
+
|
181
|
+
if integer?(@pre_release[i]) && integer?(to_compare.pre_release[i])
|
182
|
+
return compare_vars(@pre_release[i].to_i, to_compare.pre_release[i].to_i)
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
return compare_vars(@pre_release[i], to_compare.pre_release[i])
|
187
|
+
end
|
188
|
+
# Compare lengths of pre-release versions
|
189
|
+
compare_vars(@pre_release.length, to_compare.pre_release.length)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Get minimum of current Semver object's pre_release attributes length to a given Semver object
|
193
|
+
#
|
194
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
195
|
+
#
|
196
|
+
# @returns [Integer]
|
197
|
+
def get_pre_min_length(to_compare)
|
198
|
+
[@pre_release.length, to_compare.pre_release.length].min
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -189,6 +189,47 @@ module SplitIoClient
|
|
189
189
|
)
|
190
190
|
end
|
191
191
|
|
192
|
+
def matcher_equal_to_semver(params)
|
193
|
+
EqualToSemverMatcher.new(
|
194
|
+
params[:matcher][:keySelector][:attribute],
|
195
|
+
params[:matcher][:stringMatcherData],
|
196
|
+
@config.split_logger, @config.split_validator
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
def matcher_greater_than_or_equal_to_semver(params)
|
201
|
+
GreaterThanOrEqualToSemverMatcher.new(
|
202
|
+
params[:matcher][:keySelector][:attribute],
|
203
|
+
params[:matcher][:stringMatcherData],
|
204
|
+
@config.split_logger, @config.split_validator
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
def matcher_less_than_or_equal_to_semver(params)
|
209
|
+
LessThanOrEqualToSemverMatcher.new(
|
210
|
+
params[:matcher][:keySelector][:attribute],
|
211
|
+
params[:matcher][:stringMatcherData],
|
212
|
+
@config.split_logger, @config.split_validator
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
def matcher_between_semver(params)
|
217
|
+
BetweenSemverMatcher.new(
|
218
|
+
params[:matcher][:keySelector][:attribute],
|
219
|
+
params[:matcher][:betweenStringMatcherData][:start],
|
220
|
+
params[:matcher][:betweenStringMatcherData][:end],
|
221
|
+
@config.split_logger, @config.split_validator
|
222
|
+
)
|
223
|
+
end
|
224
|
+
|
225
|
+
def matcher_in_list_semver(params)
|
226
|
+
InListSemverMatcher.new(
|
227
|
+
params[:matcher][:keySelector][:attribute],
|
228
|
+
params[:matcher][:whitelistMatcherData][:whitelist],
|
229
|
+
@config.split_logger, @config.split_validator
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
192
233
|
#
|
193
234
|
# @return [object] the negate value for this condition
|
194
235
|
def negate
|
@@ -48,7 +48,7 @@ module SplitIoClient
|
|
48
48
|
events_sender
|
49
49
|
start_telemetry_sync_task
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
impressions_count_sender
|
53
53
|
start_unique_keys_tracker_task
|
54
54
|
end
|
@@ -175,7 +175,7 @@ module SplitIoClient
|
|
175
175
|
|
176
176
|
# Starts thread which loops constantly and sends impressions to the Split API
|
177
177
|
def impressions_sender
|
178
|
-
ImpressionsSender.new(@impressions_repository, @config, @impressions_api).call
|
178
|
+
ImpressionsSender.new(@impressions_repository, @config, @impressions_api).call
|
179
179
|
end
|
180
180
|
|
181
181
|
# Starts thread which loops constantly and sends events to the Split API
|
@@ -185,7 +185,7 @@ module SplitIoClient
|
|
185
185
|
|
186
186
|
# Starts thread which loops constantly and sends impressions count to the Split API
|
187
187
|
def impressions_count_sender
|
188
|
-
ImpressionsCountSender.new(@config, @impression_counter, @impressions_sender_adapter).call
|
188
|
+
ImpressionsCountSender.new(@config, @impression_counter, @impressions_sender_adapter).call
|
189
189
|
end
|
190
190
|
|
191
191
|
def start_telemetry_sync_task
|
@@ -203,7 +203,7 @@ module SplitIoClient
|
|
203
203
|
def sync_splits_and_segments
|
204
204
|
@config.logger.debug('Synchronizing feature flags and segments ...') if @config.debug_enabled
|
205
205
|
splits_result = @split_fetcher.fetch_splits
|
206
|
-
|
206
|
+
|
207
207
|
splits_result[:success] && @segment_fetcher.fetch_segments
|
208
208
|
end
|
209
209
|
end
|
@@ -13,6 +13,13 @@ module SplitIoClient
|
|
13
13
|
next
|
14
14
|
end
|
15
15
|
|
16
|
+
unless feature_flag.key?(:impressionsDisabled)
|
17
|
+
feature_flag[:impressionsDisabled] = false
|
18
|
+
if config.debug_enabled
|
19
|
+
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
16
23
|
config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
|
17
24
|
to_add.push(feature_flag)
|
18
25
|
end
|
@@ -107,7 +107,8 @@ module SplitIoClient
|
|
107
107
|
change_number: split[:changeNumber],
|
108
108
|
configs: split[:configurations] || {},
|
109
109
|
sets: split[:sets] || [],
|
110
|
-
default_treatment: split[:defaultTreatment]
|
110
|
+
default_treatment: split[:defaultTreatment],
|
111
|
+
impressions_disabled: split[:impressionsDisabled]
|
111
112
|
}
|
112
113
|
end
|
113
114
|
|
@@ -230,11 +230,6 @@ module SplitIoClient
|
|
230
230
|
end
|
231
231
|
|
232
232
|
def build_unique_keys_tracker
|
233
|
-
if @config.impressions_mode != :none
|
234
|
-
@unique_keys_tracker = Engine::Impressions::NoopUniqueKeysTracker.new
|
235
|
-
return
|
236
|
-
end
|
237
|
-
|
238
233
|
bf = Cache::Filter::BloomFilter.new(30_000_000)
|
239
234
|
filter_adapter = Cache::Filter::FilterAdapter.new(@config, bf)
|
240
235
|
cache = Concurrent::Hash.new
|
@@ -242,8 +237,7 @@ module SplitIoClient
|
|
242
237
|
end
|
243
238
|
|
244
239
|
def build_impressions_observer
|
245
|
-
if (@config.cache_adapter == :redis && @config.impressions_mode != :optimized)
|
246
|
-
(@config.cache_adapter == :memory && @config.impressions_mode == :none)
|
240
|
+
if (@config.cache_adapter == :redis && @config.impressions_mode != :optimized)
|
247
241
|
@impression_observer = Observers::NoopImpressionObserver.new
|
248
242
|
else
|
249
243
|
@impression_observer = Observers::ImpressionObserver.new
|
@@ -251,12 +245,7 @@ module SplitIoClient
|
|
251
245
|
end
|
252
246
|
|
253
247
|
def build_impression_counter
|
254
|
-
|
255
|
-
when :debug
|
256
|
-
@impression_counter = Engine::Common::NoopImpressionCounter.new
|
257
|
-
else
|
258
|
-
@impression_counter = Engine::Common::ImpressionCounter.new
|
259
|
-
end
|
248
|
+
@impression_counter = Engine::Common::ImpressionCounter.new
|
260
249
|
end
|
261
250
|
|
262
251
|
def build_impressions_sender_adapter
|
@@ -11,5 +11,13 @@ module SplitIoClient
|
|
11
11
|
def log_if_transport(message)
|
12
12
|
@config.logger.debug(message) if @config.transport_debug_enabled
|
13
13
|
end
|
14
|
+
|
15
|
+
def error(message)
|
16
|
+
@config.logger.error(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug(message)
|
20
|
+
@config.logger.debug(message) if @config.debug_enabled
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
@@ -14,8 +14,8 @@ module SplitIoClient
|
|
14
14
|
@telemetry_runtime_producer = telemetry_runtime_producer
|
15
15
|
@status_queue = status_queue
|
16
16
|
@publisher_available = Concurrent::AtomicBoolean.new(true)
|
17
|
-
@publishers_pri = Concurrent::AtomicFixnum.new
|
18
|
-
@publishers_sec = Concurrent::AtomicFixnum.new
|
17
|
+
@publishers_pri = Concurrent::AtomicFixnum.new(2)
|
18
|
+
@publishers_sec = Concurrent::AtomicFixnum.new(2)
|
19
19
|
end
|
20
20
|
|
21
21
|
def handle_incoming_occupancy_event(event)
|
data/lib/splitclient-rb.rb
CHANGED
@@ -90,6 +90,12 @@ require 'splitclient-rb/engine/matchers/dependency_matcher'
|
|
90
90
|
require 'splitclient-rb/engine/matchers/equal_to_boolean_matcher'
|
91
91
|
require 'splitclient-rb/engine/matchers/equal_to_matcher'
|
92
92
|
require 'splitclient-rb/engine/matchers/matches_string_matcher'
|
93
|
+
require 'splitclient-rb/engine/matchers/semver'
|
94
|
+
require 'splitclient-rb/engine/matchers/equal_to_semver_matcher'
|
95
|
+
require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher'
|
96
|
+
require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher'
|
97
|
+
require 'splitclient-rb/engine/matchers/between_semver_matcher'
|
98
|
+
require 'splitclient-rb/engine/matchers/in_list_semver_matcher'
|
93
99
|
require 'splitclient-rb/engine/evaluator/splitter'
|
94
100
|
require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker'
|
95
101
|
require 'splitclient-rb/engine/impressions/unique_keys_tracker'
|
@@ -105,6 +111,8 @@ require 'splitclient-rb/engine/sync_manager'
|
|
105
111
|
require 'splitclient-rb/engine/synchronizer'
|
106
112
|
require 'splitclient-rb/utilitites'
|
107
113
|
|
114
|
+
require 'splitclient-rb/spec.rb'
|
115
|
+
|
108
116
|
# SSE
|
109
117
|
require 'splitclient-rb/sse/event_source/client'
|
110
118
|
require 'splitclient-rb/sse/event_source/event_parser'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: splitclient-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.
|
4
|
+
version: 8.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Split Software
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: allocation_stats
|
@@ -489,6 +489,7 @@ files:
|
|
489
489
|
- lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb
|
490
490
|
- lib/splitclient-rb/engine/matchers/all_keys_matcher.rb
|
491
491
|
- lib/splitclient-rb/engine/matchers/between_matcher.rb
|
492
|
+
- lib/splitclient-rb/engine/matchers/between_semver_matcher.rb
|
492
493
|
- lib/splitclient-rb/engine/matchers/combiners.rb
|
493
494
|
- lib/splitclient-rb/engine/matchers/combining_matcher.rb
|
494
495
|
- lib/splitclient-rb/engine/matchers/contains_all_matcher.rb
|
@@ -498,13 +499,18 @@ files:
|
|
498
499
|
- lib/splitclient-rb/engine/matchers/ends_with_matcher.rb
|
499
500
|
- lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb
|
500
501
|
- lib/splitclient-rb/engine/matchers/equal_to_matcher.rb
|
502
|
+
- lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb
|
501
503
|
- lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb
|
502
504
|
- lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb
|
505
|
+
- lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb
|
506
|
+
- lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb
|
503
507
|
- lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb
|
508
|
+
- lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb
|
504
509
|
- lib/splitclient-rb/engine/matchers/matcher.rb
|
505
510
|
- lib/splitclient-rb/engine/matchers/matches_string_matcher.rb
|
506
511
|
- lib/splitclient-rb/engine/matchers/negation_matcher.rb
|
507
512
|
- lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb
|
513
|
+
- lib/splitclient-rb/engine/matchers/semver.rb
|
508
514
|
- lib/splitclient-rb/engine/matchers/set_matcher.rb
|
509
515
|
- lib/splitclient-rb/engine/matchers/starts_with_matcher.rb
|
510
516
|
- lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb
|
@@ -526,6 +532,7 @@ files:
|
|
526
532
|
- lib/splitclient-rb/helpers/thread_helper.rb
|
527
533
|
- lib/splitclient-rb/helpers/util.rb
|
528
534
|
- lib/splitclient-rb/managers/split_manager.rb
|
535
|
+
- lib/splitclient-rb/spec.rb
|
529
536
|
- lib/splitclient-rb/split_config.rb
|
530
537
|
- lib/splitclient-rb/split_factory.rb
|
531
538
|
- lib/splitclient-rb/split_factory_builder.rb
|
@@ -587,7 +594,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
587
594
|
- !ruby/object:Gem::Version
|
588
595
|
version: '0'
|
589
596
|
requirements: []
|
590
|
-
rubygems_version: 3.
|
597
|
+
rubygems_version: 3.0.9
|
591
598
|
signing_key:
|
592
599
|
specification_version: 4
|
593
600
|
summary: Ruby client for split SDK.
|