splitclient-rb 6.3.0 → 8.11.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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/pull_request_template.md +9 -0
  4. data/.github/workflows/ci.yml +90 -0
  5. data/.github/workflows/update-license-year.yml +45 -0
  6. data/.gitignore +4 -0
  7. data/.rubocop.yml +46 -3
  8. data/CHANGES.txt +158 -11
  9. data/CONTRIBUTORS-GUIDE.md +49 -0
  10. data/LICENSE +169 -13
  11. data/NOTICE.txt +5 -0
  12. data/README.md +67 -27
  13. data/Rakefile +1 -8
  14. data/ext/murmurhash/3_x64_128.c +117 -0
  15. data/ext/murmurhash/murmurhash.c +5 -1
  16. data/lib/murmurhash/murmurhash.jar +0 -0
  17. data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +3 -3
  18. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +4 -0
  19. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +7 -0
  20. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +12 -4
  21. data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
  22. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
  23. data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
  24. data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
  25. data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
  26. data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
  27. data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
  28. data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
  29. data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +26 -14
  30. data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +9 -14
  31. data/lib/splitclient-rb/cache/repositories/events_repository.rb +31 -10
  32. data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
  33. data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
  34. data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +22 -23
  35. data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +15 -22
  36. data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +6 -31
  37. data/lib/splitclient-rb/cache/repositories/repository.rb +6 -5
  38. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
  39. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +46 -6
  40. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +232 -43
  41. data/lib/splitclient-rb/cache/routers/impression_router.rb +24 -22
  42. data/lib/splitclient-rb/cache/senders/events_sender.rb +12 -29
  43. data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
  44. data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
  45. data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
  46. data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +27 -13
  47. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +11 -25
  48. data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
  49. data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
  50. data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
  51. data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
  52. data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
  53. data/lib/splitclient-rb/clients/split_client.rb +385 -138
  54. data/lib/splitclient-rb/constants.rb +16 -0
  55. data/lib/splitclient-rb/engine/api/client.rb +38 -43
  56. data/lib/splitclient-rb/engine/api/events.rb +19 -11
  57. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +1 -0
  58. data/lib/splitclient-rb/engine/api/impressions.rb +49 -14
  59. data/lib/splitclient-rb/engine/api/segments.rb +31 -24
  60. data/lib/splitclient-rb/engine/api/splits.rb +108 -33
  61. data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
  62. data/lib/splitclient-rb/engine/auth_api_client.rb +96 -0
  63. data/lib/splitclient-rb/engine/back_off.rb +26 -0
  64. data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
  65. data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
  66. data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
  67. data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
  68. data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
  69. data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
  70. data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
  71. data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
  72. data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
  73. data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
  74. data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
  75. data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +1 -1
  76. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +7 -5
  77. data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
  78. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +10 -8
  79. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +2 -6
  80. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +1 -5
  81. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +7 -5
  82. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +6 -5
  83. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +5 -4
  84. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +3 -2
  85. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +6 -4
  86. data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
  87. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +1 -5
  88. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +6 -4
  89. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
  90. data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
  91. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +6 -4
  92. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
  93. data/lib/splitclient-rb/engine/matchers/matcher.rb +22 -0
  94. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +3 -2
  95. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +3 -2
  96. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +2 -6
  97. data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
  98. data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
  99. data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
  100. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +2 -1
  101. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +4 -3
  102. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +3 -2
  103. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +7 -5
  104. data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +3 -65
  105. data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
  106. data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
  107. data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
  108. data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
  109. data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
  110. data/lib/splitclient-rb/engine/models/label.rb +3 -0
  111. data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
  112. data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
  113. data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
  114. data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +14 -0
  115. data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
  116. data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
  117. data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
  118. data/lib/splitclient-rb/engine/parser/condition.rb +81 -20
  119. data/lib/splitclient-rb/engine/parser/evaluator.rb +40 -51
  120. data/lib/splitclient-rb/engine/push_manager.rb +66 -0
  121. data/lib/splitclient-rb/engine/status_manager.rb +39 -0
  122. data/lib/splitclient-rb/engine/sync_manager.rb +180 -0
  123. data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
  124. data/lib/splitclient-rb/exceptions.rb +20 -1
  125. data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
  126. data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
  127. data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
  128. data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
  129. data/lib/splitclient-rb/helpers/util.rb +26 -0
  130. data/lib/splitclient-rb/managers/split_manager.rb +58 -20
  131. data/lib/splitclient-rb/spec.rb +9 -0
  132. data/lib/splitclient-rb/split_config.rb +336 -54
  133. data/lib/splitclient-rb/split_factory.rb +219 -33
  134. data/lib/splitclient-rb/split_factory_builder.rb +1 -22
  135. data/lib/splitclient-rb/split_factory_registry.rb +63 -0
  136. data/lib/splitclient-rb/split_logger.rb +9 -10
  137. data/lib/splitclient-rb/sse/event_source/client.rb +263 -0
  138. data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
  139. data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
  140. data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
  141. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
  142. data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
  143. data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
  144. data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
  145. data/lib/splitclient-rb/sse/workers/splits_worker.rb +149 -0
  146. data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
  147. data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
  148. data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
  149. data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
  150. data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
  151. data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
  152. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
  153. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
  154. data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
  155. data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
  156. data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
  157. data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
  158. data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
  159. data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
  160. data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
  161. data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
  162. data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
  163. data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
  164. data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
  165. data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
  166. data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
  167. data/lib/splitclient-rb/utilitites.rb +8 -0
  168. data/lib/splitclient-rb/validators.rb +142 -38
  169. data/lib/splitclient-rb/version.rb +1 -1
  170. data/lib/splitclient-rb.rb +101 -16
  171. data/sonar-project.properties +6 -0
  172. data/splitclient-rb.gemspec +28 -23
  173. metadata +262 -82
  174. data/.travis.yml +0 -11
  175. data/Appraisals +0 -10
  176. data/Detailed-README.md +0 -588
  177. data/NEWS +0 -141
  178. data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +0 -127
  179. data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +0 -96
  180. data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +0 -21
  181. data/lib/splitclient-rb/cache/senders/metrics_sender.rb +0 -56
  182. data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +0 -46
  183. data/lib/splitclient-rb/cache/stores/segment_store.rb +0 -81
  184. data/lib/splitclient-rb/cache/stores/split_store.rb +0 -102
  185. data/lib/splitclient-rb/clients/localhost_split_client.rb +0 -183
  186. data/lib/splitclient-rb/engine/api/faraday_adapter/patched_net_http_persistent.rb +0 -46
  187. data/lib/splitclient-rb/engine/api/metrics.rb +0 -60
  188. data/lib/splitclient-rb/engine/metrics/metrics.rb +0 -80
  189. data/lib/splitclient-rb/engine/parser/split_adapter.rb +0 -81
  190. data/lib/splitclient-rb/localhost_split_factory.rb +0 -13
  191. data/lib/splitclient-rb/localhost_utils.rb +0 -59
  192. data/lib/splitclient-rb/managers/localhost_split_manager.rb +0 -60
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class GreaterThanOrEqualToSemverMatcher < Matcher
5
+ MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, string_value, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute
13
+ @semver = SplitIoClient::Semver.build(string_value, logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def match?(args)
18
+ return false unless verify_semver_arg?(args, 'GreaterThanOrEqualsToSemverMatcher')
19
+
20
+ value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
21
+ return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
22
+
23
+ matches = [0, 1].include?(value_to_match.compare(@semver))
24
+ @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
25
+ matches
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class InListSemverMatcher < Matcher
5
+ MATCHER_TYPE = 'IN_LIST_SEMVER'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, list_value, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute
13
+ @semver_list = []
14
+
15
+ list_value.map do |item|
16
+ version = SplitIoClient::Semver.build(item, logger)
17
+ @semver_list << version unless version.nil?
18
+ end
19
+ @logger = logger
20
+ end
21
+
22
+ def match?(args)
23
+ return false if @semver_list.empty? || !verify_semver_arg?(args, 'InListSemverMatcher')
24
+
25
+ value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
26
+ if value_to_match.nil?
27
+ @logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type')
28
+ return false
29
+
30
+ end
31
+ matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true }
32
+ @logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}")
33
+ matches
34
+ end
35
+ end
36
+ end
@@ -6,21 +6,23 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute_hash)
9
+ def initialize(attribute_hash, logger, validator)
10
+ super(logger)
11
+ @validator = validator
10
12
  @attribute = attribute_hash[:attribute]
