splitclient-rb 4.1.0 → 4.2.0

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +8 -0
  3. data/NEWS +6 -0
  4. data/README.md +5 -18
  5. data/lib/splitclient-rb.rb +65 -54
  6. data/lib/{cache → splitclient-rb/cache}/adapters/memory_adapter.rb +0 -0
  7. data/lib/{cache → splitclient-rb/cache}/adapters/memory_adapters/map_adapter.rb +0 -0
  8. data/lib/{cache → splitclient-rb/cache}/adapters/memory_adapters/queue_adapter.rb +0 -0
  9. data/lib/{cache → splitclient-rb/cache}/adapters/redis_adapter.rb +0 -0
  10. data/lib/{cache → splitclient-rb/cache}/repositories/impressions/memory_repository.rb +0 -0
  11. data/lib/{cache → splitclient-rb/cache}/repositories/impressions/redis_repository.rb +0 -0
  12. data/lib/{cache → splitclient-rb/cache}/repositories/impressions_repository.rb +0 -0
  13. data/lib/{cache → splitclient-rb/cache}/repositories/metrics/memory_repository.rb +0 -0
  14. data/lib/{cache → splitclient-rb/cache}/repositories/metrics/redis_repository.rb +0 -0
  15. data/lib/{cache → splitclient-rb/cache}/repositories/metrics_repository.rb +0 -0
  16. data/lib/{cache → splitclient-rb/cache}/repositories/repository.rb +0 -0
  17. data/lib/{cache → splitclient-rb/cache}/repositories/segments_repository.rb +0 -0
  18. data/lib/{cache → splitclient-rb/cache}/repositories/splits_repository.rb +0 -0
  19. data/lib/{cache → splitclient-rb/cache}/senders/impressions_formatter.rb +0 -0
  20. data/lib/{cache → splitclient-rb/cache}/senders/impressions_sender.rb +1 -1
  21. data/lib/{cache → splitclient-rb/cache}/senders/metrics_sender.rb +1 -1
  22. data/lib/{cache → splitclient-rb/cache}/stores/sdk_blocker.rb +0 -0
  23. data/lib/{cache → splitclient-rb/cache}/stores/segment_store.rb +0 -0
  24. data/lib/{cache → splitclient-rb/cache}/stores/split_store.rb +0 -0
  25. data/lib/splitclient-rb/clients/localhost_split_client.rb +3 -3
  26. data/lib/splitclient-rb/clients/split_client.rb +28 -25
  27. data/lib/{engine → splitclient-rb/engine}/api/client.rb +4 -4
  28. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +55 -0
  29. data/lib/{engine → splitclient-rb/engine}/api/impressions.rb +0 -0
  30. data/lib/{engine → splitclient-rb/engine}/api/metrics.rb +0 -0
  31. data/lib/{engine → splitclient-rb/engine}/api/segments.rb +0 -0
  32. data/lib/{engine → splitclient-rb/engine}/api/splits.rb +0 -0
  33. data/lib/{engine → splitclient-rb/engine}/evaluator/splitter.rb +2 -2
  34. data/lib/{engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +1 -1
  35. data/lib/{engine → splitclient-rb/engine}/matchers/between_matcher.rb +2 -2
  36. data/lib/{engine → splitclient-rb/engine}/matchers/combiners.rb +0 -0
  37. data/lib/{engine → splitclient-rb/engine}/matchers/combining_matcher.rb +10 -8
  38. data/lib/{engine → splitclient-rb/engine}/matchers/contains_all_matcher.rb +1 -1
  39. data/lib/{engine → splitclient-rb/engine}/matchers/contains_any_matcher.rb +1 -1
  40. data/lib/{engine → splitclient-rb/engine}/matchers/contains_matcher.rb +1 -1
  41. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +16 -0
  42. data/lib/{engine → splitclient-rb/engine}/matchers/ends_with_matcher.rb +1 -1
  43. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +21 -0
  44. data/lib/{engine → splitclient-rb/engine}/matchers/equal_to_matcher.rb +2 -2
  45. data/lib/{engine → splitclient-rb/engine}/matchers/equal_to_set_matcher.rb +1 -1
  46. data/lib/{engine → splitclient-rb/engine}/matchers/greater_than_or_equal_to_matcher.rb +2 -2
  47. data/lib/{engine → splitclient-rb/engine}/matchers/less_than_or_equal_to_matcher.rb +2 -2
  48. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +23 -0
  49. data/lib/{engine → splitclient-rb/engine}/matchers/negation_matcher.rb +2 -2
  50. data/lib/{engine → splitclient-rb/engine}/matchers/part_of_set_matcher.rb +1 -1
  51. data/lib/{engine → splitclient-rb/engine}/matchers/set_matcher.rb +0 -0
  52. data/lib/{engine → splitclient-rb/engine}/matchers/starts_with_matcher.rb +1 -1
  53. data/lib/{engine → splitclient-rb/engine}/matchers/user_defined_segment_matcher.rb +2 -2
  54. data/lib/{engine → splitclient-rb/engine}/matchers/whitelist_matcher.rb +2 -2
  55. data/lib/{engine → splitclient-rb/engine}/metrics/binary_search_latency_tracker.rb +0 -0
  56. data/lib/{engine → splitclient-rb/engine}/metrics/metrics.rb +0 -0
  57. data/lib/splitclient-rb/engine/models/label.rb +7 -0
  58. data/lib/{engine → splitclient-rb/engine}/models/split.rb +0 -0
  59. data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
  60. data/lib/{engine → splitclient-rb/engine}/parser/condition.rb +21 -0
  61. data/lib/{engine/parser/split_treatment.rb → splitclient-rb/engine/parser/evaluator.rb} +29 -13
  62. data/lib/{engine → splitclient-rb/engine}/parser/partition.rb +0 -0
  63. data/lib/{engine → splitclient-rb/engine}/parser/split_adapter.rb +0 -0
  64. data/lib/{exceptions → splitclient-rb/exceptions}/sdk_blocker_timeout_expired_exception.rb +0 -0
  65. data/lib/splitclient-rb/localhost_utils.rb +1 -1
  66. data/lib/splitclient-rb/split_config.rb +7 -4
  67. data/lib/splitclient-rb/utilitites.rb +41 -0
  68. data/lib/splitclient-rb/version.rb +1 -1
  69. data/splitclient-rb.gemspec +6 -8
  70. metadata +76 -100
  71. data/lib/engine/models/label.rb +0 -13
  72. data/lib/engine/partitions/treatments.rb +0 -36
  73. data/lib/splitclient-rb_utilitites.rb +0 -39
@@ -0,0 +1,55 @@
1
+ require 'faraday'
2
+
3
+ module SplitIoClient
4
+ module FaradayMiddleware
5
+ class Gzip < Faraday::Middleware
6
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
7
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
8
+ CONTENT_LENGTH = 'Content-Length'.freeze
9
+ SUPPORTED_ENCODINGS = 'gzip,deflate'.freeze
10
+ RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
11
+
12
+ def call(env)
13
+ env[:request_headers][ACCEPT_ENCODING] ||= SUPPORTED_ENCODINGS
14
+ @app.call(env).on_complete do |response_env|
15
+ case response_env[:response_headers][CONTENT_ENCODING]
16
+ when 'gzip'
17
+ reset_body(response_env, &method(:uncompress_gzip))
18
+ when 'deflate'
19
+ reset_body(response_env, &method(:inflate))
20
+ end
21
+ end
22
+ end
23
+
24
+ def reset_body(env)
25
+ env[:body] = yield(env[:body])
26
+ env[:response_headers].delete(CONTENT_ENCODING)
27
+ env[:response_headers][CONTENT_LENGTH] = env[:body].length
28
+ end
29
+
30
+ def uncompress_gzip(body)
31
+ io = StringIO.new(body)
32
+ gzip_reader = if RUBY_ENCODING
33
+ Zlib::GzipReader.new(io, :encoding => 'ASCII-8BIT')
34
+ else
35
+ Zlib::GzipReader.new(io)
36
+ end
37
+ gzip_reader.read
38
+ end
39
+
40
+ def inflate(body)
41
+ # Inflate as a DEFLATE (RFC 1950+RFC 1951) stream
42
+ Zlib::Inflate.inflate(body)
43
+ rescue Zlib::DataError
44
+ # Fall back to inflating as a "raw" deflate stream which
45
+ # Microsoft servers return
46
+ inflate = Zlib::Inflate.new(-Zlib::MAX_WBITS)
47
+ begin
48
+ inflate.inflate(body)
49
+ ensure
50
+ inflate.close
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
File without changes
@@ -29,7 +29,7 @@ module SplitIoClient
29
29
  legacy = (legacy_algo == 1 || legacy_algo == nil) ? true : false
30
30
 
31
31
  if partitions.empty?
32
- return Treatments::CONTROL
32
+ return SplitIoClient::Engine::Models::Treatment::CONTROL
33
33
  end
34
34
 
35
35
  if hundred_percent_one_treatment?(partitions)
@@ -101,7 +101,7 @@ module SplitIoClient
101
101
  end
102
102
  end
103
103
 
104
- return Treatments::CONTROL
104
+ return SplitIoClient::Engine::Models::Treatment::CONTROL
105
105
  end
106
106
 
107
107
  #
@@ -15,7 +15,7 @@ module SplitIoClient
15
15
  # @param key [string] key value to be matched
16
16
  #
17
17
  # @return [boolean] true for all instances
18
- def match?(_key, _attributes)
18
+ def match?(_matching_key, _bucketing_key, _evaluator, _attributes)
19
19
  true
20
20
  end
21
21
 
@@ -12,7 +12,7 @@ module SplitIoClient
12
12
  @end_value = get_formatted_value attribute_hash[:end_value], true
13
13
  end
14
14
 
15
- def match?(key, attributes)
15
+ def match?(_matching_key, _bucketing_key, _evaluator, attributes)
16
16
  matches = false
17
17
  if (!attributes.nil? && attributes.key?(@attribute.to_sym))
18
18
  param_value = get_formatted_value(attributes[@attribute.to_sym])
@@ -41,7 +41,7 @@ module SplitIoClient
41
41
  return value
42
42
  when "DATETIME"
43
43
  value = value/1000 if is_sdk_data
44
- return ::Utilities.to_milis_zero_out_from_seconds value
44
+ return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
45
45
  else
46
46
  @logger.error('Invalid data type')
47
47
  end
@@ -1,5 +1,3 @@
1
- require 'engine/matchers/combiners'
2
-
3
1
  module SplitIoClient
4
2
  #
5
3
  # class to implement the combining matcher
@@ -31,14 +29,14 @@ module SplitIoClient
31
29
  # @param key [string] key value to be matched
32
30
  #
33
31
  # @return [boolean] match value for combiner delegates
34
- def match?(key, attributes)
32
+ def match?(matching_key, bucketing_key, evaluator, attributes)
35
33
  if @matcher_list.empty?
36
34
  return false
37
35
  end
38
36
 
39
37
  case @combiner
40
38
  when Combiners::AND
41
- return and_eval(key, attributes)
39
+ return and_eval(matching_key, bucketing_key, evaluator, attributes)
42
40
  else
43
41
  @logger.error('Invalid combiner type')
44
42
  return false
@@ -49,14 +47,18 @@ module SplitIoClient
49
47
  # auxiliary method to evaluate each of the matchers within the combiner
50
48
  #
51
49
  # @param key [string] key value to be matched
50
+ # @param evaluator [Evaluator] used in dependency_matcher
51
+ # @param attributes [hash] attributes to pass to the treatment class
52
52
  #
53
53
  # @return [boolean] match value for combiner delegates
54
- def and_eval(key, attributes)
55
- result = true
54
+ def and_eval(matching_key, bucketing_key, evaluator, attributes)
56
55
  @matcher_list.each do |delegate|
57
- result &= delegate.match? key, attributes
56
+ matched = delegate.match?(matching_key, bucketing_key, evaluator, attributes)
57
+
58
+ return false unless matched
58
59
  end
59
- result
60
+
61
+ true
60
62
  end
61
63
 
62
64
  #
@@ -8,7 +8,7 @@ module SplitIoClient
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(_key, data)
11
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
12
12
  return false if @remote_set.empty?
13
13
 
14
14
  @remote_set.subset? local_set(data, @attribute)
@@ -8,7 +8,7 @@ module SplitIoClient
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(_key, data)
11
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
12
12
  local_set(data, @attribute).intersect? @remote_set
13
13
  end
14
14
  end
@@ -9,7 +9,7 @@ module SplitIoClient
9
9
  @substr_list = substr_list
10
10
  end
11
11
 
12
- def match?(_key, data)
12
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
13
13
  value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
14
 
15
15
  return false if @substr_list.empty?
@@ -0,0 +1,16 @@
1
+ module SplitIoClient
2
+ class DependencyMatcher
3
+ def self.matcher_type
4
+ 'IN_SPLIT_TREATMENT'.freeze
5
+ end
6
+
7
+ def initialize(split, treatments)
8
+ @split = split
9
+ @treatments = treatments
10
+ end
11
+
12
+ def match?(matching_key, bucketing_key, evaluator, attributes)
13
+ @treatments.include? evaluator.call({ matching_key: matching_key, bucketing_key: bucketing_key }, @split, attributes)[:treatment]
14
+ end
15
+ end
16
+ end
@@ -9,7 +9,7 @@ module SplitIoClient
9
9
  @suffix_list = suffix_list
10
10
  end
11
11
 
12
- def match?(_key, data)
12
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
13
13
  value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
14
 
15
15
  return false if @suffix_list.empty?
@@ -0,0 +1,21 @@
1
+ module SplitIoClient
2
+ class EqualToBooleanMatcher
3
+ def self.matcher_type
4
+ 'EQUAL_TO_BOOLEAN'.freeze
5
+ end
6
+
7
+ def initialize(attribute, boolean)
8
+ @attribute = attribute
9
+ @boolean = boolean
10
+ end
11
+
12
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
13
+ value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
+
15
+ value = false if value.to_s.downcase == 'false'
16
+ value = true if value.to_s.downcase == 'true'
17
+
18
+ value == @boolean
19
+ end
20
+ end
21
+ end
@@ -11,7 +11,7 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(key, attributes)
14
+ def match?(_matching_key, _bucketing_key, _evaluator, attributes)
15
15
  matches = false
16
16
  if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
17
  param_value = get_formatted_value(attributes[@attribute.to_sym])
@@ -38,7 +38,7 @@ module SplitIoClient
38
38
  return value
39
39
  when "DATETIME"
40
40
  value = value/1000 if is_sdk_data
41
- return ::Utilities.to_milis_zero_out_from_hour value
41
+ return SplitIoClient::Utilities.to_milis_zero_out_from_hour value
42
42
  else
43
43
  @logger.error('Invalid data type')
44
44
  end
@@ -8,7 +8,7 @@ module SplitIoClient
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(_key, data)
11
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
12
12
  local_set(data, @attribute) == @remote_set
13
13
  end
14
14
  end
@@ -11,7 +11,7 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(key, attributes)
14
+ def match?(_matching_key, _bucketing_key, _evaluator, attributes)
15
15
  matches = false
16
16
  if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
17
  param_value = get_formatted_value(attributes[@attribute.to_sym])
@@ -38,7 +38,7 @@ module SplitIoClient
38
38
  return value
39
39
  when "DATETIME"
40
40
  value = value/1000 if is_sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
41
- return ::Utilities.to_milis_zero_out_from_seconds value
41
+ return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
42
42
  else
43
43
  @logger.error('Invalid data type')
44
44
  end
@@ -11,7 +11,7 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(key, attributes)
14
+ def match?(_matching_key, _bucketing_key, _evaluator, attributes)
15
15
  matches = false
16
16
  if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
17
  param_value = get_formatted_value(attributes[@attribute.to_sym])
@@ -38,7 +38,7 @@ module SplitIoClient
38
38
  return value
39
39
  when "DATETIME"
40
40
  value = value/1000 if is_sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
41
- return ::Utilities.to_milis_zero_out_from_seconds value
41
+ return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
42
42
  else
43
43
  @logger.error('Invalid data type')
44
44
  end
@@ -0,0 +1,23 @@
1
+ module SplitIoClient
2
+ class MatchesStringMatcher
3
+ def self.matcher_type
4
+ 'MATCHES_STRING'.freeze
5
+ end
6
+
7
+ def initialize(attribute, regexp_string)
8
+ @attribute = attribute
9
+ @regexp_string = regexp_string
10
+ end
11
+
12
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
13
+ value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
+
15
+ if @regexp_string.is_a? Regexp
16
+ (value =~ @regexp_string) != nil
17
+ else
18
+ # String here
19
+ value == @regexp_string
20
+ end
21
+ end
22
+ end
23
+ end
@@ -19,8 +19,8 @@ module SplitIoClient
19
19
  # @param key [string] key value to be matched
20
20
  #
21
21
  # @return [boolean] evaluation of the negation matcher
22
- def match?(key, attributes)
23
- !@matcher.match?(key, attributes)
22
+ def match?(matching_key, bucketing_key, evaluator, attributes)
23
+ !@matcher.match?(matching_key, bucketing_key, evaluator, attributes)
24
24
  end
25
25
 
26
26
  #
@@ -8,7 +8,7 @@ module SplitIoClient
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(_key, data)
11
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
12
12
  @local_set = local_set(data, @attribute)
13
13
 
14
14
  return false if @local_set.empty?
@@ -9,7 +9,7 @@ module SplitIoClient
9
9
  @prefix_list = prefix_list
10
10
  end
11
11
 
12
- def match?(_key, data)
12
+ def match?(_matching_key, _bucketing_key, _evaluator, data)
13
13
  value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
14
 
15
15
  return false if @prefix_list.empty?
@@ -19,8 +19,8 @@ module SplitIoClient
19
19
  # @param key [string] key value to be matched
20
20
  #
21
21
  # @return [boolean] evaluation of the key against the segment
22
- def match?(key, attributes)
23
- @segments_repository.in_segment?(@segment_name, key)
22
+ def match?(matching_key, _bucketing_key, _evaluator, attributes)
23
+ @segments_repository.in_segment?(@segment_name, matching_key)
24
24
  end
25
25
 
26
26
  #
@@ -20,10 +20,10 @@ module SplitIoClient
20
20
  end
21
21
  end
22
22
 
23
- def match?(key, whitelist_data)
23
+ def match?(matching_key, _bucketing_key, _evaluator, whitelist_data)
24
24
  matches = false
25
25
  if !(@matcher_type == "ATTR_WHITELIST")
26
- matches = @whitelist.include?(key)
26
+ matches = @whitelist.include?(matching_key)
27
27
  else
28
28
  if (!whitelist_data.nil? && whitelist_data.key?(@attribute.to_sym))
29
29
  value = whitelist_data[@attribute.to_sym]
@@ -0,0 +1,7 @@
1
+ class SplitIoClient::Engine::Models::Label
2
+ ARCHIVED = 'archived'.freeze
3
+ NO_RULE_MATCHED = 'no rule matched'.freeze
4
+ EXCEPTION = 'exception'.freeze
5
+ KILLED = 'killed'.freeze
6
+ NOT_IN_SPLIT = 'not in split'.freeze
7
+ end
@@ -0,0 +1,3 @@
1
+ class SplitIoClient::Engine::Models::Treatment
2
+ CONTROL = 'control'.freeze
3
+ end
@@ -157,6 +157,27 @@ module SplitIoClient
157
157
  )
158
158
  end
159
159
 
160
+ def matcher_in_split_treatment(params)
161
+ DependencyMatcher.new(
162
+ params[:matcher][:dependencyMatcherData][:split],
163
+ params[:matcher][:dependencyMatcherData][:treatments]
164
+ )
165
+ end
166
+
167
+ def matcher_equal_to_boolean(params)
168
+ EqualToBooleanMatcher.new(
169
+ params[:matcher][:keySelector][:attribute],
170
+ params[:matcher][:booleanMatcherData]
171
+ )
172
+ end
173
+
174
+ def matcher_matches_string(params)
175
+ MatchesStringMatcher.new(
176
+ params[:matcher][:keySelector][:attribute],
177
+ params[:matcher][:stringMatcherData]
178
+ )
179
+ end
180
+
160
181
  #
161
182
  # @return [object] the negate value for this condition
162
183
  def negate