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 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