11
13
  @data_type = attribute_hash[:data_type]
12
14
  @value = formatted_value(attribute_hash[:value], true)
13
15
  end
14
16
 
15
17
  def match?(args)
16
- SplitLogger.log_if_debug('[LessThanOrEqualToMatcher] evaluating value and attributes.')
18
+ @logger.log_if_debug('[LessThanOrEqualToMatcher] evaluating value and attributes.')
17
19
 
18
- return false unless SplitIoClient::Validators.valid_matcher_arguments(args)
20
+ return false unless @validator.valid_matcher_arguments(args)
19
21
 
20
22
  value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
21
23
 
22
24
  matches = value.is_a?(Integer) ? (value <= @value) : false
23
- SplitLogger.log_if_debug("[LessThanOrEqualToMatcher] #{value} less than or equal to #{@value} -> #{matches}")
25
+ @logger.log_if_debug("[LessThanOrEqualToMatcher] #{value} less than or equal to #{@value} -> #{matches}")
24
26
  matches
25
27
  end
26
28
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class LessThanOrEqualToSemverMatcher < Matcher
5
+ MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, string_value, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute
13
+ @semver = SplitIoClient::Semver.build(string_value, logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def match?(args)
18
+ return false unless verify_semver_arg?(args, 'LessThanOrEqualsToSemverMatcher')
19
+
20
+ value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
21
+ return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
22
+
23
+ matches = [0, -1].include?(value_to_match.compare(@semver))
24
+ @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
25
+ matches
26
+ end
27
+ end
28
+ end
@@ -5,6 +5,10 @@ module SplitIoClient
5
5
  # class to implement the all keys matcher
6
6
  #
7
7
  class Matcher
8
+ def initialize(logger)
9
+ @logger = logger
10
+ end
11
+
8
12
  #
9
13
  # evaluates if the given object equals the matcher
10
14
  #
@@ -26,5 +30,23 @@ module SplitIoClient
26
30
  def string_type?
27
31
  false
28
32
  end
33
+
34
+ private
35
+
36
+ def verify_semver_arg?(args, matcher_name)
37
+ @logger.debug("[#{matcher_name}] evaluating value and attributes.")
38
+ return false unless @validator.valid_matcher_arguments(args)
39
+
40
+ true
41
+ end
42
+
43
+ def check_semver_value_to_match(value_to_match, matcher_spec_name)
44
+ if value_to_match.nil? || @semver.nil?
45
+ @logger.error("stringMatcherData is required for #{matcher_spec_name} matcher type")
46
+ return false
47
+
48
+ end
49
+ true
50
+ end
29
51
  end
30
52
  end
@@ -6,9 +6,10 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, regexp_string)
9
+ def initialize(attribute, regexp_string, logger)
10
10
  @attribute = attribute
