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,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ class FallbackTreatmentCalculator
6
+ attr_accessor :fallback_treatments_configuration, :label_prefix
7
+
8
+ def initialize(fallback_treatment_configuration)
9
+ @label_prefix = 'fallback - '
10
+ @fallback_treatments_configuration = fallback_treatment_configuration
11
+ end
12
+
13
+ def resolve(flag_name, label)
14
+ default_fallback_treatment = Engine::Models::FallbackTreatment.new(
15
+ Engine::Models::Treatment::CONTROL,
16
+ nil,
17
+ label
18
+ )
19
+ return default_fallback_treatment if @fallback_treatments_configuration.nil?
20
+
21
+ if !@fallback_treatments_configuration.by_flag_fallback_treatment.nil? \
22
+ && !@fallback_treatments_configuration.by_flag_fallback_treatment.fetch(flag_name, nil).nil?
23
+ return copy_with_label(
24
+ @fallback_treatments_configuration.by_flag_fallback_treatment[flag_name],
25
+ resolve_label(label)
26
+ )
27
+ end
28
+
29
+ return copy_with_label(@fallback_treatments_configuration.global_fallback_treatment, resolve_label(label)) \
30
+ unless @fallback_treatments_configuration.global_fallback_treatment.nil?
31
+
32
+ default_fallback_treatment
33
+ end
34
+
35
+ private
36
+
37
+ def resolve_label(label)
38
+ return nil if label.nil?
39
+
40
+ @label_prefix + label
41
+ end
42
+
43
+ def copy_with_label(fallback_treatment, label)
44
+ Engine::Models::FallbackTreatment.new(fallback_treatment.treatment, fallback_treatment.config, label)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Impressions
6
+ class NoopUniqueKeysTracker
7
+ def call
8
+ # no-op
9
+ end
10
+
11
+ def track(feature_name, key)
12
+ # no-op
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Impressions
6
+ class UniqueKeysTracker
7
+ INTERVAL_TO_CLEAR_LONG_TERM_CACHE = 86_400 # 24 hours
8
+
9
+ def initialize(config,
10
+ filter_adapter,
11
+ sender_adapter,
12
+ cache)
13
+ @config = config
14
+ @filter_adapter = filter_adapter
15
+ @sender_adapter = sender_adapter
16
+ @cache = cache
17
+ @max_bulk_size = config.unique_keys_bulk_size
18
+ @semaphore = Mutex.new
19
+ @keys_size = 0
20
+ end
21
+
22
+ def call
23
+ @config.threads[:unique_keys_sender] = Thread.new { send_bulk_data_thread }
24
+ @config.threads[:clear_filter] = Thread.new { clear_filter_thread }
25
+ end
26
+
27
+ def track(feature_name, key)
28
+ return false if @filter_adapter.contains?(feature_name, key)
29
+
30
+ @filter_adapter.add(feature_name, key)
31
+
32
+ add_or_update(feature_name, key)
33
+ @keys_size += 1
34
+
35
+ send_bulk_data if @keys_size >= @max_bulk_size
36
+
37
+ true
38
+ rescue StandardError => e
39
+ @config.log_found_exception(__method__.to_s, e)
40
+ false
41
+ end
42
+
43
+ private
44
+
45
+ def send_bulk_data_thread
46
+ @config.logger.info('Starting Unique Keys Tracker.') if @config.debug_enabled
47
+ loop do
48
+ sleep(@config.unique_keys_refresh_rate)
49
+ send_bulk_data
50
+ end
51
+ rescue SplitIoClient::SDKShutdownException
52
+ send_bulk_data
53
+ @config.logger.info('Posting unique keys due to shutdown')
54
+ end
55
+
56
+ def clear_filter_thread
57
+ loop do
58
+ sleep(INTERVAL_TO_CLEAR_LONG_TERM_CACHE)
59
+ @config.logger.debug('Starting task to clean the filter cache.') if @config.debug_enabled
60
+ @filter_adapter.clear
61
+ end
62
+ rescue SplitIoClient::SDKShutdownException
63
+ @filter_adapter.clear
64
+ end
65
+
66
+ def add_or_update(feature_name, key)
67
+ if @cache[feature_name].nil?
68
+ @cache[feature_name] = Set.new([key])
69
+ else
70
+ @cache[feature_name].add(key)
71
+ end
72
+ end
73
+
74
+ def clear_cache
75
+ uniques = @cache.clone
76
+ keys_size = @keys_size
77
+ @cache.clear
78
+ @keys_size = 0
79
+
80
+ [uniques, keys_size]
81
+ end
82
+
83
+ def send_bulk_data
84
+ @semaphore.synchronize do
85
+ return if @cache.empty?
86
+
87
+ uniques, keys_size = clear_cache
88
+ if keys_size <= @max_bulk_size
89
+ @sender_adapter.record_uniques_key(uniques)
90
+ return
91
+
92
+ end
93
+ bulks = flatten_bulks(uniques)
94
+ bulks_to_post = group_bulks_by_max_size(bulks)
95
+ @sender_adapter.record_uniques_key(bulks_to_post)
96
+ end
97
+ rescue StandardError => e
98
+ @config.log_found_exception(__method__.to_s, e)
99
+ end
100
+
101
+ def group_bulks_by_max_size(bulks)
102
+ current_size = 0
103
+ bulks_to_post = Concurrent::Hash.new
104
+ bulks.each do |bulk|
105
+ key, value = bulk.first
106
+ if (value.size + current_size) > @max_bulk_size
107
+ @sender_adapter.record_uniques_key(bulks_to_post)
108
+ bulks_to_post = Concurrent::Hash.new
109
+ current_size = 0
110
+ end
111
+ bulks_to_post[key] = value
112
+ current_size += value.size
113
+ end
114
+
115
+ bulks_to_post
116
+ end
117
+
118
+ def flatten_bulks(uniques)
119
+ bulks = []
120
+ uniques.each_key do |unique_key|
121
+ bulks += check_keys_and_split_to_bulks(uniques[unique_key], unique_key)
122
+ end
123
+
124
+ bulks
125
+ end
126
+
127
+ def check_keys_and_split_to_bulks(value, key)
128
+ unique_updated = []
129
+ if value.size > @max_bulk_size
130
+ sub_bulks = SplitIoClient::Utilities.split_bulk_to_send(value, @max_bulk_size)
131
+ sub_bulks.each do |sub_bulk|
132
+ unique_updated << { key => sub_bulk.to_set }
133
+ end
134
+ return unique_updated
135
+
136
+ end
137
+ unique_updated << { key => value }
138
+
139
+ unique_updated
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -1,23 +1,18 @@
1
- module SplitIoClient
1
+ # frozen_string_literal: true
2
2
 
