vwo-fme-ruby-sdk 1.0.0 → 1.1.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vwo/api/get_flag.rb +236 -0
  3. data/lib/vwo/api/set_attribute.rb +57 -0
  4. data/lib/vwo/api/track_event.rb +77 -0
  5. data/lib/vwo/constants/constants.rb +54 -0
  6. data/lib/vwo/decorators/storage_decorator.rb +86 -0
  7. data/lib/vwo/{utils/logger_helper.rb → enums/api_enum.rb} +5 -10
  8. data/lib/vwo/enums/campaign_type_enum.rb +19 -0
  9. data/lib/vwo/enums/decision_types_enum.rb +18 -0
  10. data/lib/vwo/enums/event_enum.rb +19 -0
  11. data/lib/vwo/enums/headers_enum.rb +20 -0
  12. data/lib/vwo/enums/hooks_enum.rb +17 -0
  13. data/lib/vwo/enums/http_method_enum.rb +18 -0
  14. data/lib/vwo/enums/log_level_enum.rb +21 -0
  15. data/lib/vwo/enums/status_enum.rb +19 -0
  16. data/lib/vwo/enums/storage_enum.rb +22 -0
  17. data/lib/vwo/enums/url_enum.rb +21 -0
  18. data/lib/vwo/models/campaign/campaign_model.rb +192 -0
  19. data/lib/vwo/models/campaign/feature_model.rb +111 -0
  20. data/lib/vwo/models/campaign/impact_campaign_model.rb +38 -0
  21. data/lib/vwo/models/campaign/metric_model.rb +44 -0
  22. data/lib/vwo/models/campaign/rule_model.rb +56 -0
  23. data/lib/vwo/models/campaign/variable_model.rb +51 -0
  24. data/lib/vwo/models/campaign/variation_model.rb +137 -0
  25. data/lib/vwo/models/gateway_service_model.rb +39 -0
  26. data/lib/vwo/models/schemas/settings_schema_validation.rb +102 -0
  27. data/lib/vwo/models/settings/settings_model.rb +85 -0
  28. data/lib/vwo/models/storage/storage_data_model.rb +44 -0
  29. data/lib/vwo/models/user/context_model.rb +100 -0
  30. data/lib/vwo/models/user/context_vwo_model.rb +38 -0
  31. data/lib/vwo/{utils/feature_flag_response.rb → models/user/get_flag_response.rb} +14 -14
  32. data/lib/vwo/models/vwo_options_model.rb +107 -0
  33. data/lib/vwo/packages/decision_maker/decision_maker.rb +60 -0
  34. data/lib/vwo/packages/logger/core/log_manager.rb +90 -0
  35. data/lib/vwo/packages/logger/core/transport_manager.rb +87 -0
  36. data/lib/vwo/packages/logger/log_message_builder.rb +70 -0
  37. data/lib/vwo/packages/logger/logger.rb +38 -0
  38. data/lib/vwo/packages/logger/transports/console_transport.rb +49 -0
  39. data/lib/vwo/packages/network_layer/client/network_client.rb +107 -0
  40. data/lib/vwo/packages/network_layer/handlers/request_handler.rb +37 -0
  41. data/lib/vwo/packages/network_layer/manager/network_manager.rb +78 -0
  42. data/lib/vwo/packages/network_layer/models/global_request_model.rb +105 -0
  43. data/lib/vwo/packages/network_layer/models/request_model.rb +145 -0
  44. data/lib/vwo/packages/network_layer/models/response_model.rb +45 -0
  45. data/lib/vwo/packages/segmentation_evaluator/core/segmentation_manager.rb +76 -0
  46. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
  47. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
  48. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +30 -0
  49. data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +210 -0
  50. data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +198 -0
  51. data/lib/vwo/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
  52. data/lib/vwo/{constants.rb → packages/storage/connector.rb} +12 -10
  53. data/lib/vwo/packages/storage/storage.rb +45 -0
  54. data/lib/vwo/services/campaign_decision_service.rb +153 -0
  55. data/lib/vwo/services/hooks_service.rb +51 -0
  56. data/lib/vwo/services/logger_service.rb +83 -0
  57. data/lib/vwo/services/settings_service.rb +120 -0
  58. data/lib/vwo/services/storage_service.rb +65 -0
  59. data/lib/vwo/utils/campaign_util.rb +249 -0
  60. data/lib/vwo/utils/data_type_util.rb +105 -0
  61. data/lib/vwo/utils/decision_util.rb +253 -0
  62. data/lib/vwo/utils/function_util.rb +123 -0
  63. data/lib/vwo/utils/gateway_service_util.rb +101 -0
  64. data/lib/vwo/utils/impression_util.rb +49 -0
  65. data/lib/vwo/utils/log_message_util.rb +42 -0
  66. data/lib/vwo/utils/meg_util.rb +350 -0
  67. data/lib/vwo/utils/network_util.rb +235 -0
  68. data/lib/vwo/utils/rule_evaluation_util.rb +57 -0
  69. data/lib/vwo/utils/settings_util.rb +38 -0
  70. data/lib/vwo/utils/url_util.rb +46 -0
  71. data/lib/vwo/utils/uuid_util.rb +55 -0
  72. data/lib/vwo/vwo_builder.rb +156 -11
  73. data/lib/vwo/vwo_client.rb +163 -113
  74. data/lib/vwo.rb +49 -31
  75. metadata +187 -9
  76. data/lib/vwo/utils/request.rb +0 -89