11
11
  @regexp_string = @regexp_string.is_a?(Regexp) ? regexp_string : Regexp.new(regexp_string)
12
+ @logger = logger
12
13
  end
13
14
 
14
15
  def match?(args)
@@ -17,7 +18,7 @@ module SplitIoClient
17
18
  end
18
19
 
19
20
  matches = !(value =~ @regexp_string).nil?
20
- SplitLogger.log_if_debug("[MatchesStringMatcher] #{value} matches #{@regexp_string} -> #{matches}")
21
+ @logger.log_if_debug("[MatchesStringMatcher] #{value} matches #{@regexp_string} -> #{matches}")
21
22
  matches
22
23
  end
23
24
 
@@ -7,7 +7,8 @@ module SplitIoClient
7
7
  class NegationMatcher < Matcher
8
8
  MATCHER_TYPE = 'NEGATION_MATCHER'
9
9
 
10
- def initialize(matcher = nil)
10
+ def initialize(logger, matcher = nil)
11
+ super(logger)
11
12
  @matcher = matcher
12
13
  end
13
14
 
@@ -19,7 +20,7 @@ module SplitIoClient
19
20
  # @return [boolean] evaluation of the negation matcher
20
21
  def match?(args)
21
22
  matches = !@matcher.match?(args)
22
- SplitLogger.log_if_debug("[NegationMatcherMatcher] Matcher #{@matcher} Arguments #{args} -> #{matches}")
23
+ @logger.log_if_debug("[NegationMatcherMatcher] Matcher #{@matcher} Arguments #{args} -> #{matches}")
23
24
  matches