3
+ module SplitIoClient
3
4
  #
4
5
  # class to implement the all keys matcher
5
6
  #
6
- class AllKeysMatcher < NoMethodError
7
-
8
- attr_reader :matcher_type
9
-
10
- def initialize
11
- @matcher_type = "ALL_KEYS"
12
- end
7
+ class AllKeysMatcher < Matcher
8
+ MATCHER_TYPE = 'ALL_KEYS'
13
9
 
14
10
  #
15
11
  # evaluates if the key matches the matcher
16
12
  #
17
- # @param key [string] key value to be matched
18
- #
19
13
  # @return [boolean] true for all instances
20
- def match?(key)
14
+ def match?(_args)
15
+ @logger.log_if_debug('[AllKeysMatcher] is always -> true')
21
16
  true
22
17
  end
23
18
 
@@ -26,27 +21,21 @@ module SplitIoClient
26
21
  #
27
22
  # @param obj [object] object to be evaluated
28
23
  #
29
- # @returns [boolean] true if obj equals the matcher
24
+ # @return [boolean] true if obj equals the matcher
30
25
  def equals?(obj)
31
- if obj.nil?
32
- false
33
- elsif self.equal?(obj)
26
+ if obj.instance_of?(AllKeysMatcher)
34
27
  true
35
- elsif !obj.instance_of?(AllKeysMatcher)
36
- false
37
28
  else
