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,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class EventsTask
7
+ attr_accessor :running
8
+
9
+ def initialize(notify_internal_events, internal_events_queue, config)
10
+ @notify_internal_events = notify_internal_events
11
+ @internal_events_queue = internal_events_queue
12
+ @config = config
13
+ @running = false
14
+ end
15
+
16
+ def start
17
+ return if @running
18
+
19
+ @config.logger.info('Starting Internal Events Task.')
20
+ @running = true
21
+ @config.threads[:internal_events_task] = Thread.new do
22
+ worker_thread
23
+ end
24
+ end
25
+
26
+ def stop
27
+ return unless @running
28
+
29
+ @config.logger.info('Stopping Internal Events Task.')
30
+ @running = false
31
+ end
32
+
33
+ private
34
+
35
+ def worker_thread
36
+ while (event = @internal_events_queue.pop)
37
+ break unless @running
38
+
39
+ @config.logger.debug("Processing sdk internal event: #{event.internal_event}") if @config.debug_enabled
40
+ begin
41
+ @notify_internal_events.call(event.internal_event, event.metadata)
42
+ rescue StandardError => e
43
+ @config.log_found_exception(__method__.to_s, e)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class NoOpEventsQueue
7
+ def push(sdk_event)
8
+ # do nothing
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -12,7 +12,7 @@ module SplitIoClient
12
12
  #
13
13
  # @return [boolean] true for all instances
14
14
  def match?(_args)
15
- SplitLogger.log_if_debug('[AllKeysMatcher] is always -> true')
15
+ @logger.log_if_debug('[AllKeysMatcher] is always -> true')
16
16
  true
17
17
  end
18
18
 
@@ -6,7 +6,9 @@ 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
  @start_value = formatted_value(attribute_hash[:start_value], true)
@@ -14,16 +16,16 @@ module SplitIoClient
14
16
  end
15
17
 
16
18
  def match?(args)
17
- SplitLogger.log_if_debug('[BetweenMatcher] evaluating value and attributes.')
19
+ @logger.log_if_debug('[BetweenMatcher] evaluating value and attributes.')
18
20
 
19
- return false unless SplitIoClient::Validators.valid_matcher_arguments(args)
21
+ return false unless @validator.valid_matcher_arguments(args)
20
22
 
21
23
  value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
22
- SplitLogger.log_if_debug("[BetweenMatcher] Value from parameters: #{value}.")
24
+ @logger.log_if_debug("[BetweenMatcher] Value from parameters: #{value}.")
23
25
  return false unless value.is_a?(Integer)
24
26
 
25
27
  matches = (@start_value..@end_value).cover? value
26
- SplitLogger.log_if_debug("[BetweenMatcher] is #{value} between #{@start_value} and #{@end_value} -> #{matches} .")
28
+ @logger.log_if_debug("[BetweenMatcher] is #{value} between #{@start_value} and #{@end_value} -> #{matches} .")
27
29
  matches
28
30
  end
29
31
 
@@ -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
@@ -7,7 +7,8 @@ module SplitIoClient
7
7
  class CombiningMatcher < Matcher
8
8
  MATCHER_TYPE = 'COMBINING_MATCHER'
9
9
 
10
- def initialize(combiner = '', matchers = [])
10
+ def initialize(logger, combiner = '', matchers = [])
11
+ super(logger)
11
12
  @combiner = combiner
12
13
  @matchers = matchers
13
14
  end
@@ -23,17 +24,17 @@ module SplitIoClient
23
24
  # @return [boolean]
24
25
  def match?(args)
25
26
  if @matchers.empty?
26
- SplitLogger.log_if_debug('[CombiningMatcher] Matchers Empty')
27
+ @logger.log_if_debug('[CombiningMatcher] Matchers Empty')
27
28
  return false
28
29
  end
29
30
 
30
31
  case @combiner
31
32
  when Combiners::AND
32
33
  matches = eval_and(args)
33
- SplitLogger.log_if_debug("[CombiningMatcher] Combiner AND result -> #{matches}")
34
+ @logger.log_if_debug("[CombiningMatcher] Combiner AND result -> #{matches}")
34
35
  return matches