24
25
  end
25
26
 
@@ -6,20 +6,16 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, remote_array)
10
- super(attribute, remote_array)
11
- end
12
-
13
9
  def match?(args)
14
10
  @local_set = local_set(args[:attributes], @attribute)
15
11
 
16
12
  if @local_set.empty?
17
- SplitLogger.log_if_debug('[PartOfSetMatcher] Local Set is empty.')
13
+ @logger.log_if_debug('[PartOfSetMatcher] Local Set is empty.')
18
14
  return false
19
15
  end
20
16
 
21
17
  matches = @local_set.subset? @remote_set
22
- SplitLogger.log_if_debug("[PartOfSetMatcher] Local Set #{@local_set} is a subset of #{@remote_set} -> #{matches}")
18
+ @logger.log_if_debug("[PartOfSetMatcher] Local Set #{@local_set} is a subset of #{@remote_set} -> #{matches}")
23
19
  matches
24
20
  end
25
21
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class PrerequisitesMatcher
5
+ def initialize(prerequisites, logger)
6
+ @prerequisites = prerequisites
7
+ @logger = logger
8
+ end
9
+
10
+ def match?(args)
11
+ keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
12
+
13
+ match = true
14
+ @prerequisites.each do |prerequisite|
15
+ evaluate = args[:evaluator].evaluate_feature_flag(keys, prerequisite[:n], args[:attributes])
16
+ next if prerequisite[:ts].include?(evaluate[:treatment])
17
+
18
+ @logger.log_if_debug("[PrerequisitesMatcher] feature flag #{prerequisite[:n]} evaluated to #{evaluate[:treatment]} \
19
+ that does not exist in prerequisited treatments.")
20
+ match = false
21
+ break
22
+ end
23
+
24
+ match
25
+ end
26
+
27
+ def string_type?
28
+ false
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ #
5
+ # class to implement the user defined matcher
6
+ #
7
+ class RuleBasedSegmentMatcher < Matcher
8
+ MATCHER_TYPE = 'IN_RULE_BASED_SEGMENT'
9
+
10
+ def initialize(segments_repository, rule_based_segments_repository, segment_name, config)
11
+ super(config.logger)
12
+ @rule_based_segments_repository = rule_based_segments_repository
13
+ @segments_repository = segments_repository
14
+ @segment_name = segment_name
15
+ @config = config
16
+ end
17
+
18
+ #
19
+ # evaluates if the key matches the matcher
20
+ #
21
+ # @param key [string] key value to be matched
22
+ #
23
+ # @return [boolean] evaluation of the key against the segment
24
+ def match?(args)
25
+ rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name)
26
+ return false if rule_based_segment.nil?
27
+
28
+ key = update_key(args)
29
+ return false if rule_based_segment[:excluded][:keys].include?(key)
30
+
31
+ return false unless check_excluded_segments(rule_based_segment, key, args)
32
+
33
+ matched = false
34
+ rule_based_segment[:conditions].each do |c|
35
+ condition = SplitIoClient::Condition.new(c, @config)
36
+ next if condition.empty?
37
+
38
+ matched = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args)
39
+
40
+ break if matched
41
+ end
42
+
43
+ @logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matched}")
44
+ matched
45
+ end
46
+
47
+ private
48
+
49
+ def check_excluded_segments(rule_based_segment, key, args)
50
+ rule_based_segment[:excluded][:segments].each do |segment|
51
+ if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD &&
52
+ @segments_repository.in_segment?(segment[:name], key)
53
+ return false
54
+ end
55
+ return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::RULE_BASED_SEGMENT && match_rbs(
56
+ @rule_based_segments_repository.get_rule_based_segment(segment[:name]), args
57
+ )
58
+ end
59
+ true
60
+ end
61
+
62
+ def update_key(args)
63
+ if args[:value].nil? || args[:value].empty?
64
+ args[:matching_key]
65
+ else
66
+ args[:value]
67
+ end
68
+ end
69
+
70
+ def match_rbs(rule_based_segment, args)
71
+ rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository,
72
+ rule_based_segment[:name], @config)
73
+ rbs_matcher.match?(matching_key: args[:matching_key],
74
+ bucketing_key: args[:value],
75
+ attributes: args[:attributes])
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class Semver
5
+ METADATA_DELIMITER = '+'
6
+ PRE_RELEASE_DELIMITER = '-'
7
+ VALUE_DELIMITER = '.'
8
+
9
+ attr_reader :major, :minor, :patch, :pre_release, :is_stable, :version
10
+
11
+ def initialize(version)
12
+ @major = 0
13
+ @minor = 0
14
+ @patch = 0
15
+ @pre_release = []
16
+ @is_stable = false
17
+ @version = ''
18
+ @metadata = ''
19
+ parse(version)
20
+ end
21
+
22
+ #
23
+ # Class builder
24
+ #
25
+ # @param version [String] raw version as read from splitChanges response.
26
+ #
27
+ # @return [type] Semver instance
28
+ def self.build(version, logger)
29
+ new(version)
30
+ rescue NoMethodError => e
31
+ logger.error("Failed to parse Semver data, incorrect data type: #{e}")
32
+ nil
33
+ rescue StandardError => e
34
+ logger.error("Failed to parse Semver data: #{e}")
35
+ nil
36
+ end
37
+
38
+ #
39
+ # Check if there is any metadata characters in version.
40
+ #
41
+ # @return [type] String semver without the metadata
42
+ #
43
+ def remove_metadata_if_exists(old_version)
44
+ index = old_version.index(METADATA_DELIMITER)
45
+ return old_version if index.nil?
46
+
47
+ @metadata = old_version[index + 1, old_version.length]
48
+ old_version[0, index]
49
+ end
50
+
51
+ # Compare the current Semver object to a given Semver object, return:
52
+ # 0: if self == passed
53
+ # 1: if self > passed
54
+ # -1: if self < passed
55
+ #
56
+ # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
57
+ #
58
+ # @returns [Integer] based on comparison
59
+ def compare(to_compare)
60
+ return 0 if @version == to_compare.version
61
+
62
+ # Compare major, minor, and patch versions numerically
63
+ result = compare_attributes(to_compare)
64
+ return result if result != 0
65
+
66
+ # Compare pre-release versions lexically
67
+ compare_pre_release(to_compare)
68
+ end
69
+
70
+ private
71
+
72
+ def integer?(value)
73
+ !!value.match(/^(\d)+$/)
74
+ end
75
+
76
+ #
77
+ # Parse the string in version to update the other internal variables
78
+ #
79
+ def parse(old_version)
80
+ without_metadata = remove_metadata_if_exists(old_version)
81
+
82
+ index = without_metadata.index(PRE_RELEASE_DELIMITER)
83
+ if index.nil?
84
+ @is_stable = true
85
+ else
86
+ pre_release_data = without_metadata[index + 1..-1]
87
+ without_metadata = without_metadata[0, index]
88
+ @pre_release = pre_release_data.split(VALUE_DELIMITER)
89
+ end
90
+ assign_major_minor_and_patch(without_metadata)
91
+ end
92
+
93
+ #
94
+ # Set the major, minor and patch internal variables based on string passed.
95
+ #
96
+ # @param version [String] raw version containing major.minor.patch numbers.
97
+ def assign_major_minor_and_patch(version)
98
+ parts = version.split(VALUE_DELIMITER)
99
+ if parts.length != 3 ||
100
+ !(integer?(parts[0]) &&
101
+ integer?(parts[1]) &&
102
+ integer?(parts[2]))
103
+ raise "Unable to convert to Semver, incorrect format: #{version}"
104
+ end
105
+
106
+ @major = parts[0].to_i
107
+ @minor = parts[1].to_i
108
+ @patch = parts[2].to_i
109
+ @version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}"
110
+ @version += parse_pre_release
111
+ @version += "#{METADATA_DELIMITER}#{@metadata}" unless @metadata.empty?
112
+ end
113
+
114
+ def parse_pre_release
115
+ return '' if @pre_release.empty?
116
+
117
+ pre_parsed = []
118
+ @pre_release.each do |pre_digit|
119
+ pre_digit = pre_digit.to_i if integer?(pre_digit)
120
+ pre_parsed << pre_digit
121
+ end
122
+ "#{PRE_RELEASE_DELIMITER}#{pre_parsed.join('.')}"
123
+ end
124
+
125
+ #
126
+ # Compare 2 variables and return int as follows:
127
+ # 0: if var1 == var2
128
+ # 1: if var1 > var2
129
+ # -1: if var1 < var2
130
+ #
131
+ # @param var1 [type] String/Integer object that accept ==, < or > operators
132
+ # @param var2 [type] String/Integer object that accept ==, < or > operators
133
+ #
134
+ # @returns [Integer] based on comparison
135
+ def compare_vars(var1, var2)
136
+ return 0 if var1 == var2
137
+
138
+ return 1 if var1 > var2
139
+
140
+ -1
141
+ end
142
+
143
+ # Compare the current Semver object's major, minor, patch and is_stable attributes to a given Semver object, return:
144
+ # 0: if self == passed
145
+ # 1: if self > passed
146
+ # -1: if self < passed
147
+ #
148
+ # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
149
+ #
150
+ # @returns [Integer] based on comparison
151
+ def compare_attributes(to_compare)
152
+ result = compare_vars(@major, to_compare.major)
153
+ return result if result != 0
154
+
155
+ result = compare_vars(@minor, to_compare.minor)
156
+ return result if result != 0
157
+
158
+ result = compare_vars(@patch, to_compare.patch)
159
+ return result if result != 0
160
+
161
+ return -1 if !@is_stable && to_compare.is_stable
162
+
163
+ return 1 if @is_stable && !to_compare.is_stable
164
+
165
+ 0
166
+ end
167
+
168
+ # Compare the current Semver object's pre_release attribute to a given Semver object, return:
169
+ # 0: if self == passed
170
+ # 1: if self > passed
171
+ # -1: if self < passed
172
+ #
173
+ # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
174
+ #
175
+ # @returns [Integer] based on comparison
176
+ def compare_pre_release(to_compare)
177
+ min_length = get_pre_min_length(to_compare)
178
+ 0.upto(min_length - 1) do |i|
179
+ next if @pre_release[i] == to_compare.pre_release[i]
180
+
181
+ if integer?(@pre_release[i]) && integer?(to_compare.pre_release[i])
182
+ return compare_vars(@pre_release[i].to_i, to_compare.pre_release[i].to_i)
183
+
184
+ end
185
+
186
+ return compare_vars(@pre_release[i], to_compare.pre_release[i])
187
+ end
188
+ # Compare lengths of pre-release versions
189
+ compare_vars(@pre_release.length, to_compare.pre_release.length)
190
+ end
191
+
192
+ # Get minimum of current Semver object's pre_release attributes length to a given Semver object
193
+ #
194
+ # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
195
+ #
196
+ # @returns [Integer]
197
+ def get_pre_min_length(to_compare)
198
+ [@pre_release.length, to_compare.pre_release.length].min
199
+ end
200
+ end
201
+ end
@@ -10,9 +10,10 @@ module SplitIoClient
10
10
 