38
- true
29
+ super(obj)
39
30
  end
40
31
  end
41
32
 
42
33
  #
43
34
  # function to print string value for this matcher
44
35
  #
45
- # @reutrn [string] string value of this matcher
36
+ # @return [string] string value of this matcher
46
37
  def to_s
47
38
  'in segment all'
48
39
  end
49
-
50
40
  end
51
-
52
41
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class BetweenMatcher < Matcher
5
+ MATCHER_TYPE = 'BETWEEN'
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
+ @start_value = formatted_value(attribute_hash[:start_value], true)
15
+ @end_value = formatted_value(attribute_hash[:end_value], true)
16
+ end
17
+
18
+ def match?(args)
19
+ @logger.log_if_debug('[BetweenMatcher] evaluating value and attributes.')
20
+
21
+ return false unless @validator.valid_matcher_arguments(args)
22
+
23
+ value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
24
+ @logger.log_if_debug("[BetweenMatcher] Value from parameters: #{value}.")
25
+ return false unless value.is_a?(Integer)
26
+
27
+ matches = (@start_value..@end_value).cover? value
28
+ @logger.log_if_debug("[BetweenMatcher] is #{value} between #{@start_value} and #{@end_value} -> #{matches} .")
29
+ matches
30
+ end
31
+
32
+ private
33
+
34
+ def formatted_value(value, sdk_data = false)
35
+ case @data_type
36
+ when 'NUMBER'
37
+ value
38
+ when 'DATETIME'
39
+ value /= 1000 if sdk_data
40
+
41
+ SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
42
+ else
43
+ @logger.error('Invalid data type')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class BetweenSemverMatcher < Matcher
5
+ MATCHER_TYPE = 'BETWEEN_SEMVER'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, start_value, end_value, logger, validator)
10
+ super(logger)
11
+ @validator = validator
12
+ @attribute = attribute
13
+ @semver_start = SplitIoClient::Semver.build(start_value, logger)
14
+ @semver_end = SplitIoClient::Semver.build(end_value, logger)
15
+ @logger = logger
16
+ end
17
+
18
+ def match?(args)
19
+ return false unless verify_semver_arg?(args, 'BetweenSemverMatcher')
20
+
21
+ value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
22
+ if value_to_match.nil? || @semver_start.nil? || @semver_end.nil?
23
+ @logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type')
24
+ return false
25
+
26
+ end
27
+ matches = ([0, -1].include?(@semver_start.compare(value_to_match)) &&
28
+ [0, 1].include?(@semver_end.compare(value_to_match)))
29
+ @logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}")
30
+ matches
31
+ end
32
+ end
33
+ end
@@ -1,13 +1,11 @@
1
- module SplitIoClient
1
+ # frozen_string_literal: true
2
2
 
3
+ module SplitIoClient
3
4
  #
4
5
  # class to represent combiner values
5
6
  #
6
- class Combiners < NoMethodError
7
-
7
+ class Combiners
8
8
  # available combiners of the sdk
9
9
  AND = 'AND'
10
-
11
10
  end
