splitclient-rb 1.0.2.wip → 8.11.1.pre.rc1-java

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 (210) hide show
  1. checksums.yaml +5 -5
  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 +20 -0
  7. data/.rubocop.yml +76 -0
  8. data/.simplecov +1 -0
  9. data/CHANGES.txt +361 -2
  10. data/CONTRIBUTORS-GUIDE.md +49 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE +3 -36
  13. data/NOTICE.txt +5 -0
  14. data/README.md +59 -127
  15. data/Rakefile +27 -3
  16. data/ext/murmurhash/MurmurHash3.java +302 -0
  17. data/gemfiles/faraday_after_0.13.gemfile +7 -0
  18. data/gemfiles/faraday_before_0.13.gemfile +8 -0
  19. data/lib/murmurhash/base.rb +58 -0
  20. data/lib/murmurhash/murmurhash.jar +0 -0
  21. data/lib/murmurhash/murmurhash_mri.rb +3 -0
  22. data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +130 -0
  23. data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
  24. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
  25. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
  26. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
  27. data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
  28. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
  29. data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
  30. data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
  31. data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
  32. data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
  33. data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
  34. data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
  35. data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +45 -0
  36. data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
  37. data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
  38. data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
  39. data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
  40. data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +56 -0
  41. data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
  42. data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
  43. data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
  44. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
  45. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
  46. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
  47. data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
  48. data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
  49. data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
  50. data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
  51. data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
  52. data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +95 -0
  53. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
  54. data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
  55. data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
  56. data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
  57. data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
  58. data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
  59. data/lib/splitclient-rb/clients/split_client.rb +530 -0
  60. data/lib/splitclient-rb/constants.rb +16 -0
  61. data/lib/splitclient-rb/engine/api/client.rb +87 -0
  62. data/lib/splitclient-rb/engine/api/events.rb +57 -0
  63. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
  64. data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
  65. data/lib/splitclient-rb/engine/api/segments.rb +78 -0
  66. data/lib/splitclient-rb/engine/api/splits.rb +139 -0
  67. data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
  68. data/lib/splitclient-rb/engine/auth_api_client.rb +100 -0
  69. data/lib/splitclient-rb/engine/back_off.rb +26 -0
  70. data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
  71. data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
  72. data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
  73. data/lib/{splitclient-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
  74. data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
  75. data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
  76. data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
  77. data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
  78. data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
  79. data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
  80. data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
  81. data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
  82. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
  83. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
  84. data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
  85. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
  86. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
  87. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
  88. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
  89. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
  90. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
  91. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
  92. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
  93. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
  94. data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
  95. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
  96. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
  97. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
  98. data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
  99. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +43 -0
  100. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
  101. data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
  102. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
  103. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
  104. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
  105. data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
  106. data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
  107. data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
  108. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +27 -0
  109. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
  110. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
  111. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
  112. data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
  113. data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
  114. data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
  115. data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
  116. data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
  117. data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
  118. data/lib/splitclient-rb/engine/models/label.rb +10 -0
  119. data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
  120. data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
  121. data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
  122. data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
  123. data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
  124. data/lib/splitclient-rb/engine/models/split.rb +17 -0
  125. data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
  126. data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
  127. data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
  128. data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
  129. data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
  130. data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
  131. data/lib/splitclient-rb/engine/push_manager.rb +66 -0
  132. data/lib/splitclient-rb/engine/status_manager.rb +39 -0
  133. data/lib/splitclient-rb/engine/sync_manager.rb +183 -0
  134. data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
  135. data/lib/splitclient-rb/exceptions.rb +26 -0
  136. data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
  137. data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
  138. data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
  139. data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
  140. data/lib/splitclient-rb/helpers/util.rb +26 -0
  141. data/lib/splitclient-rb/managers/split_manager.rb +121 -0
  142. data/lib/splitclient-rb/spec.rb +9 -0
  143. data/lib/splitclient-rb/split_config.rb +591 -47
  144. data/lib/splitclient-rb/split_factory.rb +284 -0
  145. data/lib/splitclient-rb/split_factory_builder.rb +7 -0
  146. data/lib/splitclient-rb/split_factory_registry.rb +63 -0
  147. data/lib/splitclient-rb/split_logger.rb +23 -0
  148. data/lib/splitclient-rb/sse/event_source/client.rb +264 -0
  149. data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
  150. data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
  151. data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
  152. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
  153. data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
  154. data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
  155. data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
  156. data/lib/splitclient-rb/sse/workers/splits_worker.rb +153 -0
  157. data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
  158. data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
  159. data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
  160. data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
  161. data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
  162. data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
  163. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
  164. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
  165. data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
  166. data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
  167. data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
  168. data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
  169. data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
  170. data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
  171. data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
  172. data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
  173. data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
  174. data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
  175. data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
  176. data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
  177. data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
  178. data/lib/splitclient-rb/utilitites.rb +49 -0
  179. data/lib/splitclient-rb/validators.rb +361 -0
  180. data/lib/splitclient-rb/version.rb +1 -1
  181. data/lib/splitclient-rb.rb +177 -25
  182. data/sonar-project.properties +6 -0
  183. data/splitclient-rb.gemspec +53 -26
  184. data/tasks/benchmark_get_treatment.rake +31 -17
  185. data/tasks/irb.rake +6 -0
  186. metadata +411 -86
  187. data/NEWS +0 -9
  188. data/lib/splitclient-cache/local_store.rb +0 -45
  189. data/lib/splitclient-engine/impressions/impressions.rb +0 -79
  190. data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
  191. data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
  192. data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
  193. data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
  194. data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
  195. data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
  196. data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
  197. data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
  198. data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
  199. data/lib/splitclient-engine/metrics/metrics.rb +0 -158
  200. data/lib/splitclient-engine/parser/condition.rb +0 -143
  201. data/lib/splitclient-engine/parser/segment.rb +0 -84
  202. data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
  203. data/lib/splitclient-engine/parser/split.rb +0 -68
  204. data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
  205. data/lib/splitclient-engine/parser/split_parser.rb +0 -139
  206. data/lib/splitclient-engine/partitions/treatments.rb +0 -40
  207. data/lib/splitclient-rb/split_client.rb +0 -156
  208. data/lib/splitclient-rb_utilitites.rb +0 -26
  209. data/tasks/console.rake +0 -4
  210. data/tasks/rspec.rake +0 -3
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class EqualToSemverMatcher < Matcher
5
+ MATCHER_TYPE = '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, 'EqualsToSemverMatcher')
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 = (@semver.version == value_to_match.version)
24
+ @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
25
+ matches
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class EqualToSetMatcher < SetMatcher
5
+ MATCHER_TYPE = 'EQUAL_TO_SET'
6
+
7
+ attr_reader :attribute
8
+
9
+ def match?(args)
10
+ set = local_set(args[:attributes], @attribute)
11
+ matches = set == @remote_set
12
+ @logger.log_if_debug("[EqualsToSetMatcher] #{set} equals to #{@remote_set} -> #{matches}")
13
+ matches
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class GreaterThanOrEqualToMatcher < Matcher
5
+ MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute_hash, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute_hash[:attribute]
13
+ @data_type = attribute_hash[:data_type]
14
+ @value = formatted_value(attribute_hash[:value], true)
15
+ end
16
+
17
+ def match?(args)
18
+ @logger.log_if_debug('[GreaterThanOrEqualToMatcher] evaluating value and attributes.')
19
+
20
+ return false unless @validator.valid_matcher_arguments(args)
21
+
22
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
23
+
24
+ matches = value.is_a?(Integer) ? (value >= @value) : false
25
+ @logger.log_if_debug("[GreaterThanOrEqualToMatcher] #{value} greater than or equal to #{@value} -> #{matches}")
26
+ matches
27
+ end
28
+
29
+ private
30
+
31
+ def formatted_value(value, sdk_data = false)
32
+ case @data_type
33
+ when 'NUMBER'
34
+ value
35
+ when 'DATETIME'
36
+ value /= 1000 if sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_hour
37
+ SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
38
+ else
39
+ @logger.error('Invalid data type')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -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
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class LessThanOrEqualToMatcher < Matcher
5
+ MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute_hash, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute_hash[:attribute]
13
+ @data_type = attribute_hash[:data_type]
14
+ @value = formatted_value(attribute_hash[:value], true)
15
+ end
16
+
17
+ def match?(args)
18
+ @logger.log_if_debug('[LessThanOrEqualToMatcher] evaluating value and attributes.')
19
+
20
+ return false unless @validator.valid_matcher_arguments(args)
21
+
22
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
23
+
24
+ matches = value.is_a?(Integer) ? (value <= @value) : false
25
+ @logger.log_if_debug("[LessThanOrEqualToMatcher] #{value} less than or equal to #{@value} -> #{matches}")
26
+ matches
27
+ end
28
+
29
+ private
30
+
31
+ def formatted_value(value, sdk_data = false)
32
+ case @data_type
33
+ when 'NUMBER'
34
+ value
35
+ when 'DATETIME'
36
+ value /= 1000 if sdk_data # sdk returns already miliseconds, turning to seconds to do a correct zero_hour
37
+ SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
38
+ else
39
+ @logger.error('Invalid data type')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -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
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ #
5
+ # class to implement the all keys matcher
6
+ #
7
+ class Matcher
8
+ def initialize(logger)
9
+ @logger = logger
10
+ end
11
+
12
+ #
13
+ # evaluates if the given object equals the matcher
14
+ #
15
+ # @param obj [object] object to be evaluated
16
+ #
17
+ # @return [boolean] true if obj equals the matcher
18
+ def equals?(obj)
19
+ if obj.nil?
20
+ false
21
+ elsif !obj.instance_of?(self.class)
22
+ false
23
+ elsif equal?(obj)
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def string_type?
31
+ false
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
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class MatchesStringMatcher
5
+ MATCHER_TYPE = 'MATCHES_STRING'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, regexp_string, logger)
10
+ @attribute = attribute
11
+ @regexp_string = @regexp_string.is_a?(Regexp) ? regexp_string : Regexp.new(regexp_string)
12
+ @logger = logger
13
+ end
14
+
15
+ def match?(args)
16
+ value = args[:value] || args[:attributes].fetch(@attribute) do |a|
17
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
18
+ end
19
+
20
+ matches = !(value =~ @regexp_string).nil?
21
+ @logger.log_if_debug("[MatchesStringMatcher] #{value} matches #{@regexp_string} -> #{matches}")
22
+ matches
23
+ end
24
+
25
+ def string_type?
26
+ true
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ #
5
+ # class to implement the negation of a matcher
6
+ #
7
+ class NegationMatcher < Matcher
8
+ MATCHER_TYPE = 'NEGATION_MATCHER'
9
+
10
+ def initialize(logger, matcher = nil)
11
+ super(logger)
12
+ @matcher = matcher
13
+ end
14
+
15
+ #
16
+ # evaluates if the key matches the negation of the matcher
17
+ #
18
+ # @param key [string] key value to be matched
19
+ #
20
+ # @return [boolean] evaluation of the negation matcher
21
+ def match?(args)
22
+ matches = !@matcher.match?(args)
23
+ @logger.log_if_debug("[NegationMatcherMatcher] Matcher #{@matcher} Arguments #{args} -> #{matches}")
24
+ matches
25
+ end
26
+
27
+ def respond_to?(method)
28
+ @matcher.respond_to? method
29
+ end
30
+
31
+ def attribute
32
+ @matcher.attribute
33
+ end
34
+
35
+ def string_type?
36
+ @matcher.string_type?
37
+ end
38
+
39
+ #
40
+ # function to print string value for this matcher
41
+ #
42
+ # @return [string] string value of this matcher
43
+ def to_s
44
+ "not #{@matcher}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class PartOfSetMatcher < SetMatcher
5
+ MATCHER_TYPE = 'PART_OF_SET'
6
+
7
+ attr_reader :attribute
8
+
9
+ def match?(args)
10
+ @local_set = local_set(args[:attributes], @attribute)
11
+
12
+ if @local_set.empty?
13
+ @logger.log_if_debug('[PartOfSetMatcher] Local Set is empty.')
14
+ return false
15
+ end
16
+
17
+ matches = @local_set.subset? @remote_set
18
+ @logger.log_if_debug("[PartOfSetMatcher] Local Set #{@local_set} is a subset of #{@remote_set} -> #{matches}")
19
+ matches
20
+ end
21
+ end
22
+ 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
+ @config.logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matched}") if @config.debug_enabled
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