11
11
  protected
12
12
 
13
- def initialize(attribute, remote_array)
13
+ def initialize(attribute, remote_array, logger)
14
14
  @attribute = attribute
15
15
  @remote_set = remote_array.to_set
16
+ @logger = logger
16
17
  end
17
18
 
18
19
  def local_set(data, attribute)
@@ -6,21 +6,22 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, prefix_list)
9
+ def initialize(attribute, prefix_list, logger)
10
10
  @attribute = attribute
11
11
  @prefix_list = prefix_list
12
+ @logger = logger
12
13
  end
13
14
 
14
15
  def match?(args)
15
16
  if @prefix_list.empty?
16
- SplitLogger.log_if_debug('[StartsWithMatcher] Prefix List is empty.')
17
+ @logger.log_if_debug('[StartsWithMatcher] Prefix List is empty.')
17
18
  return false
18
19
  end
19
20
 
20
21
  value = get_value(args)
21
22
 
22
23
  matches = @prefix_list.any? { |prefix| value.to_s.start_with? prefix }
23
- SplitLogger.log_if_debug("[StartsWithMatcher] #{value} matches any of #{@prefix_list} -> #{matches}")
24
+ @logger.log_if_debug("[StartsWithMatcher] #{value} matches any of #{@prefix_list} -> #{matches}")
24
25
  matches