12
-
13
- end
11
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ #
5
+ # class to implement the combining matcher
6
+ #
7
+ class CombiningMatcher < Matcher
8
+ MATCHER_TYPE = 'COMBINING_MATCHER'
9
+
10
+ def initialize(logger, combiner = '', matchers = [])
11
+ super(logger)
12
+ @combiner = combiner
13
+ @matchers = matchers
14
+ end
15
+
16
+ #
17
+ # evaluates if the key matches the matchers within the combiner
18
+ #
19
+ # @param matching_key [string] key value to be matched
20
+ # @param bucketing_key [string] bucketing key to be matched
21
+ # @param evaluator [instance of Evaluator class]
22
+ # @param attributes [hash]
23
+ #
24
+ # @return [boolean]
25
+ def match?(args)
26
+ if @matchers.empty?
27
+ @logger.log_if_debug('[CombiningMatcher] Matchers Empty')
28
+ return false
29
+ end
30
+
31
+ case @combiner
32
+ when Combiners::AND
33
+ matches = eval_and(args)
34
+ @logger.log_if_debug("[CombiningMatcher] Combiner AND result -> #{matches}")
35
+ return matches
36
+ else
37
+ @logger.log_if_debug("[CombiningMatcher] Invalid Combiner Type - Combiner -> #{@combiner}")
38
+ @logger.error('Invalid combiner type')
39
+ end
40
+
41
+ false
42
+ end
43
+
44
+ #
45
+ # auxiliary method to evaluate each of the matchers within the combiner
46
+ #
47
+ # @param matching_key [string] key value to be matched
48
+ # @param bucketing_key [string] bucketing key to be matched
49
+ # @param evaluator [Evaluator] used in dependency_matcher
50
+ # @param attributes [hash] attributes to pass to the treatment class
51
+ #
52
+ # @return [boolean] match value for combiner delegates
53
+ def eval_and(args)
54
+ # Convert all keys to symbols
55
+ args[:attributes] = args[:attributes].each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v } if args && args[:attributes]
56
+
57
+ @matchers.all? do |matcher|
58
+ if match_with_key?(matcher)
59
+ key = args[:value]
60
+ key = args[:matching_key] unless args[:matching_key].nil?
61
+ matcher.match?(value: key)
62
+ else
63
+ matcher.match?(args)
64
+ end
65
+ end
66
+ end
67
+
68
+ def match_with_key?(matcher)
69
+ matcher.respond_to?(:attribute) && matcher.attribute.nil? && matcher.string_type?
70
+ end
71
+
72
+ #
73
+ # function to print string value for this matcher
74
+ #
75
+ # @return [string] string value of this matcher
76
+ def to_s
77
+ @matcher_list.map(&:to_s).join("#{@combiner} ")
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class ContainsAllMatcher < SetMatcher
5
+ MATCHER_TYPE = 'CONTAINS_ALL'
6
+
7
+ attr_reader :attribute
8
+
9
+ def match?(args)
10
+ if @remote_set.empty?
11
+ @logger.log_if_debug('[ContainsAllMatcher] Remote Set Empty')
12
+ return false
13
+ end
14
+
15
+ matches = @remote_set.subset? local_set(args[:attributes], @attribute)
16
+ @logger.log_if_debug("[ContainsAllMatcher] Remote Set #{@remote_set} contains #{@attribute} -> #{matches}")
17
+ matches
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class ContainsAnyMatcher < SetMatcher
5
+ MATCHER_TYPE = 'CONTAINS_ANY'
6
+
7
+ attr_reader :attribute
8
+
9
+ def match?(args)
10
+ matches = local_set(args[:attributes], @attribute).intersect? @remote_set
11
+ @logger.log_if_debug("[ContainsAnyMatcher] Remote Set #{@remote_set} contains any \
12
+ #{@attribute} or #{args[:attributes]}-> #{matches}")
13
+ matches
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class ContainsMatcher
5
+ MATCHER_TYPE = 'CONTAINS_WITH'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, substr_list, logger, validator)
10
+ @attribute = attribute
11
+ @substr_list = substr_list
12
+ @logger = logger
13
+ @validator = validator
14
+ end
15
+
16
+ def match?(args)
17
+ @logger.log_if_debug('[ContainsMatcher] evaluating value and attributes.')
18
+
19
+ return false unless @validator.valid_matcher_arguments(args)
20
+
21
+ value = get_value(args)
22
+
23
+ @logger.log_if_debug("[ContainsMatcher] Value from parameters: #{value}.")
24
+ return false if @substr_list.empty?
25
+
26
+ matches = @substr_list.any? { |substr| value.to_s.include? substr }
27
+ @logger.log_if_debug("[ContainsMatcher] #{@value} contains any of #{@substr_list} -> #{matches} .")
28
+ matches
29
+ end
30
+
31
+ def string_type?
32
+ true
33
+ end
34
+
35
+ private
36
+
37
+ def get_value(args)
38
+ args[:value] || args[:attributes].fetch(@attribute) do |a|
39
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class DependencyMatcher
5
+ MATCHER_TYPE = 'IN_SPLIT_TREATMENT'
6
+
7
+ def initialize(feature_flag, treatments, logger)
8
+ @feature_flag = feature_flag
9
+ @treatments = treatments
10
+ @logger = logger
11
+ end
12
+
13
+ def match?(args)
14
+ keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
15
+ evaluate = args[:evaluator].evaluate_feature_flag(keys, @feature_flag, args[:attributes])
16
+ matches = @treatments.include?(evaluate[:treatment])
17
+ @logger.log_if_debug("[dependencyMatcher] Parent feature flag #{@feature_flag} evaluated to #{evaluate[:treatment]} \
18
+ with label #{evaluate[:label]}. #{@feature_flag} evaluated treatment is part of [#{@treatments}] ? #{matches}.")
19
+ matches
20
+ end
21
+
22
+ def string_type?
23
+ false
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class EndsWithMatcher
5
+ MATCHER_TYPE = 'ENDS_WITH'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, suffix_list, logger)
10
+ @attribute = attribute
11
+ @suffix_list = suffix_list
12
+ @logger = logger
13
+ end
14
+
15
+ def match?(args)
16
+ value = get_value(args)
17
+
18
+ @logger.log_if_debug("[EndsWithMatcher] Value from parameters: #{value}.")
19
+
20
+ if @suffix_list.empty?
21
+ @logger.log_if_debug('[EndsWithMatcher] Sufix List empty.')
22
+ return false
23
+ end
24
+
25
+ matches = @suffix_list.any? { |suffix| value.to_s.end_with? suffix }
26
+ @logger.log_if_debug("[EndsWithMatcher] #{value} ends with any #{@suffix_list}")
27
+ matches
28
+ end
29
+
30
+ def string_type?
31
+ true
32
+ end
33
+
34
+ private
35
+
36
+ def get_value(args)
37
+ args[:value] || args[:attributes].fetch(@attribute) do |a|
38
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class EqualToBooleanMatcher
5
+ MATCHER_TYPE = 'EQUAL_TO_BOOLEAN'
6
+
7
+ attr_reader :attribute
8
+
9
+ def initialize(attribute, boolean, logger)
10
+ @attribute = attribute
11
+ @boolean = boolean
12
+ @logger = logger
13
+ end
14
+
15
+ def match?(args)
16
+ value = get_value(args)
17
+ value = false if value.to_s.casecmp('false').zero?
18
+ value = true if value.to_s.casecmp('true').zero?
19
+
20
+ matches = value == @boolean
21
+ @logger.log_if_debug("[EqualToBooleanMatcher] #{value} equals to #{@boolean} -> #{matches}")
22
+ matches
23
+ end
24
+
25
+ def string_type?
26
+ false
27
+ end
28
+
29
+ private
30
+
31
+ def get_value(args)
32
+ args[:attributes].fetch(@attribute) do |a|
33
+ args[:attributes][a.to_s] || args[:attributes][a.to_sym]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ class EqualToMatcher < Matcher
5
+ MATCHER_TYPE = '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('[EqualsToMatcher] 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("[EqualsToMatcher] #{value} equals 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
37
+
38
+ SplitIoClient::Utilities.to_milis_zero_out_from_hour value
39
+ else
40
+ @logger.error('Invalid data type')
41
+ end
42
+ end
43
+ end
44
+ end