splitclient-rb 3.1.3 → 3.2.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 +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
|