splitclient-rb 5.1.2.pre.rc21-java → 5.1.3.pre.rc1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/CHANGES.txt +6 -0
- data/Detailed-README.md +8 -0
- data/NEWS +8 -0
- data/lib/splitclient-rb.rb +3 -2
- data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +131 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +6 -1
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +7 -2
- data/lib/splitclient-rb/clients/split_client.rb +57 -50
- data/lib/splitclient-rb/engine/models/label.rb +0 -1
- data/lib/splitclient-rb/exceptions.rb +7 -0
- data/lib/splitclient-rb/managers/split_manager.rb +1 -2
- data/lib/splitclient-rb/split_config.rb +23 -0
- data/lib/splitclient-rb/validators.rb +185 -0
- data/lib/splitclient-rb/version.rb +1 -1
- data/splitclient-rb.gemspec +2 -0
- metadata +33 -4
- data/lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb +0 -4
- data/lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86614a077b8bb2e11605c201e8d7ce74fa53af08
|
4
|
+
data.tar.gz: 5a775b16d37564ba6897dccd48d33472e7c4e1c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22d0552dfae11161414e2a624ffaf8a4e7d402914541109da17f71178474b6627e6b5c5ef71622e56190cbb1b30cec8cba60c2bf39099b0019f8705ed7c4f71a
|
7
|
+
data.tar.gz: 90e26ace99de500d55c05ddd9dc632b17e9d67bc8a1b5abbd1facae06110dfb8a43cb5443b9172293da7a25e1156a409b8e05406c7be5c1475ef307e33d70011
|
data/.gitignore
CHANGED
data/CHANGES.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
5.1.3
|
2
|
+
- Add cache wrapper to treatments and segments.
|
3
|
+
|
4
|
+
5.1.2 (October 26th, 2018)
|
5
|
+
- Add input validation for client API methods
|
6
|
+
|
1
7
|
5.1.1 (October 4th, 2018)
|
2
8
|
- Change get_treatments so that it sends a single latency metric
|
3
9
|
- Removed unused call to Redis#scan when adding latencies
|
data/Detailed-README.md
CHANGED
@@ -301,6 +301,14 @@ _To use Redis, include `redis-rb` in your app's Gemfile._
|
|
301
301
|
|
302
302
|
*default value* = (your current hostname)
|
303
303
|
|
304
|
+
**cache_ttl** : Time to live in seconds for the memory cache values when using Redis.
|
305
|
+
|
306
|
+
*default value* = `5`
|
307
|
+
|
308
|
+
**max_cache_size** : Maximum number of items held in the memory cache values when using Redis. When cache is full an LRU strategy for pruning shall be used.
|
309
|
+
|
310
|
+
*default value* = `500`
|
311
|
+
|
304
312
|
**redis_url** : Redis URL or hash with configuration for the SDK to connect to. See [Redis#initialize](https://www.rubydoc.info/github/redis/redis-rb/Redis%3Ainitialize) for detailed information.
|
305
313
|
|
306
314
|
*default value* = `'redis://127.0.0.1:6379/0'`
|
data/NEWS
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
5.1.3
|
2
|
+
|
3
|
+
Add cache wrapper to treatments and segments.
|
4
|
+
|
5
|
+
5.1.2
|
6
|
+
|
7
|
+
Add input validation for client API methods: get_treatment, get_treatments, track, manager
|
8
|
+
|
1
9
|
5.1.1
|
2
10
|
|
3
11
|
Reduces the number of calls to Redis when calling #client.get_treatments using such cache adapter.
|
data/lib/splitclient-rb.rb
CHANGED
@@ -2,11 +2,11 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
require 'splitclient-rb/version'
|
4
4
|
|
5
|
-
require 'splitclient-rb/exceptions
|
6
|
-
require 'splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception'
|
5
|
+
require 'splitclient-rb/exceptions'
|
7
6
|
require 'splitclient-rb/cache/routers/impression_router'
|
8
7
|
require 'splitclient-rb/cache/adapters/memory_adapters/map_adapter'
|
9
8
|
require 'splitclient-rb/cache/adapters/memory_adapters/queue_adapter'
|
9
|
+
require 'splitclient-rb/cache/adapters/cache_adapter'
|
10
10
|
require 'splitclient-rb/cache/adapters/memory_adapter'
|
11
11
|
require 'splitclient-rb/cache/adapters/redis_adapter'
|
12
12
|
require 'splitclient-rb/cache/repositories/repository'
|
@@ -80,6 +80,7 @@ require 'splitclient-rb/engine/models/split'
|
|
80
80
|
require 'splitclient-rb/engine/models/label'
|
81
81
|
require 'splitclient-rb/engine/models/treatment'
|
82
82
|
require 'splitclient-rb/utilitites'
|
83
|
+
require 'splitclient-rb/validators'
|
83
84
|
|
84
85
|
# C extension
|
85
86
|
require 'murmurhash/murmurhash_mri'
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'lru_redux'
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Cache
|
5
|
+
module Adapters
|
6
|
+
class CacheAdapter
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@adapter, :initialize_set, :set_bool, :pipelined
|
9
|
+
|
10
|
+
def initialize(adapter)
|
11
|
+
@cache = LruRedux::TTL::ThreadSafeCache.new(SplitIoClient.configuration.max_cache_size, SplitIoClient.configuration.cache_ttl)
|
12
|
+
@adapter = adapter
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete(key)
|
16
|
+
@cache.delete(key)
|
17
|
+
@adapter.delete(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear(namespace_key)
|
21
|
+
@cache.clear
|
22
|
+
@adapter.clear(namespace_key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def string(key)
|
26
|
+
value = get(key)
|
27
|
+
return value if value
|
28
|
+
value = @adapter.string(key)
|
29
|
+
add(key, value)
|
30
|
+
value
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_string(key, value)
|
34
|
+
add(key, value)
|
35
|
+
@adapter.set_string(key, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def multiple_strings(keys)
|
39
|
+
cached_values = keys.each_with_object({}) do |key, memo|
|
40
|
+
memo[key] = get(key)
|
41
|
+
end
|
42
|
+
|
43
|
+
non_cached_keys = []
|
44
|
+
cached_values.delete_if{ |k,v| v.nil? ? non_cached_keys << k : false }
|
45
|
+
|
46
|
+
if non_cached_keys.any?
|
47
|
+
new_values = @adapter.multiple_strings(non_cached_keys)
|
48
|
+
|
49
|
+
new_values.keys.each do |key, value|
|
50
|
+
add(key, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
cached_values.merge!(new_values)
|
54
|
+
end
|
55
|
+
|
56
|
+
cached_values
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_strings_by_prefix(prefix)
|
60
|
+
@adapter.find_strings_by_prefix(prefix)
|
61
|
+
end
|
62
|
+
|
63
|
+
def exists?(key)
|
64
|
+
cached_value = get(key)
|
65
|
+
if cached_value.nil?
|
66
|
+
@adapter.exists?(key)
|
67
|
+
else
|
68
|
+
true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_to_set(key, values)
|
73
|
+
if values.is_a? Array
|
74
|
+
values.each { |value| add_to_map(key, value, 1) }
|
75
|
+
else
|
76
|
+
add_to_map(key, values, 1)
|
77
|
+
end
|
78
|
+
@adapter.add_to_set(key, values)
|
79
|
+
end
|
80
|
+
|
81
|
+
def in_set?(key, field)
|
82
|
+
cached_value = get(key)
|
83
|
+
if cached_value.nil?
|
84
|
+
return @adapter.in_set?(key, field)
|
85
|
+
end
|
86
|
+
cached_value.key?(field)
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_set(key)
|
90
|
+
cached_value = get(key)
|
91
|
+
if cached_value.nil?
|
92
|
+
return @adapter.get_set(key)
|
93
|
+
end
|
94
|
+
cached_value.keys
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete_from_set(key, fields)
|
98
|
+
cached_value = get(key)
|
99
|
+
if cached_value
|
100
|
+
if fields.is_a? Array
|
101
|
+
fields.each { |field| cached_value.delete(field) }
|
102
|
+
else
|
103
|
+
cached_value.delete(fields)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@adapter.delete_from_set(key, fields)
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize_map(key)
|
111
|
+
@cache[key] = Concurrent::Map.new
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def add_to_map(key, field, value)
|
117
|
+
initialize_map(key) unless get(key)
|
118
|
+
get(key).put(field.to_s, value.to_s)
|
119
|
+
end
|
120
|
+
|
121
|
+
def add(key, value)
|
122
|
+
@cache[key] = value.to_s unless value.nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
def get(key)
|
126
|
+
@cache[key]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -7,7 +7,12 @@ module SplitIoClient
|
|
7
7
|
attr_reader :adapter
|
8
8
|
|
9
9
|
def initialize(adapter)
|
10
|
-
@adapter = adapter
|
10
|
+
@adapter = case adapter.class.to_s
|
11
|
+
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
|
12
|
+
SplitIoClient::Cache::Adapters::CacheAdapter.new(adapter)
|
13
|
+
else
|
14
|
+
adapter
|
15
|
+
end
|
11
16
|
@adapter.set_bool(namespace_key('.ready'), false) unless SplitIoClient.configuration.mode == :consumer
|
12
17
|
end
|
13
18
|
|
@@ -7,7 +7,12 @@ module SplitIoClient
|
|
7
7
|
attr_reader :adapter
|
8
8
|
|
9
9
|
def initialize(adapter)
|
10
|
-
@adapter = adapter
|
10
|
+
@adapter = case adapter.class.to_s
|
11
|
+
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
|
12
|
+
SplitIoClient::Cache::Adapters::CacheAdapter.new(adapter)
|
13
|
+
else
|
14
|
+
adapter
|
15
|
+
end
|
11
16
|
unless SplitIoClient.configuration.mode == :consumer
|
12
17
|
@adapter.set_string(namespace_key('.splits.till'), '-1')
|
13
18
|
@adapter.initialize_map(namespace_key('.segments.registered'))
|
@@ -26,7 +31,7 @@ module SplitIoClient
|
|
26
31
|
|
27
32
|
def get_splits(names)
|
28
33
|
splits = {}
|
29
|
-
split_names = names.
|
34
|
+
split_names = names.map { |name| namespace_key(".split.#{name}") }
|
30
35
|
splits.merge!(
|
31
36
|
@adapter
|
32
37
|
.multiple_strings(split_names)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module SplitIoClient
|
2
|
+
|
2
3
|
class SplitClient
|
3
4
|
#
|
4
5
|
# Creates a new split client instance that connects to split.io API.
|
@@ -17,11 +18,24 @@ module SplitIoClient
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def get_treatments(key, split_names, attributes = {})
|
21
|
+
|
22
|
+
return nil unless SplitIoClient::Validators.valid_get_treatments_parameters(split_names)
|
23
|
+
|
24
|
+
sanitized_split_names = sanitize_split_names(split_names)
|
25
|
+
|
26
|
+
if sanitized_split_names.empty?
|
27
|
+
SplitIoClient.configuration.logger.warn('get_treatments: split_names is an empty array or has null values')
|
28
|
+
return {}
|
29
|
+
end
|
30
|
+
|
20
31
|
bucketing_key, matching_key = keys_from_key(key)
|
32
|
+
bucketing_key = bucketing_key ? bucketing_key.to_s : nil
|
33
|
+
matching_key = matching_key ? matching_key.to_s : nil
|
34
|
+
|
21
35
|
evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, true)
|
22
36
|
start = Time.now
|
23
37
|
treatments_labels_change_numbers =
|
24
|
-
@splits_repository.get_splits(
|
38
|
+
@splits_repository.get_splits(sanitized_split_names).each_with_object({}) do |(name, data), memo|
|
25
39
|
memo.merge!(name => get_treatment(key, name, attributes, data, false, true, evaluator))
|
26
40
|
end
|
27
41
|
latency = (Time.now - start) * 1000.0
|
@@ -34,13 +48,13 @@ module SplitIoClient
|
|
34
48
|
matching_key, bucketing_key, treatments_labels_change_numbers, time
|
35
49
|
)
|
36
50
|
|
37
|
-
route_impressions(
|
51
|
+
route_impressions(sanitized_split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
|
38
52
|
end
|
39
53
|
|
40
|
-
|
54
|
+
split_names_keys = treatments_labels_change_numbers.keys
|
41
55
|
treatments = treatments_labels_change_numbers.values.map { |v| v[:treatment] }
|
42
56
|
|
43
|
-
Hash[
|
57
|
+
Hash[split_names_keys.zip(treatments)]
|
44
58
|
end
|
45
59
|
|
46
60
|
#
|
@@ -59,68 +73,43 @@ module SplitIoClient
|
|
59
73
|
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
|
60
74
|
multiple = false, evaluator = nil
|
61
75
|
)
|
62
|
-
|
63
|
-
|
64
|
-
evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository)
|
76
|
+
control_treatment = { label: Engine::Models::Label::EXCEPTION, treatment: SplitIoClient::Engine::Models::Treatment::CONTROL }
|
77
|
+
parsed_control_treatment = parsed_treatment(multiple, control_treatment)
|
65
78
|
|
66
|
-
|
67
|
-
SplitIoClient.configuration.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
|
68
|
-
return parsed_treatment(multiple, treatment_data)
|
69
|
-
end
|
79
|
+
bucketing_key, matching_key = keys_from_key(key)
|
70
80
|
|
71
|
-
|
72
|
-
SplitIoClient.configuration.logger.warn('split_name was null for key: ' + key)
|
73
|
-
return parsed_treatment(multiple, treatment_data)
|
74
|
-
end
|
81
|
+
return parsed_control_treatment unless SplitIoClient::Validators.valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
|
75
82
|
|
76
|
-
|
83
|
+
bucketing_key = bucketing_key ? bucketing_key.to_s : nil
|
84
|
+
matching_key = matching_key.to_s
|
85
|
+
evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository)
|
77
86
|
|
78
87
|
begin
|
88
|
+
start = Time.now
|
89
|
+
|
79
90
|
split = multiple ? split_data : @splits_repository.get_split(split_name)
|
80
91
|
|
81
92
|
if split.nil?
|
82
|
-
SplitIoClient.configuration.logger.
|
83
|
-
return
|
84
|
-
else
|
85
|
-
treatment_data =
|
86
|
-
evaluator.call(
|
87
|
-
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
88
|
-
)
|
93
|
+
SplitIoClient.configuration.logger.warn("split_name: #{split_name} does not exist. Returning CONTROL")
|
94
|
+
return parsed_control_treatment
|
89
95
|
end
|
90
|
-
rescue StandardError => error
|
91
|
-
SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
|
92
96
|
|
93
|
-
|
94
|
-
|
95
|
-
{
|
96
|
-
treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
|
97
|
-
label: SplitIoClient::Engine::Models::Label::EXCEPTION
|
98
|
-
},
|
99
|
-
store_impressions, attributes
|
97
|
+
treatment_data =
|
98
|
+
evaluator.call(
|
99
|
+
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
100
100
|
)
|
101
101
|
|
102
|
-
return parsed_treatment(multiple, treatment_data)
|
103
|
-
end
|
104
|
-
|
105
|
-
begin
|
106
102
|
latency = (Time.now - start) * 1000.0
|
107
|
-
|
103
|
+
store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions, attributes)
|
108
104
|
|
109
105
|
# Measure
|
110
106
|
@adapter.metrics.time('sdk.get_treatment', latency) unless multiple
|
111
107
|
rescue StandardError => error
|
112
108
|
SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
|
113
109
|
|
114
|
-
store_impression(
|
115
|
-
split_name, matching_key, bucketing_key,
|
116
|
-
{
|
117
|
-
treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
|
118
|
-
label: SplitIoClient::Engine::Models::Label::EXCEPTION
|
119
|
-
},
|
120
|
-
store_impressions, attributes
|
121
|
-
)
|
110
|
+
store_impression(split_name, matching_key, bucketing_key, control_treatment, store_impressions, attributes)
|
122
111
|
|
123
|
-
return
|
112
|
+
return parsed_control_treatment
|
124
113
|
end
|
125
114
|
|
126
115
|
parsed_treatment(multiple, treatment_data)
|
@@ -188,16 +177,23 @@ module SplitIoClient
|
|
188
177
|
@impression_router ||= SplitIoClient::ImpressionRouter.new
|
189
178
|
end
|
190
179
|
|
191
|
-
def track(key,
|
192
|
-
|
180
|
+
def track(key, traffic_type_name, event_type, value = nil)
|
181
|
+
return false unless SplitIoClient::Validators.valid_track_parameters(key, traffic_type_name, event_type, value)
|
182
|
+
begin
|
183
|
+
@events_repository.add(key.to_s, traffic_type_name, event_type.to_s, (Time.now.to_f * 1000).to_i, value)
|
184
|
+
true
|
185
|
+
rescue StandardError => error
|
186
|
+
SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
|
187
|
+
false
|
188
|
+
end
|
193
189
|
end
|
194
190
|
|
195
191
|
def keys_from_key(key)
|
196
192
|
case key.class.to_s
|
197
193
|
when 'Hash'
|
198
|
-
key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k
|
194
|
+
key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k }
|
199
195
|
else
|
200
|
-
[nil, key].map { |k| k.nil? ? nil : k
|
196
|
+
[nil, key].map { |k| k.nil? ? nil : k }
|
201
197
|
end
|
202
198
|
end
|
203
199
|
|
@@ -212,5 +208,16 @@ module SplitIoClient
|
|
212
208
|
treatment_data[:treatment]
|
213
209
|
end
|
214
210
|
end
|
211
|
+
|
212
|
+
def sanitize_split_names(split_names)
|
213
|
+
split_names.compact.uniq.select do |split_name|
|
214
|
+
if split_name.is_a?(String) && !split_name.empty?
|
215
|
+
true
|
216
|
+
else
|
217
|
+
SplitIoClient.configuration.logger.warn('get_treatments: split_name has to be a non empty string')
|
218
|
+
false
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
215
222
|
end
|
216
223
|
end
|
@@ -43,7 +43,7 @@ module SplitIoClient
|
|
43
43
|
#
|
44
44
|
# @returns a split view
|
45
45
|
def split(split_name)
|
46
|
-
return unless @splits_repository
|
46
|
+
return unless @splits_repository && SplitIoClient::Validators.valid_split_parameters(split_name)
|
47
47
|
|
48
48
|
split = @splits_repository.get_split(split_name)
|
49
49
|
|
@@ -63,7 +63,6 @@ module SplitIoClient
|
|
63
63
|
treatments = []
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
66
|
{
|
68
67
|
name: name,
|
69
68
|
traffic_type_name: split[:trafficTypeName],
|
@@ -33,6 +33,8 @@ module SplitIoClient
|
|
33
33
|
# @option opts [Int] :impressions_queue_size Size of the impressions queue in the memory repository. Once reached, newer impressions will be dropped
|
34
34
|
# @option opts [Int] :impressions_bulk_size Max number of impressions to be sent to the backend on each post
|
35
35
|
# @option opts [#log] :impression_listener this object will capture all impressions and process them through `#log`
|
36
|
+
# @option opts [Int] :cache_ttl Time to live in seconds for the memory cache values when using Redis.
|
37
|
+
# @option opts [Int] :max_cache_size Max number of items to be held in the memory cache before prunning when using Redis.
|
36
38
|
# @return [type] SplitConfig with configuration options
|
37
39
|
def initialize(opts = {})
|
38
40
|
@base_uri = (opts[:base_uri] || SplitConfig.default_base_uri).chomp('/')
|
@@ -70,6 +72,9 @@ module SplitIoClient
|
|
70
72
|
@machine_name = opts[:machine_name] || SplitConfig.machine_hostname
|
71
73
|
@machine_ip = opts[:machine_ip] || SplitConfig.machine_ip
|
72
74
|
|
75
|
+
@cache_ttl = opts[:cache_ttl] || SplitConfig.cache_ttl
|
76
|
+
@max_cache_size = opts[:max_cache_size] || SplitConfig.max_cache_size
|
77
|
+
|
73
78
|
@language = opts[:language] || 'ruby'
|
74
79
|
@version = opts[:version] || SplitIoClient::VERSION
|
75
80
|
|
@@ -176,6 +181,9 @@ module SplitIoClient
|
|
176
181
|
attr_accessor :machine_ip
|
177
182
|
attr_accessor :machine_name
|
178
183
|
|
184
|
+
attr_accessor :cache_ttl
|
185
|
+
attr_accessor :max_cache_size
|
186
|
+
|
179
187
|
attr_accessor :language
|
180
188
|
attr_accessor :version
|
181
189
|
|
@@ -357,6 +365,21 @@ module SplitIoClient
|
|
357
365
|
false
|
358
366
|
end
|
359
367
|
|
368
|
+
#
|
369
|
+
# The default cache time to live
|
370
|
+
#
|
371
|
+
# @return [boolean]
|
372
|
+
def self.cache_ttl
|
373
|
+
5
|
374
|
+
end
|
375
|
+
|
376
|
+
# The default max cache size
|
377
|
+
#
|
378
|
+
# @return [boolean]
|
379
|
+
def self.max_cache_size
|
380
|
+
500
|
381
|
+
end
|
382
|
+
|
360
383
|
#
|
361
384
|
# custom logger of exceptions
|
362
385
|
#
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Validators
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
|
6
|
+
valid_key?(key) && valid_split_name?(split_name) && valid_matching_key?(matching_key) && valid_bucketing_key?(bucketing_key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid_get_treatments_parameters(split_names)
|
10
|
+
valid_split_names?(split_names)
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid_track_parameters(key, traffic_type_name, event_type, value)
|
14
|
+
valid_track_key?(key) && valid_traffic_type_name?(traffic_type_name) && valid_event_type?(event_type) && valid_value?(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid_split_parameters(split_name)
|
18
|
+
valid_split_name?(split_name, :split)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def string?(value)
|
24
|
+
value.is_a?(String) || value.is_a?(Symbol)
|
25
|
+
end
|
26
|
+
|
27
|
+
def number_or_string?(value)
|
28
|
+
value.is_a?(Numeric) || string?(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def log_nil(key, method)
|
32
|
+
SplitIoClient.configuration.logger.error("#{method}: #{key} cannot be nil")
|
33
|
+
end
|
34
|
+
|
35
|
+
def log_string(key, method)
|
36
|
+
SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String or a Symbol")
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_number_or_string(key, method)
|
40
|
+
SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String")
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_convert_numeric(key, method)
|
44
|
+
SplitIoClient.configuration.logger.warn("#{method}: #{key} is not of type String, converting to String")
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_split_name?(split_name, method=:get_treatment)
|
48
|
+
if split_name.nil?
|
49
|
+
log_nil(:split_name, method)
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
|
53
|
+
unless string?(split_name)
|
54
|
+
log_string(:split_name, method)
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
return true
|
59
|
+
end
|
60
|
+
|
61
|
+
def valid_key?(key)
|
62
|
+
if key.nil?
|
63
|
+
log_nil(:key, :get_treatment)
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_matching_key?(matching_key)
|
71
|
+
if matching_key.nil?
|
72
|
+
log_nil(:matching_key, :get_treatment)
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
|
76
|
+
unless number_or_string?(matching_key)
|
77
|
+
log_number_or_string(:matching_key, :get_treatment)
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
if matching_key.is_a? Numeric
|
82
|
+
log_convert_numeric(:matching_key, :get_treatment)
|
83
|
+
end
|
84
|
+
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
|
88
|
+
def valid_bucketing_key?(bucketing_key)
|
89
|
+
if bucketing_key.nil?
|
90
|
+
SplitIoClient.configuration.logger.warn('get_treatment: key object should have bucketing_key set')
|
91
|
+
return true
|
92
|
+
end
|
93
|
+
|
94
|
+
unless number_or_string?(bucketing_key)
|
95
|
+
log_number_or_string(:bucketing_key, :get_treatment)
|
96
|
+
return false
|
97
|
+
end
|
98
|
+
|
99
|
+
if bucketing_key.is_a? Numeric
|
100
|
+
log_convert_numeric(:bucketing_key, :get_treatment)
|
101
|
+
end
|
102
|
+
|
103
|
+
return true
|
104
|
+
end
|
105
|
+
|
106
|
+
def valid_split_names?(split_names)
|
107
|
+
if split_names.nil?
|
108
|
+
log_nil(:split_names, :get_treatments)
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
|
112
|
+
unless split_names.is_a? Array
|
113
|
+
SplitIoClient.configuration.logger.warn('get_treatments: split_names must be an Array')
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
|
120
|
+
def valid_track_key?(key)
|
121
|
+
if key.nil?
|
122
|
+
log_nil(:key, :track)
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
unless number_or_string?(key)
|
127
|
+
log_number_or_string(:key, :track)
|
128
|
+
return false
|
129
|
+
end
|
130
|
+
|
131
|
+
if key.is_a? Numeric
|
132
|
+
log_convert_numeric(:key, :track)
|
133
|
+
end
|
134
|
+
|
135
|
+
return true
|
136
|
+
end
|
137
|
+
|
138
|
+
def valid_event_type?(event_type)
|
139
|
+
if event_type.nil?
|
140
|
+
log_nil(:event_type, :track)
|
141
|
+
return false
|
142
|
+
end
|
143
|
+
|
144
|
+
unless string?(event_type)
|
145
|
+
log_string(:event_type, :track)
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
if (event_type.to_s =~ /[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}/).nil?
|
150
|
+
SplitIoClient.configuration.logger.error('track: event_type must adhere to [a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}')
|
151
|
+
return false
|
152
|
+
end
|
153
|
+
|
154
|
+
return true
|
155
|
+
end
|
156
|
+
|
157
|
+
def valid_traffic_type_name?(traffic_type_name)
|
158
|
+
if traffic_type_name.nil?
|
159
|
+
log_nil(:traffic_type_name, :track)
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
|
163
|
+
unless string?(traffic_type_name)
|
164
|
+
log_string(:traffic_type_name, :track)
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
|
168
|
+
if traffic_type_name.empty?
|
169
|
+
SplitIoClient.configuration.logger.error('track: traffic_type_name must not be an empty String')
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
|
173
|
+
return true
|
174
|
+
end
|
175
|
+
|
176
|
+
def valid_value?(value)
|
177
|
+
unless value.is_a?(Numeric) || value.nil?
|
178
|
+
SplitIoClient.configuration.logger.error('track: value must be a number')
|
179
|
+
return false
|
180
|
+
end
|
181
|
+
|
182
|
+
return true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/splitclient-rb.gemspec
CHANGED
@@ -46,11 +46,13 @@ Gem::Specification.new do |spec|
|
|
46
46
|
spec.add_development_dependency 'rspec'
|
47
47
|
spec.add_development_dependency 'rubocop', '0.59.0'
|
48
48
|
spec.add_development_dependency 'simplecov'
|
49
|
+
spec.add_development_dependency 'timecop'
|
49
50
|
spec.add_development_dependency 'webmock'
|
50
51
|
|
51
52
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
52
53
|
spec.add_runtime_dependency 'faraday', '>= 0.8'
|
53
54
|
spec.add_runtime_dependency 'json', '>= 1.8'
|
55
|
+
spec.add_runtime_dependency 'lru_redux'
|
54
56
|
spec.add_runtime_dependency 'net-http-persistent', '~> 2.9'
|
55
57
|
spec.add_runtime_dependency 'redis', '>= 3.2'
|
56
58
|
spec.add_runtime_dependency 'thread_safe', '>= 0.3'
|
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: 5.1.
|
4
|
+
version: 5.1.3.pre.rc1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Split Software
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
name: timecop
|
146
|
+
prerelease: false
|
147
|
+
type: :development
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|
141
155
|
requirements:
|
@@ -192,6 +206,20 @@ dependencies:
|
|
192
206
|
- - ">="
|
193
207
|
- !ruby/object:Gem::Version
|
194
208
|
version: '1.8'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
name: lru_redux
|
216
|
+
prerelease: false
|
217
|
+
type: :runtime
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
195
223
|
- !ruby/object:Gem::Dependency
|
196
224
|
requirement: !ruby/object:Gem::Requirement
|
197
225
|
requirements:
|
@@ -257,6 +285,7 @@ files:
|
|
257
285
|
- lib/murmurhash/murmurhash.jar
|
258
286
|
- lib/murmurhash/murmurhash_mri.rb
|
259
287
|
- lib/splitclient-rb.rb
|
288
|
+
- lib/splitclient-rb/cache/adapters/cache_adapter.rb
|
260
289
|
- lib/splitclient-rb/cache/adapters/memory_adapter.rb
|
261
290
|
- lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb
|
262
291
|
- lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb
|
@@ -321,8 +350,7 @@ files:
|
|
321
350
|
- lib/splitclient-rb/engine/parser/evaluator.rb
|
322
351
|
- lib/splitclient-rb/engine/parser/partition.rb
|
323
352
|
- lib/splitclient-rb/engine/parser/split_adapter.rb
|
324
|
-
- lib/splitclient-rb/exceptions
|
325
|
-
- lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb
|
353
|
+
- lib/splitclient-rb/exceptions.rb
|
326
354
|
- lib/splitclient-rb/localhost_split_factory.rb
|
327
355
|
- lib/splitclient-rb/localhost_utils.rb
|
328
356
|
- lib/splitclient-rb/managers/localhost_split_manager.rb
|
@@ -332,6 +360,7 @@ files:
|
|
332
360
|
- lib/splitclient-rb/split_factory_builder.rb
|
333
361
|
- lib/splitclient-rb/split_logger.rb
|
334
362
|
- lib/splitclient-rb/utilitites.rb
|
363
|
+
- lib/splitclient-rb/validators.rb
|
335
364
|
- lib/splitclient-rb/version.rb
|
336
365
|
- splitclient-rb.gemspec
|
337
366
|
- splitio.yml.example
|