splitclient-rb 3.1.0.pre.rc5 → 3.1.0.pre.rc6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b87eb417698ad4261d7875bf106cbdd68454f44
4
- data.tar.gz: bf1bbfa658d60391665cbba727552a9bf63a08ec
3
+ metadata.gz: 58d09e0e5a6d94a0a79c0fc5b4cea7a32e7bdd1d
4
+ data.tar.gz: 8bdc8dbc17fd1cfd7e6fa1d2df4445d65638d139
5
5
  SHA512:
6
- metadata.gz: ac75a102f260b28e54a65354593bca386f1c603d0521c2b99b2ace068f76337454dd06834d75de7af22483dbbdacbd66a33e7d3349bfd5bc81326e0a3a8498e6
7
- data.tar.gz: 11d3e31a4fb3adcc0e15dcd42c956e679a557dc2da7a423211ee44f15bc01bc5cc8537a64caee76faeab0cc3a841491d6e5ef8dd370e2daafdbd2d4529807588
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.splitNames()
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
@@ -1,4 +1,5 @@
1
1
  3.1.0
2
+
2
3
  Now supporting Redis as a cache adapter
3
4
 
4
5
  3.0.2
@@ -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[key] = nil
96
+ @map.delete(key)
91
97
  end
92
98
  end
93
99
  end
@@ -53,6 +53,10 @@ module SplitIoClient
53
53
  @redis.keys("#{prefix}*")
54
54
  end
55
55
 
56
+ def multiple_strings(keys)
57
+ Hash[keys.zip(@redis.mget(keys))]
58
+ end
59
+
56
60
  # Bool
57
61
  def set_bool(key, val)
58
62
  @redis.set(key, val.to_s)
@@ -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
- @splits_repository.add_split(split)
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()}): #{data[:segment_names].to_a}") if @config.debug_enabled
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(splits_repository, segments_repository)
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, split_name, default_treatment, attributes = nil)
11
- split = @splits_repository.get_split(split_name)
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 self.class.archived?(split)
13
+ return Treatments::CONTROL if split_model.archived?
14
14
 
15
- matchable?(split) ? match(split, keys, attributes, default_treatment) : default_treatment
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
- memo << build_split_view(name, split) unless Engine::Parser::SplitTreatment.archived?(split)
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 splitNames
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.splitNames
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) if split and !Engine::Parser::SplitTreatment.archived?(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 [string/hash] user id or hash with matching_key/bucketing_key
149
- # @param feature [string] name of the feature that is being validated
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 [Treatment] treatment constant value
152
- def get_treatment(key, feature, attributes = nil)
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 feature: ' + feature)
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 feature.nil?
162
- @config.logger.warn('feature was null for key: ' + key)
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(feature)
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
- result = get_treatment_without_exception_handling({ bucketing_key: bucketing_key, matching_key: matching_key }, feature, attributes)
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, feature, result, (Time.now.to_f * 1000.0))
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
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '3.1.0-rc5'
2
+ VERSION = '3.1.0-rc6'
3
3
  end
@@ -35,4 +35,5 @@ require 'engine/evaluator/splitter'
35
35
  require 'engine/impressions/impressions'
36
36
  require 'engine/metrics/metrics'
37
37
  require 'engine/metrics/binary_search_latency_tracker'
38
+ require 'engine/models/split'
38
39
  require 'splitclient-rb_utilitites'
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.rc5
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-10-29 00:00:00.000000000 Z
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
@@ -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