splitclient-rb 3.1.0.pre.rc5 → 3.1.0.pre.rc6
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 +8 -2
- data/NEWS +1 -0
- data/lib/cache/adapters/memory_adapter.rb +7 -1
- data/lib/cache/adapters/redis_adapter.rb +4 -0
- data/lib/cache/repositories/splits_repository.rb +24 -1
- data/lib/cache/stores/split_store.rb +28 -2
- data/lib/engine/models/split.rb +19 -0
- data/lib/engine/parser/split_treatment.rb +6 -14
- data/lib/splitclient-rb/split_factory.rb +38 -41
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +1 -0
- metadata +3 -3
- data/lib/engine/parser/split.rb +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58d09e0e5a6d94a0a79c0fc5b4cea7a32e7bdd1d
|
4
|
+
data.tar.gz: 8bdc8dbc17fd1cfd7e6fa1d2df4445d65638d139
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 584a396de37a5e5783f958bd6e3f93473af6b9bfc033aa47099997e9bd0a695ed05aca65e0880d5f43897928f9618cd175bd1a12ec8cb0a34b0fc7d4e799d57e
|
7
|
+
data.tar.gz: ec4bfc56c8fa786ba28d547d0a8198f6c4589e8ad89068f4a1fd64063bca796d7657d5e012f1b30da7366708044c221f6870a2942c25a1268d861ef74a30438a
|
data/CHANGES.txt
CHANGED
@@ -1,26 +1,32 @@
|
|
1
1
|
3.1.0
|
2
|
-
- Add RedisAdapter
|
3
|
-
- adds manager.
|
2
|
+
- Add RedisAdapter
|
3
|
+
- adds manager.split_names()
|
4
4
|
|
5
5
|
3.0.3
|
6
|
+
|
6
7
|
- Fix nil ref in manager
|
7
8
|
|
8
9
|
3.0.2
|
10
|
+
|
9
11
|
- add ability to provide different bucketing/matching keys
|
10
12
|
|
11
13
|
3.0.1
|
14
|
+
|
12
15
|
- fix segments not deleting from the cache
|
13
16
|
|
14
17
|
3.0.0
|
18
|
+
|
15
19
|
- add new caching interface
|
16
20
|
- add replaceable adapters to store cache in
|
17
21
|
- add first cache adapter: MemoryAdapter
|
18
22
|
- refactoring
|
19
23
|
|
20
24
|
2.0.1
|
25
|
+
|
21
26
|
- Supress warnings cause by Net::HTTP when it already exists.
|
22
27
|
|
23
28
|
2.0.0
|
29
|
+
|
24
30
|
- Add Factory for creation of Client and Manager interface.
|
25
31
|
|
26
32
|
1.0.4
|
data/NEWS
CHANGED
@@ -58,6 +58,12 @@ module SplitIoClient
|
|
58
58
|
@map.keys.select { |str| str.start_with? prefix }
|
59
59
|
end
|
60
60
|
|
61
|
+
def multiple_strings(keys)
|
62
|
+
keys.each_with_object({}) do |key, memo|
|
63
|
+
memo[key] = string(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
61
67
|
# Bool
|
62
68
|
def set_bool(key, val)
|
63
69
|
@map[key] = val
|
@@ -87,7 +93,7 @@ module SplitIoClient
|
|
87
93
|
end
|
88
94
|
|
89
95
|
def delete(key)
|
90
|
-
@map
|
96
|
+
@map.delete(key)
|
91
97
|
end
|
92
98
|
end
|
93
99
|
end
|
@@ -4,6 +4,8 @@ module SplitIoClient
|
|
4
4
|
module Cache
|
5
5
|
module Repositories
|
6
6
|
class SplitsRepository < Repository
|
7
|
+
SPLITS_SLICE = 10
|
8
|
+
|
7
9
|
def initialize(adapter)
|
8
10
|
@adapter = adapter
|
9
11
|
|
@@ -19,10 +21,27 @@ module SplitIoClient
|
|
19
21
|
@adapter.delete(namespace_key("split.#{name}"))
|
20
22
|
end
|
21
23
|
|
24
|
+
def get_splits(names, slice = SPLITS_SLICE)
|
25
|
+
splits = {}
|
26
|
+
|
27
|
+
names.each_slice(slice) do |splits_slice|
|
28
|
+
splits.merge!(
|
29
|
+
@adapter
|
30
|
+
.multiple_strings(splits_slice.map { |name| namespace_key("split.#{name}") })
|
31
|
+
.map { |name, data| [name.gsub(namespace_key('split.'), ''), data] }.to_h
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
splits.map do |name, data|
|
36
|
+
parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
|
37
|
+
[name.to_sym, parsed_data]
|
38
|
+
end.to_h
|
39
|
+
end
|
40
|
+
|
22
41
|
def get_split(name)
|
23
42
|
split = @adapter.string(namespace_key("split.#{name}"))
|
24
43
|
|
25
|
-
JSON.parse(split, symbolize_names: true)
|
44
|
+
JSON.parse(split, symbolize_names: true) if split
|
26
45
|
end
|
27
46
|
|
28
47
|
def splits
|
@@ -57,6 +76,10 @@ module SplitIoClient
|
|
57
76
|
@adapter.add_to_set(namespace_key('segments.registered'), name)
|
58
77
|
end
|
59
78
|
end
|
79
|
+
|
80
|
+
def exists?(name)
|
81
|
+
@adapter.exists?(namespace_key("split.#{name}"))
|
82
|
+
end
|
60
83
|
end
|
61
84
|
end
|
62
85
|
end
|
@@ -32,13 +32,13 @@ module SplitIoClient
|
|
32
32
|
data = splits_since(@splits_repository.get_change_number)
|
33
33
|
|
34
34
|
data[:splits] && data[:splits].each do |split|
|
35
|
-
|
35
|
+
add_split_unless_archived(split)
|
36
36
|
end
|
37
37
|
|
38
38
|
@splits_repository.set_segment_names(data[:segment_names])
|
39
39
|
@splits_repository.set_change_number(data[:till])
|
40
40
|
|
41
|
-
@config.logger.debug("segments seen(#{data[:segment_names].length
|
41
|
+
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
|
42
42
|
|
43
43
|
if @config.block_until_ready && !@sdk_blocker.ready?
|
44
44
|
@sdk_blocker.splits_ready!
|
@@ -58,6 +58,32 @@ module SplitIoClient
|
|
58
58
|
def splits_since(since)
|
59
59
|
SplitIoClient::Api::Splits.new(@api_key, @config, @metrics).since(since)
|
60
60
|
end
|
61
|
+
|
62
|
+
def add_split_unless_archived(split)
|
63
|
+
if split_model(split).archived?
|
64
|
+
@config.logger.debug("Seeing archived split #{split[:name]}") if @config.debug_enabled
|
65
|
+
|
66
|
+
remove_archived_split(split)
|
67
|
+
else
|
68
|
+
store_split(split)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_archived_split(split)
|
73
|
+
@config.logger.debug("removing split from store(#{split})") if @config.debug_enabled
|
74
|
+
|
75
|
+
@splits_repository.remove_split(split[:name])
|
76
|
+
end
|
77
|
+
|
78
|
+
def store_split(split)
|
79
|
+
@config.logger.debug("storing split (#{split[:name]})") if @config.debug_enabled
|
80
|
+
|
81
|
+
@splits_repository.add_split(split)
|
82
|
+
end
|
83
|
+
|
84
|
+
def split_model(split)
|
85
|
+
Engine::Models::Split.new(split)
|
86
|
+
end
|
61
87
|
end
|
62
88
|
end
|
63
89
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Engine
|
3
|
+
module Models
|
4
|
+
class Split
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def matchable?
|
10
|
+
@data && @data[:status] == 'ACTIVE' && @data[:killed] == false
|
11
|
+
end
|
12
|
+
|
13
|
+
def archived?
|
14
|
+
@data && @data[:status] == 'ARCHIVED'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,17 +2,17 @@ module SplitIoClient
|
|
2
2
|
module Engine
|
3
3
|
module Parser
|
4
4
|
class SplitTreatment
|
5
|
-
def initialize(
|
6
|
-
@splits_repository = splits_repository
|
5
|
+
def initialize(segments_repository)
|
7
6
|
@segments_repository = segments_repository
|
8
7
|
end
|
9
8
|
|
10
|
-
def call(keys,
|
11
|
-
|
9
|
+
def call(keys, split, attributes = nil)
|
10
|
+
split_model = Models::Split.new(split)
|
11
|
+
default_treatment = split[:defaultTreatment]
|
12
12
|
|
13
|
-
return Treatments::CONTROL if
|
13
|
+
return Treatments::CONTROL if split_model.archived?
|
14
14
|
|
15
|
-
matchable?
|
15
|
+
split_model.matchable? ? match(split, keys, attributes, default_treatment) : default_treatment
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -51,14 +51,6 @@ module SplitIoClient
|
|
51
51
|
final_matcher
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
55
|
-
def matchable?(split)
|
56
|
-
!split.nil? && split[:status] == 'ACTIVE' && split[:killed] == false
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.archived?(split)
|
60
|
-
!split.nil? && split[:status] == 'ARCHIVED'
|
61
|
-
end
|
62
54
|
end
|
63
55
|
end
|
64
56
|
end
|
@@ -53,7 +53,9 @@ module SplitIoClient
|
|
53
53
|
return if @splits_repository.nil?
|
54
54
|
|
55
55
|
@splits_repository.splits.each_with_object([]) do |(name, split), memo|
|
56
|
-
|
56
|
+
split_model = Engine::Models::Split.new(split)
|
57
|
+
|
58
|
+
memo << build_split_view(name, split) unless split_model.archived?
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -61,18 +63,18 @@ module SplitIoClient
|
|
61
63
|
# method to get the list of just split names. Ideal for ietrating and calling client.get_treatment
|
62
64
|
#
|
63
65
|
# @returns [object] array of split names (String)
|
64
|
-
def
|
66
|
+
def split_names
|
65
67
|
if @localhost_mode
|
66
68
|
local_feature_names = []
|
67
69
|
@localhost_mode_features.each do |split|
|
68
|
-
local_feature_names << split[:feature]
|
70
|
+
local_feature_names << split[:feature]
|
69
71
|
end
|
70
72
|
return local_feature_names
|
71
73
|
end
|
72
74
|
|
73
75
|
return if @splits_repository.nil?
|
74
76
|
|
75
|
-
@splits_repository.
|
77
|
+
@splits_repository.split_names
|
76
78
|
end
|
77
79
|
|
78
80
|
#
|
@@ -86,10 +88,9 @@ module SplitIoClient
|
|
86
88
|
end
|
87
89
|
|
88
90
|
if @splits_repository
|
89
|
-
|
90
91
|
split = @splits_repository.get_split(split_name)
|
91
92
|
|
92
|
-
build_split_view(split_name, split)
|
93
|
+
build_split_view(split_name, split) unless split.nil? or split_model(split).archived?
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
@@ -106,6 +107,11 @@ module SplitIoClient
|
|
106
107
|
}
|
107
108
|
end
|
108
109
|
|
110
|
+
private
|
111
|
+
|
112
|
+
def split_model(split)
|
113
|
+
split_model = Engine::Models::Split.new(split)
|
114
|
+
end
|
109
115
|
end
|
110
116
|
|
111
117
|
|
@@ -142,35 +148,49 @@ module SplitIoClient
|
|
142
148
|
end
|
143
149
|
end
|
144
150
|
|
151
|
+
def get_treatments(key, split_names, attributes = nil)
|
152
|
+
@splits_repository.get_splits(split_names).each_with_object({}) do |(name, data), memo|
|
153
|
+
memo.merge!(name => get_treatment(key, name, attributes, data))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
145
157
|
#
|
146
158
|
# obtains the treatment for a given feature
|
147
159
|
#
|
148
|
-
# @param key [
|
149
|
-
# @param
|
160
|
+
# @param key [String/Hash] user id or hash with matching_key/bucketing_key
|
161
|
+
# @param split_name [String/Array] name of the feature that is being validated or array of them
|
150
162
|
#
|
151
|
-
# @return [
|
152
|
-
def get_treatment(key,
|
163
|
+
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
164
|
+
def get_treatment(key, split_name, attributes = nil, split_data = nil)
|
153
165
|
bucketing_key, matching_key = keys_from_key(key)
|
154
166
|
bucketing_key = matching_key if bucketing_key.nil?
|
155
167
|
|
156
168
|
if matching_key.nil?
|
157
|
-
@config.logger.warn('matching_key was null for
|
169
|
+
@config.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
|
158
170
|
return Treatments::CONTROL
|
159
171
|
end
|
160
172
|
|
161
|
-
if
|
162
|
-
@config.logger.warn('
|
173
|
+
if split_name.nil?
|
174
|
+
@config.logger.warn('split_name was null for key: ' + key)
|
163
175
|
return Treatments::CONTROL
|
164
176
|
end
|
165
177
|
|
166
178
|
if is_localhost_mode?
|
167
|
-
result = get_localhost_treatment(
|
179
|
+
result = get_localhost_treatment(split_name)
|
168
180
|
else
|
169
181
|
start = Time.now
|
170
182
|
result = nil
|
171
183
|
|
172
184
|
begin
|
173
|
-
|
185
|
+
split = split_data ? split_data : @splits_repository.get_split(split_name)
|
186
|
+
|
187
|
+
result = if split.nil?
|
188
|
+
Treatments::CONTROL
|
189
|
+
else
|
190
|
+
SplitIoClient::Engine::Parser::SplitTreatment.new(@segments_repository).call(
|
191
|
+
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
192
|
+
)
|
193
|
+
end
|
174
194
|
rescue StandardError => error
|
175
195
|
@config.log_found_exception(__method__.to_s, error)
|
176
196
|
end
|
@@ -178,7 +198,7 @@ module SplitIoClient
|
|
178
198
|
result = result.nil? ? Treatments::CONTROL : result
|
179
199
|
|
180
200
|
begin
|
181
|
-
@adapter.impressions.log(matching_key,
|
201
|
+
@adapter.impressions.log(matching_key, split_name, result, (Time.now.to_f * 1000.0))
|
182
202
|
latency = (Time.now - start) * 1000.0
|
183
203
|
rescue StandardError => error
|
184
204
|
@config.log_found_exception(__method__.to_s, error)
|
@@ -198,28 +218,6 @@ module SplitIoClient
|
|
198
218
|
end
|
199
219
|
end
|
200
220
|
|
201
|
-
#
|
202
|
-
# auxiliary method to get the treatments avoding exceptions
|
203
|
-
#
|
204
|
-
# @param key [string/hash] user id or hash with matching_key/bucketing_key
|
205
|
-
# @param feature [string] name of the feature that is being validated
|
206
|
-
#
|
207
|
-
# @return [Treatment] tretment constant value
|
208
|
-
def get_treatment_without_exception_handling(key, feature, attributes = nil)
|
209
|
-
|
210
|
-
split = @splits_repository.get_split(feature)
|
211
|
-
|
212
|
-
if split.nil?
|
213
|
-
Treatments::CONTROL
|
214
|
-
else
|
215
|
-
default_treatment = split[:defaultTreatment]
|
216
|
-
|
217
|
-
SplitIoClient::Engine::Parser::SplitTreatment
|
218
|
-
.new(@splits_repository, @segments_repository)
|
219
|
-
.call(key, feature, default_treatment, attributes)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
221
|
#
|
224
222
|
# method that returns the sdk gem version
|
225
223
|
#
|
@@ -228,6 +226,8 @@ module SplitIoClient
|
|
228
226
|
'RubyClientSDK-'+SplitIoClient::VERSION
|
229
227
|
end
|
230
228
|
|
229
|
+
private
|
230
|
+
|
231
231
|
#
|
232
232
|
# method to check if the sdk is running in localhost mode based on api key
|
233
233
|
#
|
@@ -261,9 +261,6 @@ module SplitIoClient
|
|
261
261
|
localhost_result = treatment[:treatment] if !treatment.nil?
|
262
262
|
localhost_result
|
263
263
|
end
|
264
|
-
|
265
|
-
private :get_treatment_without_exception_handling, :is_localhost_mode?,
|
266
|
-
:load_localhost_mode_features, :get_localhost_treatment
|
267
264
|
end
|
268
265
|
|
269
266
|
private_constant :SplitClient
|
data/lib/splitclient-rb.rb
CHANGED
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.1.0.pre.
|
4
|
+
version: 3.1.0.pre.rc6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Split Software
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -275,9 +275,9 @@ files:
|
|
275
275
|
- lib/engine/matchers/whitelist_matcher.rb
|
276
276
|
- lib/engine/metrics/binary_search_latency_tracker.rb
|
277
277
|
- lib/engine/metrics/metrics.rb
|
278
|
+
- lib/engine/models/split.rb
|
278
279
|
- lib/engine/parser/condition.rb
|
279
280
|
- lib/engine/parser/partition.rb
|
280
|
-
- lib/engine/parser/split.rb
|
281
281
|
- lib/engine/parser/split_adapter.rb
|
282
282
|
- lib/engine/parser/split_treatment.rb
|
283
283
|
- lib/engine/partitions/treatments.rb
|
data/lib/engine/parser/split.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
module SplitIoClient
|
2
|
-
#
|
3
|
-
# acts as dto for a split structure
|
4
|
-
#
|
5
|
-
class Split < NoMethodError
|
6
|
-
#
|
7
|
-
# definition of the split
|
8
|
-
#
|
9
|
-
# @returns [object] split values
|
10
|
-
attr_accessor :data
|
11
|
-
|
12
|
-
def initialize(split)
|
13
|
-
@data = split
|
14
|
-
@conditions = set_conditions
|
15
|
-
end
|
16
|
-
|
17
|
-
#
|
18
|
-
# @returns [string] name of the split
|
19
|
-
def name
|
20
|
-
@data[:name]
|
21
|
-
end
|
22
|
-
|
23
|
-
#
|
24
|
-
# @returns [int] seed value of the split
|
25
|
-
def seed
|
26
|
-
@data[:seed]
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# @returns [string] status value of the split
|
31
|
-
def status
|
32
|
-
@data[:status]
|
33
|
-
end
|
34
|
-
|
35
|
-
#
|
36
|
-
# @returns [string] killed value of the split
|
37
|
-
def killed?
|
38
|
-
@data[:killed]
|
39
|
-
end
|
40
|
-
|
41
|
-
#
|
42
|
-
# @returns [object] array of condition objects for this split
|
43
|
-
def conditions
|
44
|
-
@conditions
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# @return [boolean] true if the condition is empty false otherwise
|
49
|
-
def empty?
|
50
|
-
@data.empty?
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
# converts the conditions data into an array of condition objects for this split
|
55
|
-
#
|
56
|
-
# @return [object] array of condition objects
|
57
|
-
def set_conditions
|
58
|
-
conditions_list = []
|
59
|
-
@data[:conditions].each do |c|
|
60
|
-
condition = SplitIoClient::Condition.new(c)
|
61
|
-
conditions_list << condition
|
62
|
-
end
|
63
|
-
conditions_list
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_h
|
67
|
-
{
|
68
|
-
name: name,
|
69
|
-
seed: seed,
|
70
|
-
status: status,
|
71
|
-
killed: killed?,
|
72
|
-
conditions: conditions
|
73
|
-
}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|