splitclient-rb 4.3.0.canary.2 → 4.3.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +2 -1
  3. data/Detailed-README.md +8 -0
  4. data/lib/splitclient-rb.rb +0 -1
  5. data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +8 -10
  6. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +27 -22
  7. data/lib/splitclient-rb/engine/matchers/combiners.rb +3 -7
  8. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +33 -44
  9. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +9 -5
  10. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +9 -5
  11. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +16 -4
  12. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +9 -5
  13. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +12 -4
  14. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +12 -4
  15. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +26 -21
  16. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +9 -5
  17. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +25 -21
  18. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +25 -21
  19. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +12 -4
  20. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +19 -13
  21. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +9 -5
  22. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +5 -1
  23. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +12 -4
  24. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +8 -7
  25. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +25 -20
  26. data/lib/splitclient-rb/engine/parser/condition.rb +1 -1
  27. data/lib/splitclient-rb/engine/parser/evaluator.rb +14 -16
  28. data/lib/splitclient-rb/version.rb +1 -1
  29. metadata +4 -5
  30. data/lib/splitclient-rb/engine/matchers/attribute_matcher.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b65d200504de0d0e0c33db504df5d40bde0a023b
4
- data.tar.gz: 8b483c1b5ba41fe2745a992e018ba14dbce4e3cb
3
+ metadata.gz: 1b62b60ce935de1b5345ec16107e6911321abf9c
4
+ data.tar.gz: 2ed5525256582608b165f9a42a056657fb6fc24c
5
5
  SHA512:
6
- metadata.gz: 7506af68ad2f63241782631576a73eaa845701dfc7dbbfc6274a9afde8eabf3cf3c830eae4e2b1ec8734ceee64f15fc6c57bed268662595b0e92f5760133f748
7
- data.tar.gz: 600ea370b948fcb2de1f1bbcf6f07b85cc39daa93f894675b62e0bce1b801a9c4b943fcf205b6a9324238323fc4d29e4898059d749f3564cb6913ecffb4f4770
6
+ metadata.gz: 837b99afc42609f08b51eee920c0b9492da45fb93e3ddfe187f0edc454e36077684e1bef54312a3a31e529d7fba2b10730179cc7e82a88becc4dc4e9d2435acf
7
+ data.tar.gz: cb785d0b3152892b86fa24a7b3369247ee4d17ffd2a4e9d01452bdb3d4e7b7eedb7648793ce8eb5c9e51d3ce2911256ce1da5e9bea7ad285d90f1a6bee913708
data/CHANGES.txt CHANGED
@@ -1,8 +1,9 @@
1
- 4.3.0 (Sept 14th, 2017)
1
+ 4.3.0 (Oct 13th, 2017)
2
2
  - Add impression listener
3
3
  - Add support for client shutdown (destroy())
4
4
  - Add "time" to the routed impressions
5
5
  - Add support to apply attribute matcher to the traffic type
6
+ - Add support for string matchers to match on matching keys
6
7
 
7
8
  4.2.3 (August 4, 2017)
8
9
  - Use ENV vars in producer
data/Detailed-README.md CHANGED
@@ -480,6 +480,14 @@ end
480
480
 
481
481
  By doing that SDK will recreate threads for each new worker, besides master.
482
482
 
483
+ ## Proxy support
484
+
485
+ SDK respects http_proxy environment variable, all you need to do to use proxy is assign your proxy address to that variable in the format:
486
+
487
+ ```
488
+ http_proxy=http://username:password@hostname:port
489
+ ```
490
+
483
491
  ## Framework support
484
492
 
485
493
  Currently SDK supports:
@@ -43,7 +43,6 @@ require 'splitclient-rb/engine/parser/condition'
43
43
  require 'splitclient-rb/engine/parser/partition'
44
44
  require 'splitclient-rb/engine/parser/split_adapter'
45
45
  require 'splitclient-rb/engine/parser/evaluator'
46
- require 'splitclient-rb/engine/matchers/attribute_matcher'
47
46
  require 'splitclient-rb/engine/matchers/combiners'
48
47
  require 'splitclient-rb/engine/matchers/combining_matcher'
49
48
  require 'splitclient-rb/engine/matchers/all_keys_matcher'
@@ -2,20 +2,14 @@ module SplitIoClient
2
2
  #
3
3
  # class to implement the all keys matcher
4
4
  #
5
- class AllKeysMatcher < NoMethodError
6
- attr_reader :matcher_type
7
-
8
- def initialize
9
- @matcher_type = 'ALL_KEYS'
10
- end
5
+ class AllKeysMatcher
6
+ MATCHER_TYPE = 'ALL_KEYS'.freeze
11
7
 