25
26
  end
26
27
 
@@ -7,7 +7,8 @@ module SplitIoClient
7
7
  class UserDefinedSegmentMatcher < Matcher
8
8
  MATCHER_TYPE = 'IN_SEGMENT'
9
9
 
10
- def initialize(segments_repository, segment_name)
10
+ def initialize(segments_repository, segment_name, logger)
11
+ super(logger)
11
12
  @segments_repository = segments_repository
12
13
  @segment_name = segment_name
13
14
  end
@@ -20,7 +21,7 @@ module SplitIoClient
20
21
  # @return [boolean] evaluation of the key against the segment
21
22
  def match?(args)
22
23
  matches = @segments_repository.in_segment?(@segment_name, args[:value] || args[:matching_key])
23
- SplitLogger.log_if_debug("[InSegmentMatcher] #{@segment_name} is in segment -> #{matches}")
24
+ @logger.log_if_debug("[InSegmentMatcher] #{@segment_name} is in segment -> #{matches}")
24
25
  matches
25
26
  end
26
27
  end
@@ -9,7 +9,9 @@ module SplitIoClient
9
9
 
10
10
  attr_reader :attribute
11
11
 
12
- def initialize(whitelist_data)
12
+ def initialize(whitelist_data, logger, validator)
13
+ super(logger)
14
+ @validator = validator
13
15
  @whitelist = case whitelist_data
