splitclient-rb 3.1.3 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.txt +3 -0
- data/Detailed-README.md +8 -4
- data/console +2 -0
- data/exe/splitio +2 -2
- data/lib/cache/adapters/redis_adapter.rb +13 -1
- data/lib/cache/repositories/impressions/memory_repository.rb +6 -4
- data/lib/cache/repositories/impressions/redis_repository.rb +17 -7
- data/lib/cache/repositories/repository.rb +1 -1
- data/lib/cache/senders/impressions_formatter.rb +15 -7
- data/lib/cache/stores/split_store.rb +1 -1
- data/lib/engine/api/client.rb +9 -3
- data/lib/engine/api/impressions.rb +13 -5
- data/lib/engine/api/metrics.rb +14 -10
- data/lib/engine/api/segments.rb +3 -1
- data/lib/engine/api/splits.rb +3 -1
- data/lib/engine/models/label.rb +12 -0
- data/lib/engine/parser/split_adapter.rb +7 -4
- data/lib/engine/parser/split_treatment.rb +20 -7
- data/lib/splitclient-rb.rb +1 -0
- data/lib/splitclient-rb/clients/split_client.rb +112 -90
- data/lib/splitclient-rb/split_config.rb +21 -8
- data/lib/splitclient-rb/split_factory.rb +1 -1
- data/lib/splitclient-rb/version.rb +1 -1
- data/runTests +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c70d0111affdbf6d68572303ca335f9712fc8b3
|
4
|
+
data.tar.gz: 58b5eacffa5a95c9ff1b906b376660e0d6331788
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99550497b640c1fdfc623ecf1800dea0e4d4ca6837132b81fd9c1ddd302bce2e251976fc46897670fe9ba87a1d62a1ba1a3fc4a385df43e90cbe3d1e86ada374
|
7
|
+
data.tar.gz: d1e7b4822aae284c209b2b866a61c4e8cfc95a71204c5b9ff96fa91c11884fa6bae83342571e81495b4b29a24610ee7d3f33fc4fe845f56a87277fd6a2c7a664
|
data/CHANGES.txt
CHANGED
data/Detailed-README.md
CHANGED
@@ -152,9 +152,13 @@ split_client.get_treatment('user_id','feature_name', attr: 'val')
|
|
152
152
|
|
153
153
|
*default value* = `Logger.new($stdout)`
|
154
154
|
|
155
|
-
**
|
155
|
+
**ready** : The SDK will block your app for provided amount of seconds until it's ready. If timeout expires `SplitIoClient::SDKBlockerTimeoutExpiredException` will be thrown. If `0` provided, then SDK would run in non-blocking mode
|
156
156
|
|
157
|
-
*default value* = `
|
157
|
+
*default value* = `0`
|
158
|
+
|
159
|
+
**labels_enabled** : Enables sending labels along with sensitive information
|
160
|
+
|
161
|
+
*default value* = `true`
|
158
162
|
|
159
163
|
**mode** : See [SDK modes section](#sdk-modes).
|
160
164
|
|
@@ -211,7 +215,7 @@ end
|
|
211
215
|
|
212
216
|
#### IMPORTANT
|
213
217
|
|
214
|
-
For now, SDK does not support both `producer` mode and `
|
218
|
+
For now, SDK does not support both `producer` mode and `ready`. You must either run SDK in `standalone` mode, or do not use `ready` option.
|
215
219
|
|
216
220
|
This begin-rescue-end block is optional, you might want to use it to catch timeout expired exception and apply some logic.
|
217
221
|
|
@@ -367,7 +371,7 @@ To run the suite of unit tests a rake task is provided.
|
|
367
371
|
|
368
372
|
Make sure redis is running in localhost at redis://127.0.0.1:6379/0 and then just run:
|
369
373
|
```bash
|
370
|
-
SPLITCLIENT_ENV=test bundle exec
|
374
|
+
SPLITCLIENT_ENV=test bundle exec rspec spec
|
371
375
|
```
|
372
376
|
|
373
377
|
Also, simplecov is used for coverage reporting. After the execution of the rake task it will create the `/coverage` folder with coverage reports in pretty HTML format.
|
data/console
ADDED
data/exe/splitio
CHANGED
@@ -54,8 +54,8 @@ opt_parser = OptionParser.new do |opts|
|
|
54
54
|
options[:impressions_refresh_rate] = c
|
55
55
|
end
|
56
56
|
|
57
|
-
opts.on("--
|
58
|
-
options[:
|
57
|
+
opts.on("--ready=SECONDS", "Seconds to block the app until SDK is ready or false to run in non-blocking mode") do |c|
|
58
|
+
options[:ready] = c
|
59
59
|
end
|
60
60
|
|
61
61
|
opts.on("--redis-url=REDIS_URL", "Set base uri for Split SDK") do |c|
|
@@ -5,6 +5,8 @@ module SplitIoClient
|
|
5
5
|
module Adapters
|
6
6
|
# Redis adapter used to provide interface to Redis
|
7
7
|
class RedisAdapter
|
8
|
+
SCAN_SLICE = 5000
|
9
|
+
|
8
10
|
attr_reader :redis
|
9
11
|
|
10
12
|
def initialize(redis_url)
|
@@ -52,7 +54,17 @@ module SplitIoClient
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def find_strings_by_prefix(prefix)
|
55
|
-
|
57
|
+
memo = { items: [], cursor: 0 }
|
58
|
+
|
59
|
+
loop do
|
60
|
+
memo[:cursor], items = @redis.scan(memo[:cursor], match: "#{prefix}*", count: SCAN_SLICE)
|
61
|
+
|
62
|
+
memo[:items].push(*items)
|
63
|
+
|
64
|
+
break if memo[:cursor] == '0'
|
65
|
+
end
|
66
|
+
|
67
|
+
memo[:items]
|
56
68
|
end
|
57
69
|
|
58
70
|
def multiple_strings(keys)
|
@@ -18,13 +18,15 @@ module SplitIoClient
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def add_bulk(key, bucketing_key,
|
22
|
-
|
21
|
+
def add_bulk(key, bucketing_key, treatments_labels_change_numbers, time)
|
22
|
+
treatments_labels_change_numbers.each do |split_name, treatment_label_number|
|
23
23
|
add(
|
24
24
|
split_name,
|
25
25
|
'key_name' => key,
|
26
26
|
'bucketing_key' => bucketing_key,
|
27
|
-
'treatment' => treatment,
|
27
|
+
'treatment' => treatment_label_number[:treatment],
|
28
|
+
'label' => @config.labels_enabled ? treatment_label_number[:label] : nil,
|
29
|
+
'change_number' => treatment_label_number[:change_number],
|
28
30
|
'time' => time
|
29
31
|
)
|
30
32
|
end
|
@@ -32,7 +34,7 @@ module SplitIoClient
|
|
32
34
|
|
33
35
|
# Get everything from the queue and leave it empty
|
34
36
|
def clear
|
35
|
-
@adapter.clear
|
37
|
+
@adapter.clear.map { |impression| impression.update(ip: @config.machine_ip) }
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
@@ -13,15 +13,23 @@ module SplitIoClient
|
|
13
13
|
# Store impression data in Redis
|
14
14
|
def add(split_name, data)
|
15
15
|
@adapter.add_to_set(
|
16
|
-
namespace_key("impressions.#{split_name}"),
|
16
|
+
namespace_key("impressions.#{split_name}"),
|
17
|
+
data.merge(split_name: split_name).to_json
|
17
18
|
)
|
18
19
|
end
|
19
20
|
|
20
|
-
def add_bulk(key, bucketing_key,
|
21
|
+
def add_bulk(key, bucketing_key, treatments_labels_change_numbers, time)
|
21
22
|
@adapter.redis.pipelined do
|
22
|
-
|
23
|
-
|
24
|
-
add(split_name,
|
23
|
+
treatments_labels_change_numbers.each_slice(IMPRESSIONS_SLICE) do |treatments_labels_change_numbers_slice|
|
24
|
+
treatments_labels_change_numbers_slice.each do |split_name, treatment_label_change_number|
|
25
|
+
add(split_name,
|
26
|
+
'key_name' => key,
|
27
|
+
'bucketing_key' => bucketing_key,
|
28
|
+
'treatment' => treatment_label_change_number[:treatment],
|
29
|
+
'label' => @config.labels_enabled ? treatment_label_change_number[:label] : nil,
|
30
|
+
'change_number' => treatment_label_change_number[:change_number],
|
31
|
+
'time' => time
|
32
|
+
)
|
25
33
|
end
|
26
34
|
end
|
27
35
|
end
|
@@ -31,13 +39,15 @@ module SplitIoClient
|
|
31
39
|
# delete fetched impressions afterwards
|
32
40
|
def clear
|
33
41
|
impressions = impression_keys.each_with_object([]) do |key, memo|
|
42
|
+
_, _, ip, = key.split('/')
|
34
43
|
members = @adapter.random_set_elements(key, @config.impressions_queue_size)
|
35
44
|
members.each do |impression|
|
36
45
|
parsed_impression = JSON.parse(impression)
|
37
46
|
|
38
47
|
memo << {
|
39
48
|
feature: parsed_impression['split_name'],
|
40
|
-
impressions: parsed_impression.reject { |k
|
49
|
+
impressions: parsed_impression.reject { |k| k == 'split_name' },
|
50
|
+
ip: ip
|
41
51
|
}
|
42
52
|
end
|
43
53
|
|
@@ -51,7 +61,7 @@ module SplitIoClient
|
|
51
61
|
|
52
62
|
# Get all sets by prefix
|
53
63
|
def impression_keys
|
54
|
-
@adapter.find_sets_by_prefix(
|
64
|
+
@adapter.find_sets_by_prefix("#{@config.redis_namespace}/*/impressions.*")
|
55
65
|
end
|
56
66
|
end
|
57
67
|
end
|
@@ -14,20 +14,26 @@ module SplitIoClient
|
|
14
14
|
return [] if impressions.empty? || filtered_impressions.empty?
|
15
15
|
|
16
16
|
formatted_impressions = unique_features(filtered_impressions).each_with_object([]) do |feature, memo|
|
17
|
+
ip = nil
|
17
18
|
current_impressions =
|
18
19
|
filtered_impressions
|
19
|
-
.select { |
|
20
|
-
.map do |
|
20
|
+
.select { |impression| impression[:feature] == feature }
|
21
|
+
.map do |impression|
|
22
|
+
ip = impression[:ip]
|
21
23
|
{
|
22
|
-
keyName:
|
23
|
-
treatment:
|
24
|
-
time:
|
24
|
+
keyName: impression[:impressions]['key_name'],
|
25
|
+
treatment: impression[:impressions]['treatment'],
|
26
|
+
time: impression[:impressions]['time'],
|
27
|
+
bucketingKey: impression[:impressions]['bucketing_key'],
|
28
|
+
label: impression[:impressions]['label'],
|
29
|
+
changeNumber: impression[:impressions]['change_number'],
|
25
30
|
}
|
26
31
|
end
|
27
32
|
|
28
33
|
memo << {
|
29
34
|
testName: feature,
|
30
|
-
keyImpressions: current_impressions
|
35
|
+
keyImpressions: current_impressions,
|
36
|
+
ip: ip
|
31
37
|
}
|
32
38
|
end
|
33
39
|
|
@@ -37,7 +43,7 @@ module SplitIoClient
|
|
37
43
|
private
|
38
44
|
|
39
45
|
def unique_features(impressions)
|
40
|
-
impressions.map { |
|
46
|
+
impressions.map { |impression| impression[:feature] }.uniq
|
41
47
|
end
|
42
48
|
|
43
49
|
# Filter seen impressions by impression_hash
|
@@ -57,6 +63,8 @@ module SplitIoClient
|
|
57
63
|
def impression_hash(impression)
|
58
64
|
"#{impression[:feature]}:" \
|
59
65
|
"#{impression[:impressions]['key_name']}:" \
|
66
|
+
"#{impression[:impressions]['bucketing_key']}:" \
|
67
|
+
"#{impression[:impressions]['change_number']}:" \
|
60
68
|
"#{impression[:impressions]['treatment']}"
|
61
69
|
end
|
62
70
|
end
|
@@ -50,7 +50,7 @@ module SplitIoClient
|
|
50
50
|
|
51
51
|
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
|
52
52
|
|
53
|
-
if @config.block_until_ready && !@sdk_blocker.ready?
|
53
|
+
if @config.block_until_ready > 0 && !@sdk_blocker.ready?
|
54
54
|
@sdk_blocker.splits_ready!
|
55
55
|
@config.logger.info('splits are ready')
|
56
56
|
end
|
data/lib/engine/api/client.rb
CHANGED
@@ -15,12 +15,16 @@ module SplitIoClient
|
|
15
15
|
config.logger.debug("GET #{url}") if config.debug_enabled
|
16
16
|
end
|
17
17
|
rescue StandardError => e
|
18
|
-
config.logger.warn("#{e}\nURL:#{url}\
|
18
|
+
config.logger.warn("#{e}\nURL:#{url}\nparams:#{params}")
|
19
|
+
|
20
|
+
false
|
19
21
|
end
|
20
22
|
|
21
|
-
def post_api(url, config, api_key, data, params = {})
|
23
|
+
def post_api(url, config, api_key, data, headers = {}, params = {})
|
22
24
|
api_client.post(url) do |req|
|
23
|
-
req.headers = common_headers(api_key, config)
|
25
|
+
req.headers = common_headers(api_key, config)
|
26
|
+
.merge('Content-Type' => 'application/json')
|
27
|
+
.merge(headers)
|
24
28
|
|
25
29
|
req.body = data.to_json
|
26
30
|
|
@@ -35,6 +39,8 @@ module SplitIoClient
|
|
35
39
|
end
|
36
40
|
rescue StandardError => e
|
37
41
|
config.logger.warn("#{e}\nURL:#{url}\ndata:#{data}\nparams:#{params}")
|
42
|
+
|
43
|
+
false
|
38
44
|
end
|
39
45
|
|
40
46
|
private
|
@@ -13,12 +13,14 @@ module SplitIoClient
|
|
13
13
|
return
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
impressions_by_ip.each do |ip, impressions|
|
17
|
+
result = post_api("#{@config.events_uri}/testImpressions/bulk", @config, @api_key, impressions, 'SplitSDKMachineIP' => ip)
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
if (200..299).include? result.status
|
20
|
+
@config.logger.debug("Impressions reported: #{total_impressions(@impressions)}") if @config.debug_enabled
|
21
|
+
else
|
22
|
+
@config.logger.error("Unexpected status code while posting impressions: #{result.status}")
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -29,6 +31,12 @@ module SplitIoClient
|
|
29
31
|
impressions_count += impression[:keyImpressions].length
|
30
32
|
end
|
31
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def impressions_by_ip
|
38
|
+
@impressions.group_by { |impression| impression[:ip] }
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
data/lib/engine/api/metrics.rb
CHANGED
@@ -23,11 +23,7 @@ module SplitIoClient
|
|
23
23
|
|
24
24
|
result = post_api("#{@config.events_uri}/metrics/time", @config, @api_key, metrics_time)
|
25
25
|
|
26
|
-
|
27
|
-
@config.logger.error("Unexpected status code while posting time metrics: #{result.status}")
|
28
|
-
else
|
29
|
-
@config.logger.debug("Metric time reported: #{metrics_time.size}") if @config.debug_enabled
|
30
|
-
end
|
26
|
+
log_status(result, metrics_time.size)
|
31
27
|
end
|
32
28
|
end
|
33
29
|
|
@@ -43,15 +39,23 @@ module SplitIoClient
|
|
43
39
|
|
44
40
|
result = post_api("#{@config.events_uri}/metrics/counter", @config, @api_key, metrics_count)
|
45
41
|
|
46
|
-
|
47
|
-
@config.logger.error("Unexpected status code while posting count metrics: #{result.status}")
|
48
|
-
else
|
49
|
-
@config.logger.debug("Metric counts reported: #{metrics_count.size}") if @config.debug_enabled
|
50
|
-
end
|
42
|
+
log_status(result, metrics_count.size)
|
51
43
|
end
|
52
44
|
end
|
53
45
|
@metrics_repository.clear_counts
|
54
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def log_status(result, info_to_log)
|
51
|
+
if result == false
|
52
|
+
@config.logger.error("Failed to make a http request")
|
53
|
+
elsif (200..299).include? result.status
|
54
|
+
@config.logger.debug("Metric time reported: #{info_to_log}") if @config.debug_enabled
|
55
|
+
else
|
56
|
+
@config.logger.error("Unexpected status code while posting time metrics: #{result.status}")
|
57
|
+
end
|
58
|
+
end
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
data/lib/engine/api/segments.rb
CHANGED
@@ -35,7 +35,9 @@ module SplitIoClient
|
|
35
35
|
segments = []
|
36
36
|
segment = get_api("#{@config.base_uri}/segmentChanges/#{name}", @config, @api_key, since: since)
|
37
37
|
|
38
|
-
if segment
|
38
|
+
if segment == false
|
39
|
+
@config.logger.error("Failed to make a http request")
|
40
|
+
elsif segment.status / 100 == 2
|
39
41
|
segment_content = JSON.parse(segment.body, symbolize_names: true)
|
40
42
|
@segments_repository.set_change_number(name, segment_content[:till])
|
41
43
|
@metrics.count(prefix + '.status.' + segment.status.to_s, 1)
|
data/lib/engine/api/splits.rb
CHANGED
@@ -12,7 +12,9 @@ module SplitIoClient
|
|
12
12
|
prefix = 'splitChangeFetcher'
|
13
13
|
splits = get_api("#{@config.base_uri}/splitChanges", @config, @api_key, since: since)
|
14
14
|
|
15
|
-
if splits
|
15
|
+
if splits == false
|
16
|
+
@config.logger.error("Failed to make a http request")
|
17
|
+
elsif splits.status / 100 == 2
|
16
18
|
result = splits_with_segment_names(splits.body)
|
17
19
|
|
18
20
|
@metrics.count(prefix + '.status.' + splits.status.to_s, 1)
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'thread'
|
3
3
|
|
4
|
+
include SplitIoClient::Cache::Stores
|
5
|
+
include SplitIoClient::Cache::Senders
|
6
|
+
|
4
7
|
module SplitIoClient
|
5
8
|
#
|
6
9
|
# acts as an api adapater to connect to split endpoints
|
@@ -56,22 +59,22 @@ module SplitIoClient
|
|
56
59
|
|
57
60
|
# Starts thread which loops constantly and stores splits in the splits_repository of choice
|
58
61
|
def split_store
|
59
|
-
|
62
|
+
SplitStore.new(@splits_repository, @config, @api_key, @metrics, @sdk_blocker).call
|
60
63
|
end
|
61
64
|
|
62
65
|
# Starts thread which loops constantly and stores segments in the segments_repository of choice
|
63
66
|
def segment_store
|
64
|
-
|
67
|
+
SegmentStore.new(@segments_repository, @config, @api_key, @metrics, @sdk_blocker).call
|
65
68
|
end
|
66
69
|
|
67
70
|
# Starts thread which loops constantly and sends impressions to the Split API
|
68
71
|
def impressions_sender
|
69
|
-
|
72
|
+
ImpressionsSender.new(@impressions_repository, @config, @api_key).call
|
70
73
|
end
|
71
74
|
|
72
75
|
# Starts thread which loops constantly and sends metrics to the Split API
|
73
76
|
def metrics_sender
|
74
|
-
|
77
|
+
MetricsSender.new(@metrics_repository, @config, @api_key).call
|
75
78
|
end
|
76
79
|
end
|
77
80
|
end
|
@@ -8,29 +8,38 @@ module SplitIoClient
|
|
8
8
|
|
9
9
|
def call(keys, split, attributes = nil)
|
10
10
|
split_model = Models::Split.new(split)
|
11
|
-
default_treatment = split[:defaultTreatment]
|
11
|
+
@default_treatment = split[:defaultTreatment]
|
12
12
|
|
13
|
-
return Treatments::CONTROL if split_model.archived?
|
13
|
+
return treatment(Models::Label::ARCHIVED, Treatments::CONTROL, split[:changeNumber]) if split_model.archived?
|
14
14
|
|
15
|
-
split_model.matchable?
|
15
|
+
if split_model.matchable?
|
16
|
+
match(split, keys, attributes)
|
17
|
+
else
|
18
|
+
treatment(Models::Label::KILLED, @default_treatment, split[:changeNumber])
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
private
|
19
23
|
|
20
|
-
def match(split, keys, attributes
|
24
|
+
def match(split, keys, attributes)
|
21
25
|
split[:conditions].each do |c|
|
22
26
|
condition = SplitIoClient::Condition.new(c)
|
23
27
|
|
24
28
|
next if condition.empty?
|
25
29
|
|
26
30
|
if matcher_type(condition).match?(keys[:matching_key], attributes)
|
27
|
-
|
31
|
+
key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
|
32
|
+
result = Splitter.get_treatment(key, split[:seed], condition.partitions)
|
28
33
|
|
29
|
-
|
34
|
+
if result.nil?
|
35
|
+
return treatment(Models::Label::NO_RULE_MATCHED, @default_treatment, split[:changeNumber])
|
36
|
+
else
|
37
|
+
return treatment(c[:label], result, split[:changeNumber])
|
38
|
+
end
|
30
39
|
end
|
31
40
|
end
|
32
41
|
|
33
|
-
default_treatment
|
42
|
+
treatment(Models::Label::NO_RULE_MATCHED, @default_treatment, split[:changeNumber])
|
34
43
|
end
|
35
44
|
|
36
45
|
def matcher_type(condition)
|
@@ -53,6 +62,10 @@ module SplitIoClient
|
|
53
62
|
final_matcher
|
54
63
|
end
|
55
64
|
end
|
65
|
+
|
66
|
+
def treatment(label, treatment, change_number = nil)
|
67
|
+
{ label: label, treatment: treatment, change_number: change_number }
|
68
|
+
end
|
56
69
|
end
|
57
70
|
end
|
58
71
|
end
|
data/lib/splitclient-rb.rb
CHANGED
@@ -1,114 +1,136 @@
|
|
1
1
|
module SplitIoClient
|
2
2
|
class SplitClient
|
3
|
-
#
|
4
|
-
# Creates a new split client instance that connects to split.io API.
|
5
|
-
#
|
6
|
-
# @param api_key [String] the API key for your split account
|
7
|
-
#
|
8
|
-
# @return [SplitIoClient] split.io client instance
|
9
|
-
def initialize(api_key, config = {}, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository)
|
10
|
-
@config = config
|
11
|
-
|
12
|
-
@splits_repository = splits_repository
|
13
|
-
@segments_repository = segments_repository
|
14
|
-
@impressions_repository = impressions_repository
|
15
|
-
@metrics_repository = metrics_repository
|
16
|
-
|
17
|
-
@adapter = adapter
|
18
|
-
end
|
19
3
|
|
20
|
-
|
21
|
-
|
22
|
-
|
4
|
+
#
|
5
|
+
# Creates a new split client instance that connects to split.io API.
|
6
|
+
#
|
7
|
+
# @param api_key [String] the API key for your split account
|
8
|
+
#
|
9
|
+
# @return [SplitIoClient] split.io client instance
|
10
|
+
def initialize(api_key, config = {}, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository)
|
11
|
+
@config = config
|
12
|
+
|
13
|
+
@splits_repository = splits_repository
|
14
|
+
@segments_repository = segments_repository
|
15
|
+
@impressions_repository = impressions_repository
|
16
|
+
@metrics_repository = metrics_repository
|
17
|
+
|
18
|
+
@adapter = adapter
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_treatments(key, split_names, attributes = nil)
|
22
|
+
bucketing_key, matching_key = keys_from_key(key)
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
treatments_labels_change_numbers =
|
25
|
+
@splits_repository.get_splits(split_names).each_with_object({}) do |(name, data), memo|
|
26
|
+
memo.merge!(name => get_treatment(key, name, attributes, data, false, true))
|
27
|
+
end
|
28
|
+
|
29
|
+
if @config.impressions_queue_size > 0
|
30
|
+
@impressions_repository.add_bulk(matching_key, bucketing_key, treatments_labels_change_numbers, (Time.now.to_f * 1000.0).to_i)
|
27
31
|
end
|
28
32
|
|
29
|
-
|
30
|
-
|
33
|
+
split_names = treatments_labels_change_numbers.keys
|
34
|
+
treatments = treatments_labels_change_numbers.values.map { |v| v[:treatment] }
|
35
|
+
|
36
|
+
Hash[split_names.zip(treatments)]
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
|
39
|
+
#
|
40
|
+
# obtains the treatment for a given feature
|
41
|
+
#
|
42
|
+
# @param key [String/Hash] user id or hash with matching_key/bucketing_key
|
43
|
+
# @param split_name [String/Array] name of the feature that is being validated or array of them
|
44
|
+
# @param attributes [Hash] attributes to pass to the treatment class
|
45
|
+
# @param split_data [Hash] split data, when provided this method doesn't fetch splits_repository for the data
|
46
|
+
# @param store_impressions [Boolean] impressions aren't stored if this flag is false
|
47
|
+
# @param multiple [Hash] internal flag to signal if method is called by get_treatments
|
48
|
+
#
|
49
|
+
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
50
|
+
def get_treatment(key, split_name, attributes = nil, split_data = nil, store_impressions = true, multiple = false)
|
51
|
+
bucketing_key, matching_key = keys_from_key(key)
|
52
|
+
|
53
|
+
if matching_key.nil?
|
54
|
+
@config.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
|
55
|
+
return parsed_treatment(multiple, { label: Engine::Models::Label::EXCEPTION, treatment: Treatments::CONTROL })
|
56
|
+
end
|
35
57
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
# @param split_name [String/Array] name of the feature that is being validated or array of them
|
41
|
-
#
|
42
|
-
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
43
|
-
def get_treatment(key, split_name, attributes = nil, split_data = nil, store_impressions = true)
|
44
|
-
bucketing_key, matching_key = keys_from_key(key)
|
45
|
-
bucketing_key = matching_key if bucketing_key.nil?
|
46
|
-
|
47
|
-
if matching_key.nil?
|
48
|
-
@config.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
|
49
|
-
return Treatments::CONTROL
|
50
|
-
end
|
58
|
+
if split_name.nil?
|
59
|
+
@config.logger.warn('split_name was null for key: ' + key)
|
60
|
+
return parsed_treatment(multiple, { label: Engine::Models::Label::EXCEPTION, treatment: Treatments::CONTROL })
|
61
|
+
end
|
51
62
|
|
52
|
-
|
53
|
-
|
54
|
-
return Treatments::CONTROL
|
55
|
-
end
|
63
|
+
start = Time.now
|
64
|
+
treatment_label_change_number = { label: Engine::Models::Label::EXCEPTION, treatment: Treatments::CONTROL }
|
56
65
|
|
57
|
-
|
58
|
-
|
66
|
+
begin
|
67
|
+
split = multiple ? split_data : @splits_repository.get_split(split_name)
|
59
68
|
|
60
|
-
|
61
|
-
|
69
|
+
if split.nil?
|
70
|
+
return parsed_treatment(multiple, treatment_label_change_number)
|
71
|
+
else
|
72
|
+
treatment_label_change_number = SplitIoClient::Engine::Parser::SplitTreatment.new(@segments_repository).call(
|
73
|
+
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
74
|
+
)
|
75
|
+
end
|
76
|
+
rescue StandardError => error
|
77
|
+
@config.log_found_exception(__method__.to_s, error)
|
62
78
|
|
63
|
-
|
64
|
-
Treatments::CONTROL
|
65
|
-
else
|
66
|
-
SplitIoClient::Engine::Parser::SplitTreatment.new(@segments_repository).call(
|
67
|
-
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
68
|
-
)
|
79
|
+
return parsed_treatment(multiple, treatment_label_change_number)
|
69
80
|
end
|
70
|
-
rescue StandardError => error
|
71
|
-
@config.log_found_exception(__method__.to_s, error)
|
72
|
-
end
|
73
81
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
begin
|
83
|
+
latency = (Time.now - start) * 1000.0
|
84
|
+
if @config.impressions_queue_size > 0 && store_impressions && split
|
85
|
+
# Disable impressions if @config.impressions_queue_size == -1
|
86
|
+
@impressions_repository.add(split_name,
|
87
|
+
'key_name' => matching_key,
|
88
|
+
'bucketing_key' => bucketing_key,
|
89
|
+
'treatment' => treatment_label_change_number[:treatment],
|
90
|
+
'label' => @config.labels_enabled ? treatment_label_change_number[:label] : nil,
|
91
|
+
'time' => (Time.now.to_f * 1000.0).to_i,
|
92
|
+
'change_number' => treatment_label_change_number[:change_number]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Measure
|
97
|
+
@adapter.metrics.time('sdk.get_treatment', latency)
|
98
|
+
rescue StandardError => error
|
99
|
+
@config.log_found_exception(__method__.to_s, error)
|
100
|
+
|
101
|
+
return parsed_treatment(multiple, treatment_label_change_number)
|
86
102
|
end
|
87
103
|
|
88
|
-
|
89
|
-
@adapter.metrics.time("sdk.get_treatment", latency)
|
90
|
-
rescue StandardError => error
|
91
|
-
@config.log_found_exception(__method__.to_s, error)
|
104
|
+
parsed_treatment(multiple, treatment_label_change_number)
|
92
105
|
end
|
93
106
|
|
94
|
-
|
95
|
-
|
107
|
+
def keys_from_key(key)
|
108
|
+
case key.class.to_s
|
109
|
+
when 'Hash'
|
110
|
+
key.values_at(:bucketing_key, :matching_key)
|
111
|
+
when 'String'
|
112
|
+
[nil, key]
|
113
|
+
end
|
114
|
+
end
|
96
115
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
116
|
+
def parsed_treatment(multiple, treatment_label_change_number)
|
117
|
+
if multiple
|
118
|
+
{
|
119
|
+
treatment: treatment_label_change_number[:treatment],
|
120
|
+
label: treatment_label_change_number[:label],
|
121
|
+
change_number: treatment_label_change_number[:change_number]
|
122
|
+
}
|
123
|
+
else
|
124
|
+
treatment_label_change_number[:treatment]
|
125
|
+
end
|
103
126
|
end
|
104
|
-
end
|
105
127
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
128
|
+
#
|
129
|
+
# method that returns the sdk gem version
|
130
|
+
#
|
131
|
+
# @return [string] version value for this sdk
|
132
|
+
def self.sdk_version
|
133
|
+
'ruby-'+SplitIoClient::VERSION
|
134
|
+
end
|
112
135
|
end
|
113
136
|
end
|
114
|
-
end
|
@@ -52,10 +52,12 @@ module SplitIoClient
|
|
52
52
|
@logger = opts[:logger] || SplitConfig.default_logger
|
53
53
|
@debug_enabled = opts[:debug_enabled] || SplitConfig.default_debug
|
54
54
|
@transport_debug_enabled = opts[:transport_debug_enabled] || SplitConfig.default_debug
|
55
|
-
@block_until_ready = opts[:block_until_ready] ||
|
55
|
+
@block_until_ready = opts[:ready] || opts[:block_until_ready] || 0
|
56
56
|
@machine_name = SplitConfig.get_hostname
|
57
57
|
@machine_ip = SplitConfig.get_ip
|
58
58
|
|
59
|
+
@labels_enabled = opts[:labels_enabled].nil? ? SplitConfig.default_labels_logging : opts[:labels_enabled]
|
60
|
+
|
59
61
|
startup_log
|
60
62
|
end
|
61
63
|
|
@@ -125,6 +127,12 @@ module SplitIoClient
|
|
125
127
|
# @return [Boolean] The value for the debug flag
|
126
128
|
attr_reader :transport_debug_enabled
|
127
129
|
|
130
|
+
#
|
131
|
+
# Enable logging labels and sending potentially sensitive information
|
132
|
+
#
|
133
|
+
# @return [Boolean] The value for the labels enabled flag
|
134
|
+
attr_reader :labels_enabled
|
135
|
+
|
128
136
|
#
|
129
137
|
# The number of seconds to wait for SDK readiness
|
130
138
|
# or false to disable waiting
|
@@ -161,7 +169,7 @@ module SplitIoClient
|
|
161
169
|
#
|
162
170
|
# @return [string] version value for this sdk
|
163
171
|
def self.sdk_version
|
164
|
-
'
|
172
|
+
'ruby-'+SplitIoClient::VERSION
|
165
173
|
end
|
166
174
|
|
167
175
|
#
|
@@ -261,6 +269,14 @@ module SplitIoClient
|
|
261
269
|
false
|
262
270
|
end
|
263
271
|
|
272
|
+
#
|
273
|
+
# The default labels logging value
|
274
|
+
#
|
275
|
+
# @return [boolean]
|
276
|
+
def self.default_labels_logging
|
277
|
+
true
|
278
|
+
end
|
279
|
+
|
264
280
|
def self.default_redis_url
|
265
281
|
'redis://127.0.0.1:6379/0'
|
266
282
|
end
|
@@ -316,12 +332,9 @@ module SplitIoClient
|
|
316
332
|
#
|
317
333
|
# @return [string]
|
318
334
|
def self.get_ip
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
#unable to get local ip
|
323
|
-
'127.0.0.0'
|
324
|
-
end
|
335
|
+
Socket.ip_address_list.detect { |intf| intf.ipv4_private? }.ip_address
|
336
|
+
rescue StandardError
|
337
|
+
'unknown'
|
325
338
|
end
|
326
339
|
end
|
327
340
|
end
|
@@ -22,7 +22,7 @@ module SplitIoClient
|
|
22
22
|
@client = SplitClient.new(@api_key, @config, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository)
|
23
23
|
@manager = SplitManager.new(@api_key, @config, @adapter, @splits_repository)
|
24
24
|
|
25
|
-
@sdk_blocker.block if @config.block_until_ready
|
25
|
+
@sdk_blocker.block if @config.block_until_ready > 0
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/runTests
ADDED
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: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Split Software
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -250,6 +250,7 @@ files:
|
|
250
250
|
- NEWS
|
251
251
|
- README.md
|
252
252
|
- Rakefile
|
253
|
+
- console
|
253
254
|
- exe/splitio
|
254
255
|
- lib/cache/adapters/memory_adapter.rb
|
255
256
|
- lib/cache/adapters/memory_adapters/map_adapter.rb
|
@@ -289,6 +290,7 @@ files:
|
|
289
290
|
- lib/engine/matchers/whitelist_matcher.rb
|
290
291
|
- lib/engine/metrics/binary_search_latency_tracker.rb
|
291
292
|
- lib/engine/metrics/metrics.rb
|
293
|
+
- lib/engine/models/label.rb
|
292
294
|
- lib/engine/models/split.rb
|
293
295
|
- lib/engine/parser/condition.rb
|
294
296
|
- lib/engine/parser/partition.rb
|
@@ -308,6 +310,7 @@ files:
|
|
308
310
|
- lib/splitclient-rb/split_factory_builder.rb
|
309
311
|
- lib/splitclient-rb/version.rb
|
310
312
|
- lib/splitclient-rb_utilitites.rb
|
313
|
+
- runTests
|
311
314
|
- splitclient-rb.gemspec
|
312
315
|
- splitio.yml.example
|
313
316
|
- tasks/benchmark_get_treatment.rake
|