12
8
  #
13
9
  # evaluates if the key matches the matcher
14
10
  #
15
- # @param key [string] key value to be matched
16
- #
17
11
  # @return [boolean] true for all instances
18
- def match?(_matching_key, _bucketing_key, _evaluator, _attributes)
12
+ def match?(_args)
19
13
  true
20
14
  end
21
15
 
@@ -24,7 +18,7 @@ module SplitIoClient
24
18
  #
25
19
  # @param obj [object] object to be evaluated
26
20
  #
27
- # @returns [boolean] true if obj equals the matcher
21
+ # @return [boolean] true if obj equals the matcher
28
22
  def equals?(obj)
29
23
  if obj.nil?
30
24
  false
@@ -37,6 +31,10 @@ module SplitIoClient
37
31
  end
38
32
  end
39
33
 
34
+ def string_type?
35
+ false
36
+ end
37
+
40
38
  #
41
39
  # function to print string value for this matcher
42
40
  #
@@ -1,25 +1,25 @@
1
1
  module SplitIoClient
2
+ class BetweenMatcher
3
+ MATCHER_TYPE = 'BETWEEN'.freeze
2
4
 
3
- class BetweenMatcher < NoMethodError
4
-
5
- attr_reader :matcher_type
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute_hash)
8
- @matcher_type = "BETWEEN"
9
8
  @attribute = attribute_hash[:attribute]
10
9
  @data_type = attribute_hash[:data_type]
11
- @start_value = get_formatted_value attribute_hash[:start_value], true
12
- @end_value = get_formatted_value attribute_hash[:end_value], true
10
+ @start_value = formatted_value(attribute_hash[:start_value], true)
11
+ @end_value = formatted_value(attribute_hash[:end_value], true)
13
12
  end
14
13
 
15
- def match?(value, _matching_key, _bucketing_key, _evaluator)
16
- matches = false
17
- if !value.nil?
18
- param_value = get_formatted_value(value)
19
- matches = param_value.is_a?(Integer) ? ((param_value >= @start_value) && (param_value <= @end_value)) : false
20
- end
14
+ def match?(args)
15
+ return false if !args.key?(:attributes) && !args.key?(:value)
16
+ return false if args.key?(:value) && args[:value].nil?
17
+ return false if args.key?(:attributes) && args[:attributes].nil?
18
+
19
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
20
+ return false unless value.is_a?(Integer)
21
21
 
22
- matches
22
+ (@start_value..@end_value).include? value
23
23
  end
24
24
 
25
25
  def equals?(obj)
@@ -34,18 +34,23 @@ module SplitIoClient
34
34
  end
35
35
  end
36
36
 
37
+ def string_type?
38
+ false
39
+ end
40
+
37
41
  private
38
- def get_formatted_value(value, is_sdk_data = false)
42
+
43
+ def formatted_value(value, sdk_data = false)
39
44
  case @data_type
40
- when "NUMBER"
41
- return value
42
- when "DATETIME"
43
- value = value/1000 if is_sdk_data
44
- return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
45
- else
46
- @logger.error('Invalid data type')
45
+ when 'NUMBER'
46
+ value
47
+ when 'DATETIME'
48
+ value = value / 1000 if sdk_data
49
+
50
+ SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
51
+ else
52
+ @logger.error('Invalid data type')
47
53
  end
48
54
  end
49
55
  end
50
-
51
56
  end
@@ -1,13 +1,9 @@
1
1
  module SplitIoClient
2
-
3
2
  #
4
3
  # class to represent combiner values
5
4
  #
6
- class Combiners < NoMethodError
7
-
5
+ class Combiners
8
6
  # available combiners of the sdk
9
- AND = 'AND'
10
-
7
+ AND = 'AND'.freeze
11
8
  end
12
-
13
- end
9
+ end
@@ -2,63 +2,59 @@ module SplitIoClient
2
2
  #
3
3
  # class to implement the combining matcher
4
4
  #
5
- class CombiningMatcher < NoMethodError
5
+ class CombiningMatcher
6
+ MATCHER_TYPE = 'COMBINING_MATCHER'.freeze
6
7
 
7
- #
8
- # list of matcher within the combiner
9
- #
10
- @matcher_list = []
11
-
12
- #
13
- # combiner value
14
- #
15
- @combiner = ''
16
-
17
- def initialize(combiner, delegates)
18
- unless delegates.nil?
19
- @matcher_list = delegates
20
- end
21
- unless combiner.nil?
22
- @combiner = combiner
23
- end
8
+ def initialize(combiner = '', matchers = [])
9
+ @combiner = combiner
10
+ @matchers = matchers
24
11
  end
25
12
 