14
16
  when Array
15
17
  whitelist_data
@@ -26,9 +28,9 @@ module SplitIoClient
26
28
  def match?(args)
27
29
  return matches_user_whitelist(args) unless @matcher_type == 'ATTR_WHITELIST'
28
30
 
29
- SplitLogger.log_if_debug('[WhitelistMatcher] evaluating value and attributes.')
31
+ @logger.log_if_debug('[WhitelistMatcher] evaluating value and attributes.')
30
32
 
31
- return false unless SplitIoClient::Validators.valid_matcher_arguments(args)
33
+ return false unless @validator.valid_matcher_arguments(args)
32
34
 
33
35
  matches_attr_whitelist(args)
34
36
  end
@@ -49,14 +51,14 @@ module SplitIoClient
49
51
 
50
52
  def matches_user_whitelist(args)
51
53
  matches = @whitelist.include?(args[:value] || args[:matching_key])
52
- SplitLogger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
54
+ @logger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
53
55
  #{args[:value] || args[:matching_key]} -> #{matches}")
54
56
  matches
55
57
  end
56
58
 
57
59
  def matches_attr_whitelist(args)
58
60
  matches = @whitelist.include?(args[:value] || args[:attributes][@attribute.to_sym])
59
- SplitLogger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
61
+ @logger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
60
62
  #{args[:value] || args[:attributes][@attribute.to_sym]} -> #{matches}")
61
63
  matches
62
64
  end