35
36
  else
36
- SplitLogger.log_if_debug("[CombiningMatcher] Invalid Combiner Type - Combiner -> #{@combiner}")
37
+ @logger.log_if_debug("[CombiningMatcher] Invalid Combiner Type - Combiner -> #{@combiner}")
37
38
  @logger.error('Invalid combiner type')
38
39
  end
39
40
 
@@ -51,12 +52,13 @@ module SplitIoClient
51
52
  # @return [boolean] match value for combiner delegates
52
53
  def eval_and(args)
53
54
  # Convert all keys to symbols
54
- if args && args[:attributes]
55
- args[:attributes] = args[:attributes].each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
56
- end
55
+ args[:attributes] = args[:attributes].each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v } if args && args[:attributes]
56
+
57
57
  @matchers.all? do |matcher|
58
58
  if match_with_key?(matcher)
59
- matcher.match?(value: args[:matching_key])
59
+ key = args[:value]
60
+ key = args[:matching_key] unless args[:matching_key].nil?
61
+ matcher.match?(value: key)
60
62
  else
61
63
  matcher.match?(args)
62
64
  end
@@ -6,18 +6,14 @@ 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
  if @remote_set.empty?
15
- SplitLogger.log_if_debug('[ContainsAllMatcher] Remote Set Empty')
11
+ @logger.log_if_debug('[ContainsAllMatcher] Remote Set Empty')
16
12
  return false
17
13
  end
18
14
 
19
15
  matches = @remote_set.subset? local_set(args[:attributes], @attribute)
20
- SplitLogger.log_if_debug("[ContainsAllMatcher] Remote Set #{@remote_set} contains #{@attribute} -> #{matches}")
16
+ @logger.log_if_debug("[ContainsAllMatcher] Remote Set #{@remote_set} contains #{@attribute} -> #{matches}")
21
17
  matches
22
18
  end
23
19
  end
@@ -6,13 +6,9 @@ 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
  matches = local_set(args[:attributes], @attribute).intersect? @remote_set
15
- SplitLogger.log_if_debug("[ContainsAnyMatcher] Remote Set #{@remote_set} contains any \
11
+ @logger.log_if_debug("[ContainsAnyMatcher] Remote Set #{@remote_set} contains any \
16
12
  #{@attribute} or #{args[:attributes]}-> #{matches}")
17
13
  matches
18
14
  end
@@ -6,23 +6,25 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, substr_list)
9
+ def initialize(attribute, substr_list, logger, validator)
10
10
  @attribute = attribute
11
11
  @substr_list = substr_list
12
+ @logger = logger
13
+ @validator = validator
12
14
  end
13
15
 
14
16
  def match?(args)
15
- SplitLogger.log_if_debug('[ContainsMatcher] evaluating value and attributes.')
17
+ @logger.log_if_debug('[ContainsMatcher] evaluating value and attributes.')
16
18
 
17
- return false unless SplitIoClient::Validators.valid_matcher_arguments(args)
19
+ return false unless @validator.valid_matcher_arguments(args)
18
20
 
19
21
  value = get_value(args)
20
22
 
21
- SplitLogger.log_if_debug("[ContainsMatcher] Value from parameters: #{value}.")
23
+ @logger.log_if_debug("[ContainsMatcher] Value from parameters: #{value}.")
22
24
  return false if @substr_list.empty?
23
25
 
24
26
  matches = @substr_list.any? { |substr| value.to_s.include? substr }
25
- SplitLogger.log_if_debug("[ContainsMatcher] #{@value} contains any of #{@substr_list} -> #{matches} .")
27
+ @logger.log_if_debug("[ContainsMatcher] #{@value} contains any of #{@substr_list} -> #{matches} .")
26
28
  matches
27
29
  end
28
30
 
@@ -4,17 +4,18 @@ module SplitIoClient
4
4
  class DependencyMatcher
5
5
  MATCHER_TYPE = 'IN_SPLIT_TREATMENT'
6
6
 
7
- def initialize(split, treatments)
8
- @split = split
7
+ def initialize(feature_flag, treatments, logger)
8
+ @feature_flag = feature_flag
9
9
  @treatments = treatments
10
+ @logger = logger
10
11
  end
11
12
 
12
13
  def match?(args)
13
14
  keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
14
- evaluate = args[:evaluator].call(keys, @split, args[:attributes])
15
+ evaluate = args[:evaluator].evaluate_feature_flag(keys, @feature_flag, args[:attributes])
15
16
  matches = @treatments.include?(evaluate[:treatment])
16
- SplitLogger.log_if_debug("[dependencyMatcher] Parent split #{@split} evaluated to #{evaluate[:treatment]} \
17
- with label #{evaluate[:label]}. #{@split} evaluated treatment is part of [#{@treatments}] ? #{matches}.")
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}.")
18
19
  matches
19
20
  end
20
21
 
@@ -6,23 +6,24 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, suffix_list)
9
+ def initialize(attribute, suffix_list, logger)
10
10
  @attribute = attribute