26
13
  #
27
14
  # evaluates if the key matches the matchers within the combiner
28
15
  #
29
- # @param key [string] key value to be matched
16
+ # @param matching_key [string] key value to be matched
17
+ # @param bucketing_key [string] bucketing key to be matched
18
+ # @param evaluator [instance of Evaluator class]
19
+ # @param attributes [hash]
30
20
  #
31
- # @return [boolean] match value for combiner delegates
32
- def match?(matching_key, bucketing_key, evaluator, attributes)
33
- if @matcher_list.empty?
34
- return false
35
- end
21
+ # @return [boolean]
22
+ def match?(args)
23
+ return false if @matchers.empty?
36
24
 
37
25
  case @combiner
38
- when Combiners::AND
39
- return and_eval(matching_key, bucketing_key, evaluator, attributes)
40
- else
41
- @logger.error('Invalid combiner type')
42
- return false
26
+ when Combiners::AND
27
+ return eval_and(args)
28
+ else
29
+ @logger.error('Invalid combiner type')
43
30
  end
31
+
32
+ false
44
33
  end
45
34
 
46
35
  #
47
36
  # auxiliary method to evaluate each of the matchers within the combiner
48
37
  #
49
- # @param key [string] key value to be matched
38
+ # @param matching_key [string] key value to be matched
39
+ # @param bucketing_key [string] bucketing key to be matched
50
40
  # @param evaluator [Evaluator] used in dependency_matcher
51
41
  # @param attributes [hash] attributes to pass to the treatment class
52
42
  #
53
43
  # @return [boolean] match value for combiner delegates
54
- def and_eval(matching_key, bucketing_key, evaluator, attributes)
55
- @matcher_list.each do |delegate|
56
- matched = delegate.match?(matching_key, bucketing_key, evaluator, attributes)
57
-
58
- return false unless matched
44
+ def eval_and(args)
45
+ # Convert all keys to symbols
46
+ args[:attributes] = args[:attributes].inject({}){ |memo, (k,v)| memo[k.to_sym] = v; memo } if args && args[:attributes]
47
+ @matchers.all? do |matcher|
48
+ if match_with_key?(matcher)
49
+ matcher.match?(value: args[:matching_key])
50
+ else
51
+ matcher.match?(args)
52
+ end
59
53
  end
54
+ end
60
55
 
61
- true
56
+ def match_with_key?(matcher)
57
+ matcher.respond_to?(:attribute) && matcher.attribute.nil? && matcher.string_type?
62
58
  end
63
59
 
64
60
  #
@@ -84,14 +80,7 @@ module SplitIoClient
84
80
  #
85
81
  # @reutrn [string] string value of this matcher
86
82
  def to_s
87
- result = ''
88
- @matcher_list.each_with_index do |matcher, i|
89
- result += matcher.to_s
90
- result += ' ' + @combiner if i != 0
91
- end
92
- result
83
+ @matcher_list.map(&:to_s).join("#{@combiner} ")
93
84
  end
94
-
95
85
  end
96
-
97
86
  end
@@ -1,17 +1,21 @@
1
1
  module SplitIoClient
2
2
  class ContainsAllMatcher < SetMatcher
3
- def self.matcher_type
4
- 'CONTAINS_ALL'.freeze
5
- end
3
+ MATCHER_TYPE = 'CONTAINS_ALL'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, remote_array)
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(data, bucketing_key, _evaluator, _attributes)
11
+ def match?(args)
12
12
  return false if @remote_set.empty?
13
13
 
14
- @remote_set.subset? local_set(data)
14
+ @remote_set.subset? local_set(args[:attributes], @attribute)
15
+ end
16
+
17
+ def string_type?
18
+ false
15
19
  end
16
20
  end
17
21
  end
@@ -1,15 +1,19 @@
1
1
  module SplitIoClient
2
2
  class ContainsAnyMatcher < SetMatcher
3
- def self.matcher_type
4
- 'CONTAINS_ANY'.freeze
5
- end
3
+ MATCHER_TYPE = 'CONTAINS_ANY'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, remote_array)
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(data, bucketing_key, _evaluator, _attributes)
12
- local_set(data).intersect? @remote_set
11
+ def match?(args)
12
+ local_set(args[:attributes], @attribute).intersect? @remote_set
13
+ end
14
+
15
+ def string_type?
16
+ false
13
17
  end
14
18
  end
15
19
  end
@@ -1,18 +1,30 @@
1
1
  module SplitIoClient
2
2
  class ContainsMatcher
3
- def self.matcher_type
4
- 'CONTAINS_WITH'.freeze
5
- end
3
+ MATCHER_TYPE = 'CONTAINS_WITH'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, substr_list)
8
8
  @attribute = attribute