@@ -0,0 +1,120 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'json'
16
+ require_relative '../packages/network_layer/manager/network_manager'
17
+ require_relative '../packages/network_layer/models/request_model'
18
+ require_relative '../packages/network_layer/models/response_model'
19
+ require_relative '../constants/constants'
20
+ require_relative '../utils/network_util'
21
+ require_relative '../services/logger_service'
22
+ require_relative '../enums/log_level_enum'
23
+ require_relative '../models/schemas/settings_schema_validation'
24
+
25
+ class SettingsService
26
+ attr_accessor :sdk_key, :account_id, :expiry, :network_timeout, :hostname, :port, :protocol, :is_gateway_service_provided
27
+
28
+ class << self
29
+ attr_accessor :instance
30
+
31
+ def get_instance
32
+ @instance ||= SettingsService.new
33
+ end
34
+ end
35
+
36
+ def initialize(options)
37
+ @sdk_key = options[:sdk_key]
38
+ @account_id = options[:account_id]
39
+ @expiry = options.dig(:settings, :expiry) || Constants::SETTINGS_EXPIRY
40
+ @network_timeout = options.dig(:settings, :timeout) || Constants::SETTINGS_TIMEOUT
41
+
42
+ if options[:gateway_service] && options[:gateway_service][:url]
43
+ parsed_url = URI.parse(options[:gateway_service][:url].start_with?(Constants::HTTP_PROTOCOL) || options[:gateway_service][:url].start_with?(Constants::HTTPS_PROTOCOL) ? options[:gateway_service][:url] : "#{Constants::HTTPS_PROTOCOL}#{options[:gateway_service][:url]}")
44
+ @hostname = parsed_url.hostname
45
+ @protocol = parsed_url.scheme
46
+ @port = parsed_url.port || options.dig(:gateway_service, :port)
47
+ @is_gateway_service_provided = true
48
+ else
49
+ @hostname = Constants::HOST_NAME
50
+ @protocol = Constants::HTTPS_PROTOCOL
51
+ end
52
+
53
+ LoggerService.log(LogLevelEnum::DEBUG, "SERVICE_INITIALIZED", { service: 'Settings Manager' })
54
+ SettingsService.instance = self
55
+ end
56
+
57
+ # Fetch settings and cache them in storage.
58
+ # @return [SettingsModel] The fetched settings
59
+ def fetch_settings_and_cache_in_storage
60
+ begin
61
+ response = fetch_settings
62
+ response
63
+ rescue => e
64
+ LoggerService.log(LogLevelEnum::ERROR, "SETTINGS_FETCH_ERROR", { err: e.message })
65
+ nil
66
+ end
67
+ end
68
+
69
+ # Fetch settings from the server.
70
+ # @param is_via_webhook [Boolean] Whether to fetch settings via webhook
71
+ # @return [SettingsModel] The fetched settings
72
+ def fetch_settings(is_via_webhook = false)
73
+ if @sdk_key.nil? || @account_id.nil?
74
+ LoggerService.log(LogLevelEnum::ERROR, "sdkKey is required for fetching account settings. Aborting!", nil)
75
+ end
76
+
77
+ network_instance = NetworkManager.instance
78
+ options = NetworkUtil.get_settings_path(@sdk_key, @account_id)
79
+
80
+ options['api-version'] = Constants::API_VERSION
81
+ options[:source] = 'prod'
82
+
83
+ # When using gateway service, always fetch from SETTINGS_ENDPOINT since the gateway maintains the latest settings
84
+ if @is_gateway_service_provided
85
+ path = Constants::SETTINGS_ENDPOINT
86
+ else
87
+ path = is_via_webhook ? Constants::WEBHOOK_SETTINGS_ENDPOINT : Constants::SETTINGS_ENDPOINT
88
+ end
89
+
90
+ request = RequestModel.new(@hostname, "GET", path, options, nil, nil, @protocol, @port)
91
+ request.set_timeout(@network_timeout)
92
+
93
+ begin
94
+ response = network_instance.get(request)
95
+ response.get_data
96
+ rescue => e
97
+ LoggerService.log(LogLevelEnum::ERROR, "Error fetching settings: #{e.message}", nil)
98
+ raise e
99
+ end
100
+ end
101
+
102
+ # Get settings (either from storage or by forcing fetch).
103
+ # @param force_fetch [Boolean] Whether to force fetch the settings
104
+ # @return [SettingsModel] The fetched settings
105
+ def get_settings(force_fetch = false)
106
+ if force_fetch
107
+ fetch_settings_and_cache_in_storage
108
+ else
109
+ settings = fetch_settings_and_cache_in_storage
110
+ is_valid = SettingsSchema.new.is_settings_valid(settings)
111
+ if is_valid
112
+ LoggerService.log(LogLevelEnum::INFO, "SETTINGS_FETCH_SUCCESS")
113
+ settings
114
+ else
115
+ LoggerService.log(LogLevelEnum::ERROR, "SETTINGS_SCHEMA_INVALID")
116
+ {}
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../enums/storage_enum'
16
+ require_relative '../models/user/context_model'
17
+ require_relative '../packages/storage/storage'
18
+ require_relative '../services/logger_service'
19
+ require_relative '../enums/log_level_enum'
20
+ require_relative '../utils/data_type_util'
21
+
22
+ class StorageService
23
+ attr_accessor :storage_data
24
+
25
+ def initialize
26
+ @storage_data = {}
27
+ end
28
+
29
+ # Retrieves data from storage based on the feature key and user ID.
30
+ # @param feature_key [String] The key to identify the feature data.
31
+ # @param context [ContextModel] The user context containing an ID.
32
+ # @return [Hash] The data retrieved or a storage status enum.
33
+ def get_data_from_storage(feature_key, context)
34
+ storage_instance = Storage.instance.get_connector
35
+
36
+ # Check if the storage instance is available
37
+ return StorageEnum::STORAGE_UNDEFINED if DataTypeUtil.is_null(storage_instance) || DataTypeUtil.is_undefined(storage_instance)
38
+
39
+ begin
40
+ data = storage_instance.get(feature_key, context.get_id)
41
+ return data.nil? ? StorageEnum::NO_DATA_FOUND : data
42
+ rescue StandardError => e
43
+ LoggerService.log(LogLevelEnum::ERROR, "STORED_DATA_ERROR", { err: e.message })
44
+ return StorageEnum::NO_DATA_FOUND
45
+ end
46
+ end
47
+
48
+ # Stores data in the storage.
49
+ # @param data [Hash] The data to be stored.
50
+ # @return [Boolean] True if data is successfully stored, otherwise false.
51
+ def set_data_in_storage(data)
52
+ storage_instance = Storage.instance.get_connector
53
+
54
+ # Check if the storage instance is available
55
+ return false if storage_instance.nil?
56
+
57
+ begin
58
+ storage_instance.set(data)
59
+ return true
60
+ rescue StandardError
61
+ LoggerService.log(LogLevelEnum::ERROR, "STORED_DATA_ERROR", { err: e.message })
62
+ return false
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,249 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../constants/constants'
16
+ require_relative '../enums/campaign_type_enum'
17
+ require_relative '../models/campaign/campaign_model'
18
+ require_relative '../models/campaign/feature_model'
19
+ require_relative '../models/campaign/variation_model'
20
+ require_relative '../models/settings/settings_model'
21
+ require_relative '../services/logger_service'
22
+ require_relative '../enums/log_level_enum'
23
+ require_relative '../models/campaign/rule_model'
24
+
25
+ module CampaignUtil
26
+ # Sets the variation allocation for a campaign
27
+ # @param campaign [CampaignModel] The campaign to set the variation allocation for
28
+ def self.set_variation_allocation(campaign)
29
+ if [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
30
+ handle_rollout_campaign(campaign)
31
+ else
32
+ current_allocation = 0
33
+ campaign.get_variations.each do |variation|
34
+ step_factor = assign_range_values(variation, current_allocation)
35
+ current_allocation += step_factor
36
+
37
+ LoggerService.log(LogLevelEnum::INFO, "VARIATION_RANGE_ALLOCATION", {
38
+ variationKey: variation.get_key,
39
+ campaignKey: campaign.get_key,
40
+ variationWeight: variation.get_weight,
41
+ startRange: variation.get_start_range_variation,
42
+ endRange: variation.get_end_range_variation
43
+ })
44
+ end
45
+ end
46
+ end
47
+
48
+ # Assigns start and end range values to a variation
49
+ # @param variation [VariationModel] The variation to assign the start and end range values to
50
+ # @param current_allocation [Integer] The current allocation
51
+ # @return [Integer] The step factor
52
+ def self.assign_range_values(variation, current_allocation)
53
+ step_factor = get_variation_bucket_range(variation.get_weight)
54
+
55
+ if step_factor > 0
56
+ variation.set_start_range(current_allocation + 1)
57
+ variation.set_end_range(current_allocation + step_factor)
58
+ else
59
+ variation.set_start_range(-1)
60
+ variation.set_end_range(-1)
61
+ end
62
+
63
+ step_factor
64
+ end
65
+
66
+ # Scales variation weights to sum up to 100%
67
+ # @param variations [Array<VariationModel>] The variations to scale the weights of
68
+ # @return [Array<VariationModel>] The scaled variations
69
+ def self.scale_variation_weights(variations)
70
+ total_weight = variations.sum(&:weight)
71
+
72
+ if total_weight.zero?
73
+ equal_weight = 100.0 / variations.length
74
+ variations.each { |variation| variation.weight = equal_weight }
75
+ else
76
+ variations.each { |variation| variation.weight = (variation.weight / total_weight) * 100 }
77
+ end
78
+ end
79
+
80
+ # Generates a bucketing seed based on user ID and campaign
81
+ # @param user_id [String] The ID of the user
82
+ # @param campaign [CampaignModel] The campaign to generate the bucketing seed for
83
+ # @param group_id [String] The ID of the group
84
+ # @return [String] The bucketing seed
85
+ def self.get_bucketing_seed(user_id, campaign, group_id = nil)
86
+ return "#{group_id}_#{user_id}" if group_id
87
+
88
+ is_rollout_or_personalize = [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
89
+ salt = is_rollout_or_personalize ? campaign.get_variations.first.get_salt : campaign.get_salt
90
+
91
+ salt ? "#{salt}_#{user_id}" : "#{campaign.get_id}_#{user_id}"
92
+ end
93
+
94
+ # Retrieves variation from campaign key
95
+ # @param settings [SettingsModel] The settings for the VWO instance
96
+ # @param campaign_key [String] The key of the campaign
97
+ # @param variation_id [Integer] The ID of the variation
98
+ # @return [VariationModel] The variation
99
+ def self.get_variation_from_campaign_key(settings, campaign_key, variation_id)
100
+ campaign = settings.get_campaigns.find { |c| c.get_key == campaign_key }
101
+ return nil unless campaign
102
+
103
+ variation = campaign.get_variations.find { |v| v.get_id == variation_id }
104
+ variation ? VariationModel.new.model_from_dictionary(variation) : nil
105
+ end
106
+
107
+ # Sets campaign allocation ranges
108
+ # @param campaigns [Array<CampaignModel>] The campaigns to set the allocation ranges for
109
+ def self.set_campaign_allocation(campaigns)
110
+ current_allocation = 0
111
+
112
+ campaigns.each do |campaign|
113
+ step_factor = assign_range_values_meg(campaign, current_allocation)
114
+ current_allocation += step_factor
115
+ end
116
+ end
117
+
118
+ # Retrieves campaign group details if part of a group
119
+ # @param settings [SettingsModel] The settings for the VWO instance
120
+ # @param campaign_id [Integer] The ID of the campaign
121
+ # @param variation_id [Integer] The ID of the variation
122
+ # @return [Hash] The group details
123
+ def self.get_group_details_if_campaign_part_of_it(settings, campaign_id, variation_id = nil)
124
+ campaign_to_check = variation_id ? "#{campaign_id}_#{variation_id}" : campaign_id.to_s
125
+
126
+ if settings.get_campaign_groups.key?(campaign_to_check)
127
+ group_id = settings.get_campaign_groups[campaign_to_check]
128
+ { group_id: group_id, group_name: settings.get_groups[group_id.to_s][:name.to_s]}
129
+ else
130
+ {}
131
+ end
132
+ end
133
+
134
+ # Finds groups associated with a feature
135
+ # @param settings [SettingsModel] The settings for the VWO instance
136
+ # @param feature_key [String] The key of the feature
137
+ # @return [Array] The groups associated with the feature
138
+ def self.find_groups_feature_part_of(settings, feature_key)
139
+ rule_array = []
140
+ settings.get_features.each do |feature|
141
+ if feature.get_key == feature_key
142
+ rule_array.concat(feature.get_rules)
143
+ end
144
+ end
145
+
146
+ groups = []
147
+ rule_array.each do |rule|
148
+ group = get_group_details_if_campaign_part_of_it(settings, rule.get_campaign_id, rule.get_type == CampaignTypeEnum::PERSONALIZE ? rule.get_variation_id : nil)
149
+ groups << group unless group.empty? || groups.any? { |g| g[:group_id] == group[:group_id] }
150
+ end
151
+
152
+ groups
153
+ end
154
+
155
+ # Retrieves campaigns by group ID
156
+ # @param settings [SettingsModel] The settings for the VWO instance
157
+ # @param group_id [String] The ID of the group
158
+ # @return [Array] The campaigns associated with the group
159
+ def self.get_campaigns_by_group_id(settings, group_id)
160
+ settings.get_groups[group_id.to_s]&.fetch(:campaigns.to_s, []) || []
161
+ end
162
+
163
+ # Retrieves feature keys from campaign IDs
164
+ # @param settings [SettingsModel] The settings for the VWO instance
165
+ # @param campaign_ids [Array] The IDs of the campaigns
166
+ # @return [Array] The feature keys associated with the campaigns
167
+ def self.get_feature_keys_from_campaign_ids(settings, campaign_ids)
168
+ feature_keys = []
169
+
170
+ campaign_ids.each do |campaign|
171
+ campaign_id, variation_id = campaign.split('_').map(&:to_i)
172
+
173
+ settings.get_features.each do |feature|
174
+ next if feature_keys.include?(feature.get_key)
175
+
176
+ feature.get_rules.each do |rule|
177
+ if rule.get_campaign_id == campaign_id
178
+ feature_keys << feature.get_key if variation_id.nil? || rule.get_variation_id == variation_id
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ feature_keys
185
+ end
186
+
187
+ # Retrieves campaign IDs from a feature key
188
+ # @param settings [SettingsModel] The settings for the VWO instance
189
+ # @param feature_key [String] The key of the feature
190
+ # @return [Array] The campaign IDs associated with the feature
191
+ def self.get_campaign_ids_from_feature_key(settings, feature_key)
192
+ settings.get_features.each do |feature|
193
+ return feature.get_rules.map(&:get_campaign_id) if feature.get_key == feature_key
194
+ end
195
+ []
196
+ end
197
+
198
+ # Assigns range values to a MEG campaign
199
+ # @param data [VariationModel] The variation to assign the start and end range values to
200
+ # @param current_allocation [Integer] The current allocation
201
+ # @return [Integer] The step factor
202
+ def self.assign_range_values_meg(data, current_allocation)
203
+ step_factor = get_variation_bucket_range(data.weight)
204
+
205
+ if step_factor > 0
206
+ data.start_range_variation = current_allocation + 1
207
+ data.end_range_variation = current_allocation + step_factor
208
+ else
209
+ data.start_range_variation = -1
210
+ data.end_range_variation = -1
211
+ end
212
+
213
+ step_factor
214
+ end
215
+
216
+ # Retrieves the rule type for a given campaign ID from a feature
217
+ def self.get_rule_type_using_campaign_id_from_feature(feature, campaign_id)
218
+ rule = feature.get_rules.find { |r| r.get_campaign_id == campaign_id }
219
+ rule ? rule.get_type : ''
220
+ end
221
+
222
+ # Calculates bucket range for a variation
223
+ # @param variation_weight [Float] The weight of the variation
224
+ # @return [Integer] The bucket range
225
+ def self.get_variation_bucket_range(variation_weight)
226
+ return 0 unless variation_weight && variation_weight.positive?
227
+
228
+ start_range = (variation_weight * 100).ceil
229
+ [start_range, Constants::MAX_TRAFFIC_VALUE].min
230
+ end
231
+
232
+ # Handles rollout campaign logic
233
+ # @param campaign [CampaignModel] The campaign to handle the rollout logic for
234
+ def self.handle_rollout_campaign(campaign)
235
+ campaign.get_variations.each do |variation|
236
+ end_range = variation.get_weight * 100
237
+ variation.set_start_range(1)
238
+ variation.set_end_range(end_range)
239
+
240
+ LoggerService.log(LogLevelEnum::INFO, "VARIATION_RANGE_ALLOCATION", {
241
+ variationKey: variation.get_key,
242
+ campaignKey: campaign.get_key,
243
+ variationWeight: variation.get_weight,
244
+ startRange: 1,
245
+ endRange: end_range
246
+ })
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,105 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module DataTypeUtil
16
+ # Checks if the value is a Hash (object in JS)
17
+ def self.is_object(val)
18
+ val.is_a?(Hash)
19
+ end
20
+
21
+ # Checks if the value is an Array
22
+ def self.is_array(val)
23
+ val.is_a?(Array)
24
+ end
25
+
26
+ # Checks if the value is nil
27
+ def self.is_null(val)
28
+ val.nil?
29
+ end
30
+
31
+ # Checks if the value is undefined (not applicable in Ruby, so return false)
32
+ def self.is_undefined(val)
33
+ false
34
+ end
35
+
36
+ # Checks if the value is defined (not nil)
37
+ def self.is_defined(val)
38
+ !val.nil?
39
+ end
40
+
41
+ # Checks if the value is a Number (including NaN)
42
+ def self.is_number(val)
43
+ val.is_a?(Numeric)
44
+ end
45
+
46
+ # Checks if the value is a String
47
+ def self.is_string(val)
48
+ val.is_a?(String)
49
+ end
50
+
51
+ # Checks if the value is a Boolean
52
+ def self.is_boolean(val)
53
+ val.is_a?(TrueClass) || val.is_a?(FalseClass)
54
+ end
55
+
56
+ # Checks if the value is NaN (only applicable for Float in Ruby)
57
+ def self.is_nan(val)
58
+ val.is_a?(Float) && val.nan?
59
+ end
60
+
61
+ # Checks if the value is a Date
62
+ def self.is_date(val)
63
+ val.is_a?(Date) || val.is_a?(Time) || val.is_a?(DateTime)
64
+ end
65
+
66
+ # Checks if the value is a Function (Proc or Lambda in Ruby)
67
+ def self.is_function(val)
68
+ val.is_a?(Proc) || val.is_a?(Method)
69
+ end
70
+
71
+ # Checks if the value is a Regular Expression
72
+ def self.is_regex(val)
73
+ val.is_a?(Regexp)
74
+ end
75
+
76
+ # Determines the type of the given value
77
+ def self.get_type(val)
78
+ case
79
+ when is_object(val)
80
+ "Object"
81
+ when is_array(val)
82
+ "Array"
83
+ when is_null(val)
84
+ "Null"
85
+ when is_undefined(val)
86
+ "Undefined"
87
+ when is_nan(val)
88
+ "NaN"
89
+ when is_number(val)
90
+ "Number"
91
+ when is_string(val)
92
+ "String"
93
+ when is_boolean(val)
94
+ "Boolean"
95
+ when is_date(val)
96
+ "Date"
97
+ when is_regex(val)
98
+ "Regex"
99
+ when is_function(val)
100
+ "Function"
101
+ else
102
+ "Unknown Type"
103
+ end
104
+ end
105
+ end