11
11
  @suffix_list = suffix_list
12
+ @logger = logger
12
13
  end
13
14
 
14
15
  def match?(args)
15
16
  value = get_value(args)
16
17
 
17
- SplitLogger.log_if_debug("[EndsWithMatcher] Value from parameters: #{value}.")
18
+ @logger.log_if_debug("[EndsWithMatcher] Value from parameters: #{value}.")
18
19
 
19
20
  if @suffix_list.empty?
20
- SplitLogger.log_if_debug('[EndsWithMatcher] Sufix List empty.')
21
+ @logger.log_if_debug('[EndsWithMatcher] Sufix List empty.')
21
22
  return false
22
23
  end
23
24
 
24
25
  matches = @suffix_list.any? { |suffix| value.to_s.end_with? suffix }
25
- SplitLogger.log_if_debug("[EndsWithMatcher] #{value} ends with any #{@suffix_list}")
26
+ @logger.log_if_debug("[EndsWithMatcher] #{value} ends with any #{@suffix_list}")
26
27
  matches
27
28
  end
28
29
 
@@ -6,9 +6,10 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :attribute
8
8
 
9
- def initialize(attribute, boolean)
9
+ def initialize(attribute, boolean, logger)
10
10
  @attribute = attribute
11
11
  @boolean = boolean
12
+ @logger = logger
12
13
  end
13
14
 
14
15
  def match?(args)
@@ -17,7 +18,7 @@ module SplitIoClient
17
18
  value = true if value.to_s.casecmp('true').zero?
18
19
 
19
20
  matches = value == @boolean
20
- SplitLogger.log_if_debug("[EqualToBooleanMatcher] #{value} equals to #{@boolean} -> #{matches}")
21
+ @logger.log_if_debug("[EqualToBooleanMatcher] #{value} equals to #{@boolean} -> #{matches}")
21
22
  matches
22
23
  end
23
24
 
@@ -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('[EqualsToMatcher] evaluating value and attributes.')
18
+ @logger.log_if_debug('[EqualsToMatcher] 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("[EqualsToMatcher] #{value} equals to #{@value} -> #{matches}")
25
+ @logger.log_if_debug("[EqualsToMatcher] #{value} equals 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 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
@@ -6,14 +6,10 @@ 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
  set = local_set(args[:attributes], @attribute)
15
11
  matches = set == @remote_set
16
- SplitLogger.log_if_debug("[EqualsToSetMatcher] #{set} equals to #{@remote_set} -> #{matches}")
12
+ @logger.log_if_debug("[EqualsToSetMatcher] #{set} equals to #{@remote_set} -> #{matches}")
17
13
  matches
18
14
  end
19
15
  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('[GreaterThanOrEqualToMatcher] evaluating value and attributes.')
18
+ @logger.log_if_debug('[GreaterThanOrEqualToMatcher] 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("[GreaterThanOrEqualToMatcher] #{value} greater than or equal to #{@value} -> #{matches}")
25
+ @logger.log_if_debug("[GreaterThanOrEqualToMatcher] #{value} greater than or equal to #{@value} -> #{matches}")
24
26
  matches
25
27
  end
26
28