9
9
  @substr_list = substr_list
10
10
  end
11
11
 
12
- def match?(value, _matching_key, _bucketing_key, _evaluator)
12
+ def match?(args)
13
+ return false if !args.key?(:attributes) && !args.key?(:value)
14
+ return false if args.key?(:value) && args[:value].nil?
15
+ return false if args.key?(:attributes) && args[:attributes].nil?
16
+
17
+ value = args[:value] || args[:attributes].fetch(@attribute) do |a|
18
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
19
+ end
20
+
13
21
  return false if @substr_list.empty?
14
22
 
15
23
  @substr_list.any? { |substr| value.to_s.include? substr }
16
24
  end
25
+
26
+ def string_type?
27
+ true
28
+ end
17
29
  end
18
30
  end
@@ -1,16 +1,20 @@
1
1
  module SplitIoClient
2
2
  class DependencyMatcher
3
- def self.matcher_type
4
- 'IN_SPLIT_TREATMENT'.freeze
5
- end
3
+ MATCHER_TYPE = 'IN_SPLIT_TREATMENT'.freeze
6
4
 
7
5
  def initialize(split, treatments)
8
6
  @split = split
9
7
  @treatments = treatments
10
8
  end
11
9
 
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]
10
+ def match?(args)
11
+ keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
12
+
13
+ @treatments.include?(args[:evaluator].call(keys, @split, args[:attributes])[:treatment])
14
+ end
15
+
16
+ def string_type?
17
+ false
14
18
  end
15
19
  end
16
20
  end
@@ -1,18 +1,26 @@
1
1
  module SplitIoClient
2
2
  class EndsWithMatcher
3
- def self.matcher_type
4
- 'ENDS_WITH'.freeze
5
- end
3
+ MATCHER_TYPE = 'ENDS_WITH'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, suffix_list)
8
8
  @attribute = attribute
9
9
  @suffix_list = suffix_list
10
10
  end
11
11
 
12
- def match?(value, _matching_key, _bucketing_key, _evaluator)
12
+ def match?(args)
13
+ value = args[:value] || args[:attributes].fetch(@attribute) do |a|
14
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
15
+ end
16
+
13
17
  return false if @suffix_list.empty?
14
18
 
15
19
  @suffix_list.any? { |suffix| value.to_s.end_with? suffix }
16
20
  end
21
+
22
+ def string_type?
23
+ true
24
+ end
17
25
  end
18
26
  end
@@ -1,19 +1,27 @@
1
1
  module SplitIoClient
2
2
  class EqualToBooleanMatcher
3
- def self.matcher_type
4
- 'EQUAL_TO_BOOLEAN'.freeze
5
- end
3
+ MATCHER_TYPE = 'EQUAL_TO_BOOLEAN'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, boolean)
8
8
  @attribute = attribute
9
9
  @boolean = boolean
10
10
  end
11
11
 
12
- def match?(value, _matching_key, _bucketing_key, _evaluator)
12
+ def match?(args)
13
+ value = args[:attributes].fetch(@attribute) do |a|
14
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
15
+ end
16
+
13
17
  value = false if value.to_s.downcase == 'false'
14
18
  value = true if value.to_s.downcase == 'true'
15
19
 
16
20
  value == @boolean
17
21
  end
22
+
23
+ def string_type?
24
+ false
25
+ end
18
26
  end
19
27
  end
@@ -1,22 +1,23 @@
1
1
  module SplitIoClient
2
+ class EqualToMatcher
3
+ MATCHER_TYPE = 'EQUAL_TO'.freeze
2
4
 
3
- class EqualToMatcher < NoMethodError
4
-
5
- attr_reader :matcher_type
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute_hash)
8
- @matcher_type = "EQUAL_TO"
9
8
  @attribute = attribute_hash[:attribute]
10
9
  @data_type = attribute_hash[:data_type]
11
- @value = get_formatted_value attribute_hash[:value], true
10
+ @value = formatted_value(attribute_hash[:value], true)
12
11
  end
13
12
 
14
- def match?(value, _matching_key, _bucketing_key, _evaluator)
15
- matches = false
16
- if !value.nil?
17
- param_value = get_formatted_value(value)
18
- matches = param_value.is_a?(Integer) ? (param_value == @value) : false
19
- end
13
+ def match?(args)
14
+ return false if !args.key?(:attributes) && !args.key?(:value)
15
+ return false if args.key?(:value) && args[:value].nil?
16
+ return false if args.key?(:attributes) && args[:attributes].nil?
17
+
18
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
19
+
20
+ value.is_a?(Integer) ? (value == @value) : false
20
21
  end
21
22
 
22
23
  def equals?(obj)
@@ -31,19 +32,23 @@ module SplitIoClient
31
32
  end
32
33
  end
33
34
 
35
+ def string_type?
36
+ false
37
+ end
38
+
34
39
  private
35
- def get_formatted_value(value, is_sdk_data = false)
40
+
41
+ def formatted_value(value, sdk_data = false)
36
42
  case @data_type
37
- when "NUMBER"
38
- return value
39
- when "DATETIME"
40
- value = value/1000 if is_sdk_data
41
- return SplitIoClient::Utilities.to_milis_zero_out_from_hour value
42
- else
43
- @logger.error('Invalid data type')
43
+ when 'NUMBER'
44
+ value
45
+ when 'DATETIME'
46
+ value = value / 1000 if sdk_data
47
+
48
+ SplitIoClient::Utilities.to_milis_zero_out_from_hour value
49
+ else
50
+ @logger.error('Invalid data type')
44
51
  end
45
52
  end
46
-
47
53
  end
48
-
49
54
  end
@@ -1,15 +1,19 @@
1
1
  module SplitIoClient
2
2
  class EqualToSetMatcher < SetMatcher
3
- def self.matcher_type
4
- 'EQUAL_TO_SET'.freeze
5
- end
3
+ MATCHER_TYPE = 'EQUAL_TO_SET'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, remote_array)
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(data, bucketing_key, _evaluator, _attributes)
12
- local_set(data) == @remote_set
11
+ def match?(args)
12
+ local_set(args[:attributes], @attribute) == @remote_set
13
+ end
14
+
15
+ def string_type?
16
+ false
13
17
  end
14
18
  end
15
19
  end
@@ -1,22 +1,23 @@
1
1
  module SplitIoClient
2
+ class GreaterThanOrEqualToMatcher
3
+ MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO'.freeze
2
4
 
3
- class GreaterThanOrEqualToMatcher < NoMethodError
4
-
5
- attr_reader :matcher_type
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute_hash)
8
- @matcher_type = "GREATER_THAN_OR_EQUAL_TO"
9
8
  @attribute = attribute_hash[:attribute]
10
9
  @data_type = attribute_hash[:data_type]
11
- @value = get_formatted_value attribute_hash[:value], true
10
+ @value = formatted_value(attribute_hash[:value], true)
12
11
  end
13
12
 
14
- def match?(value, _matching_key, _bucketing_key, _evaluator)
15
- matches = false
16
- if !value.nil?
17
- param_value = get_formatted_value(value)
18
- matches = param_value.is_a?(Integer) ? (param_value >= @value) : false
19
- end
13
+ def match?(args)
14
+ return false if !args.key?(:attributes) && !args.key?(:value)
15
+ return false if args.key?(:value) && args[:value].nil?
16
+ return false if args.key?(:attributes) && args[:attributes].nil?
17
+
18
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
19
+
20
+ value.is_a?(Integer) ? (value >= @value) : false
20
21
  end
21
22
 
22
23
  def equals?(obj)
@@ -31,19 +32,22 @@ module SplitIoClient
31
32
  end
32
33
  end
33
34
 
35
+ def string_type?
36
+ false
37
+ end
38
+
34
39
  private
35
- def get_formatted_value(value, is_sdk_data = false)
40
+
41
+ def formatted_value(value, sdk_data = false)
36
42
  case @data_type
37
- when "NUMBER"
38
- return value
39
- when "DATETIME"
40
- value = value/1000 if is_sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
41
- return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
42
- else
43
- @logger.error('Invalid data type')
43
+ when 'NUMBER'
44
+ return value
45
+ when 'DATETIME'
46
+ value = value / 1000 if sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
47
+ return SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
48
+ else
49
+ @logger.error('Invalid data type')
44
50
  end
45
51
  end
46
-
47
52
  end
48
-
49
53
  end
@@ -1,22 +1,23 @@
1
1
  module SplitIoClient
2
+ class LessThanOrEqualToMatcher
3
+ MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO'.freeze
2
4
 
3
- class LessThanOrEqualToMatcher < NoMethodError
4
-
5
- attr_reader :matcher_type
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute_hash)
8
- @matcher_type = "LESS_THAN_OR_EQUAL_TO"
9
8
  @attribute = attribute_hash[:attribute]
10
9
  @data_type = attribute_hash[:data_type]
11
- @value = get_formatted_value attribute_hash[:value], true
10
+ @value = formatted_value(attribute_hash[:value], true)
12
11
  end
13
12
 
14
- def match?(value, _matching_key, _bucketing_key, _evaluator)
15
- matches = false
16
- if !value.nil?
17
- param_value = get_formatted_value(value)
18
- matches = param_value.is_a?(Integer) ? (param_value <= @value) : false
19
- end
13
+ def match?(args)
14
+ return false if !args.key?(:attributes) && !args.key?(:value)
15
+ return false if args.key?(:value) && args[:value].nil?
16
+ return false if args.key?(:attributes) && args[:attributes].nil?
17
+
18
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
19
+
20
+ value.is_a?(Integer) ? (value <= @value) : false
20
21
  end
21
22
 
22
23
  def equals?(obj)
@@ -31,19 +32,22 @@ module SplitIoClient
31
32
  end
32
33
  end
33
34
 
35
+ def string_type?
36
+ false
37
+ end
38
+
34
39
  private
35
- def get_formatted_value(value, is_sdk_data = false)
40
+
41
+ def formatted_value(value, sdk_data = false)
36
42
  case @data_type
37
- when "NUMBER"
38
- return value
39
- when "DATETIME"
40
- value = value/1000 if is_sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
41
- return SplitIoClient::Utilities.to_milis_zero_out_from_seconds value
42
- else
43
- @logger.error('Invalid data type')
43
+ when 'NUMBER'
44
+ return value
45
+ when 'DATETIME'
46
+ value = value / 1000 if sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_our
47
+ return SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
48
+ else
49
+ @logger.error('Invalid data type')
44
50
  end
45
51
  end
46
-
47
52
  end
48
-
49
53
  end
@@ -1,16 +1,24 @@
1
1
  module SplitIoClient
2
2
  class MatchesStringMatcher
3
- def self.matcher_type
4
- 'MATCHES_STRING'.freeze
5
- end
3
+ MATCHER_TYPE = 'MATCHES_STRING'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, regexp_string)
8
8
  @attribute = attribute
9
9
  @regexp_string = @regexp_string.is_a?(Regexp) ? regexp_string : Regexp.new(regexp_string)
10
10
  end
11
11
 
12
- def match?(value, _matching_key, _bucketing_key, _evaluator)
12
+ def match?(args)
13
+ value = args[:value] || args[:attributes].fetch(@attribute) do |a|
14
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
15
+ end
16
+
13
17
  (value =~ @regexp_string) != nil
14
18
  end
19
+
20
+ def string_type?
21
+ true
22
+ end
15
23
  end
16
24
  end
@@ -1,16 +1,12 @@
1
1
  module SplitIoClient
2
-
3
2
  #
4
3
  # class to implement the negation of a matcher
5
4
  #
6
- class NegationMatcher < NoMethodError
7
-
8
- @matcher = nil
5
+ class NegationMatcher
6
+ MATCHER_TYPE = 'NEGATION_MATCHER'.freeze
9
7
 
10
- def initialize(matcher)
11
- unless matcher.nil?
12
- @matcher = matcher
13
- end
8
+ def initialize(matcher = nil)
9
+ @matcher = matcher
14
10
  end
15
11
 
16
12
  #
@@ -19,8 +15,20 @@ module SplitIoClient
19
15
  # @param key [string] key value to be matched
20
16
  #
21
17
  # @return [boolean] evaluation of the negation matcher
22
- def match?(matching_key, bucketing_key, evaluator, attributes)
23
- !@matcher.match?(matching_key, bucketing_key, evaluator, attributes)
18
+ def match?(args)
19
+ !@matcher.match?(args)
20
+ end
21
+
22
+ def respond_to?(method)
23
+ @matcher.respond_to? method
24
+ end
25
+
26
+ def attribute
27
+ @matcher.attribute
28
+ end
29
+
30
+ def string_type?
31
+ @matcher.string_type?
24
32
  end
25
33
 
26
34
  #
@@ -46,9 +54,7 @@ module SplitIoClient
46
54
  #
47
55
  # @reutrn [string] string value of this matcher
48
56
  def to_s
49
- 'not ' + @matcher.to_s
57
+ "not #{@matcher}"
50
58
  end
51
-
52
59
  end
53
-
54
60
  end
@@ -1,19 +1,23 @@
1
1
  module SplitIoClient
2
2
  class PartOfSetMatcher < SetMatcher
3
- def self.matcher_type
4
- 'PART_OF_SET'.freeze
5
- end
3
+ MATCHER_TYPE = 'PART_OF_SET'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, remote_array)
8
8
  super(attribute, remote_array)
9
9
  end
10
10
 
11
- def match?(data, bucketing_key, _evaluator, _attributes)
12
- @local_set = local_set(data)
11
+ def match?(args)
12
+ @local_set = local_set(args[:attributes], @attribute)
13
13
 
14
14
  return false if @local_set.empty?
15
15
 
16
16
  @local_set.subset? @remote_set
17
17
  end
18
+
19
+ def string_type?
20
+ false
21
+ end
18
22
  end
19
23
  end
@@ -1,12 +1,16 @@
1
+ require 'set'
2
+
1
3
  module SplitIoClient
2
4
  class SetMatcher
3
5
  protected
4
6
 
5
7
  def initialize(attribute, remote_array)
8
+ @attribute = attribute
6
9
  @remote_set = remote_array.to_set
7
10
  end
8
11
 
9
- def local_set(data)
12
+ def local_set(data, attribute)
13
+ data = data.fetch(attribute) { |a| data[a.to_s] || data[a.to_sym] }
10
14
  # Allow user to pass individual elements as well
11
15
  local_array = data.kind_of?(Array) ? data : [data]
12
16
 
@@ -1,18 +1,26 @@
1
1
  module SplitIoClient
2
2
  class StartsWithMatcher
3
- def self.matcher_type
4
- 'STARTS_WITH'.freeze
5
- end
3
+ MATCHER_TYPE = 'STARTS_WITH'.freeze
4
+
5
+ attr_reader :attribute
6
6
 
7
7
  def initialize(attribute, prefix_list)
8
8
  @attribute = attribute
9
9
  @prefix_list = prefix_list
10
10
  end
11
11
 
12
- def match?(value, _matching_key, _bucketing_key, _evaluator)
12
+ def match?(args)
13
+ value = args[:value] || args[:attributes].fetch(@attribute) do |a|
14
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
15
+ end
16
+
13
17
  return false if @prefix_list.empty?
14
18
 
15
19
  @prefix_list.any? { |prefix| value.to_s.start_with? prefix }
16
20
  end
21
+
22
+ def string_type?
23
+ true
24
+ end
17
25
  end
18
26
  end
@@ -1,14 +1,11 @@
1
1
  module SplitIoClient
2
-
3
2
  #
4
3
  # class to implement the user defined matcher
5
4
  #
6
- class UserDefinedSegmentMatcher < NoMethodError
7
-
8
- attr_reader :matcher_type
5
+ class UserDefinedSegmentMatcher
6
+ MATCHER_TYPE = 'IN_SEGMENT'.freeze
9
7
 
10
8
  def initialize(segments_repository, segment_name)
11
- @matcher_type = "IN_SEGMENT"
12
9
  @segments_repository = segments_repository
13
10
  @segment_name = segment_name
14
11
  end
@@ -19,8 +16,8 @@ module SplitIoClient
19
16
  # @param key [string] key value to be matched
20
17
  #
21
18
  # @return [boolean] evaluation of the key against the segment
22
- def match?(matching_key, _bucketing_key, _evaluator, attributes)
23
- @segments_repository.in_segment?(@segment_name, matching_key)
19
+ def match?(args)
20
+ @segments_repository.in_segment?(@segment_name, args[:value] || args[:matching_key])
24
21
  end
25
22
 
26
23
  #
@@ -40,5 +37,9 @@ module SplitIoClient
40
37
  false
41
38
  end
42
39
  end
40
+
41
+ def string_type?
42
+ false
43
+ end
43
44
  end
44
45
  end
@@ -1,33 +1,36 @@
1
1
  module SplitIoClient
2
-
3
2
  #
4
3
  # class to implement the user defined matcher
5
4
  #
6
5
  class WhitelistMatcher < NoMethodError
6
+ MATCHER_TYPE = 'WHITELIST_MATCHER'
7
7
 
8
- attr_reader :matcher_type
9
-
10
- # variable that contains the keys of the whitelist
11
- @whitelist = []
8
+ attr_reader :attribute
12
9
 
13
10
  def initialize(whitelist_data)
14
- if whitelist_data.instance_of? Array
15
- @whitelist = whitelist_data unless whitelist_data.nil?
16
- elsif whitelist_data.instance_of? Hash
17
- @matcher_type = "ATTR_WHITELIST"
11
+ @whitelist = case whitelist_data
12
+ when Array
13
+ whitelist_data
14
+ when Hash
15
+ @matcher_type = 'ATTR_WHITELIST'
18
16
  @attribute = whitelist_data[:attribute]
19
- @whitelist = whitelist_data[:value] unless whitelist_data[:value].nil?
17
+
18
+ whitelist_data[:value]
19
+ else
20
+ []
20
21
  end
21
22
  end
22
23
 
23
- def match?(value, _matching_key, _bucketing_key, _evaluator)
24
- matches = false
25
-
26
- if !value.nil?
27
- matches = @whitelist.include?(value)
28
- end
24
+ def match?(args)
25
+ return @whitelist.include?(args[:value] || args[:matching_key]) unless @matcher_type == 'ATTR_WHITELIST'
26
+
27
+ return false if !args.key?(:attributes) && !args.key?(:value)
28
+ return false if args.key?(:value) && args[:value].nil?
29
+ return false if args.key?(:attributes) && args[:attributes].nil?
29
30
 
30
- matches
31
+ return @whitelist.include?(args[:value] || args[:attributes][@attribute.to_sym])
32
+
33
+ false
31
34
  end
32
35
 
33
36
  #
@@ -48,14 +51,16 @@ module SplitIoClient
48
51
  end
49
52
  end
50
53
 
54
+ def string_type?
55
+ true
56
+ end
57
+
51
58
  #
52
59
  # function to print string value for this matcher
53
60
  #
54
61
  # @reutrn [string] string value of this matcher
55
62
  def to_s
56
- 'in segment ' + @whitelist.to_s
63
+ "in segment #{@whitelist}"
57
64
  end
58
-
59
65
  end
60
-
61
66
  end
@@ -17,7 +17,7 @@ module SplitIoClient
17
17
  end
18
18
 
19
19
  def create_condition_matcher(matchers)
20
- CombiningMatcher.new(combiner, matchers) unless combiner.nil?
20
+ CombiningMatcher.new(combiner, matchers) if combiner
21
21
  end
22
22
 
23
23
  #
@@ -58,23 +58,21 @@ module SplitIoClient
58
58
  in_rollout = true
59
59
  end
60
60
 
61
- begin
62
- matcher = SplitIoClient::AttributeMatcher.new(
63
- condition.data[:matcherGroup][:matchers][0][:keySelector][:attribute],
64
- matcher_type(condition)
65
- )
66
- rescue NoMethodError
67
- matcher = SplitIoClient::AttributeMatcher.new(nil, matcher_type(condition))
68
- end
69
- if matcher.match?(keys[:matching_key], keys[:bucketing_key], self, attributes)
70
- key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
71
- result = Splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
61
+ condition_matched = matcher_type(condition).match?(
62
+ matching_key: keys[:matching_key],
63
+ bucketing_key: keys[:bucketing_key],
64
+ evaluator: self,
65
+ attributes: attributes
66
+ )
72
67
 
73
- if result.nil?
74
- return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber])
75
- else
76
- return treatment_hash(c[:label], result, split[:changeNumber])
77
- end
68
+ next unless condition_matched
69
+
70
+ result = Splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
71
+
72
+ if result.nil?
73
+ return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber])
74
+ else
75
+ return treatment_hash(c[:label], result, split[:changeNumber])
78
76
  end
79
77
  end
80
78
 
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '4.3.0.canary.2'
2
+ VERSION = '4.3.0'
3
3
  end
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: 4.3.0.canary.2
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-18 00:00:00.000000000 Z
11
+ date: 2017-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -283,7 +283,6 @@ files:
283
283
  - lib/splitclient-rb/engine/api/splits.rb
284
284
  - lib/splitclient-rb/engine/evaluator/splitter.rb
285
285
  - lib/splitclient-rb/engine/matchers/all_keys_matcher.rb
286
- - lib/splitclient-rb/engine/matchers/attribute_matcher.rb
287
286
  - lib/splitclient-rb/engine/matchers/between_matcher.rb
288
287
  - lib/splitclient-rb/engine/matchers/combiners.rb
289
288
  - lib/splitclient-rb/engine/matchers/combining_matcher.rb
@@ -346,9 +345,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
346
345
  version: '0'
347
346
  required_rubygems_version: !ruby/object:Gem::Requirement
348
347
  requirements:
349
- - - ">"
348
+ - - ">="
350
349
  - !ruby/object:Gem::Version
351
- version: 1.3.1
350
+ version: '0'
352
351
  requirements: []
353
352
  rubyforge_project:
354
353
  rubygems_version: 2.5.2
@@ -1,20 +0,0 @@
1
- module SplitIoClient
2
- class AttributeMatcher
3
- def initialize(attribute, matcher)
4
- @attribute = attribute
5
- @matcher = matcher
6
- end
7
-
8
- def match?(matching_key, bucketing_key = nil, evaluator = nil, attributes = {})
9
- if @attribute != nil
10
- return false unless attributes
11
-
12
- value = attributes.fetch(@attribute) { |name| attributes[name.to_s] || attributes[name.to_sym] }
13
-
14
- @matcher.match?(value, bucketing_key, nil, nil)
15
- else
16
- @matcher.match?(matching_key, bucketing_key, evaluator, attributes)
17
- end
18
- end
19
- end
20
- end