vwo-fme-ruby-sdk 1.4.1 → 1.6.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.
- checksums.yaml +4 -4
- data/lib/vwo/constants/constants.rb +1 -1
- data/lib/vwo/enums/event_enum.rb +3 -2
- data/lib/vwo/models/schemas/settings_schema_validation.rb +1 -0
- data/lib/vwo/models/settings/settings_model.rb +6 -1
- data/lib/vwo/models/user/context_model.rb +12 -2
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +4 -4
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +3 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +6 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +265 -49
- data/lib/vwo/utils/event_util.rb +13 -0
- data/lib/vwo/utils/gateway_service_util.rb +1 -1
- data/lib/vwo/utils/impression_util.rb +1 -3
- data/lib/vwo/utils/network_util.rb +98 -47
- data/lib/vwo/utils/url_util.rb +7 -0
- data/lib/vwo/utils/usage_stats_util.rb +3 -1
- data/lib/vwo.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aba596eb41128d18fecdbac11316dfe345d0fdcd2b8c1a0a612b1145bde5b779
|
|
4
|
+
data.tar.gz: 0ca14ec46f853f5cd86171ee4a0a7c93fb57f009872f847e2777fc3375e01cd6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 398d9be0a02c8b364712eb1b1ef38061138ecfcd71a8ec4f8ec5a6ad5d7ef1b0a6ee678f8ed1cb057bc6db337baf885cd3852c0ad935d480d5cd705ab7e2768c
|
|
7
|
+
data.tar.gz: ae7f284247ccc40c0c881dcd7ab23665989b71f3252931e755417a428cc606cf5e7af091cac9a28f20d274c5a782afab095633635ced57a23879b965579d56a6
|
data/lib/vwo/enums/event_enum.rb
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# Event types
|
|
16
16
|
module EventEnum
|
|
17
17
|
VWO_VARIATION_SHOWN = 'vwo_variationShown'
|
|
18
|
-
VWO_SYNC_VISITOR_PROP = 'vwo_syncVisitorProp'
|
|
18
|
+
VWO_SYNC_VISITOR_PROP = 'vwo_syncVisitorProp'
|
|
19
19
|
VWO_INIT_CALLED = 'vwo_fmeSdkInit'
|
|
20
|
-
|
|
20
|
+
VWO_USAGE_STATS = 'vwo_sdkUsageStats'
|
|
21
|
+
end
|
|
@@ -84,6 +84,7 @@ class SettingsSchema
|
|
|
84
84
|
optional(:sdkKey).maybe(:string)
|
|
85
85
|
required(:version).filled(:integer)
|
|
86
86
|
required(:accountId).filled(:integer)
|
|
87
|
+
optional(:usageStatsAccountId).maybe(:integer)
|
|
87
88
|
optional(:features).array(:hash)
|
|
88
89
|
required(:campaigns).array(:hash)
|
|
89
90
|
optional(:groups).maybe(:hash)
|
|
@@ -17,12 +17,13 @@ require_relative '../campaign/feature_model'
|
|
|
17
17
|
require_relative '../../constants/constants'
|
|
18
18
|
|
|
19
19
|
class SettingsModel
|
|
20
|
-
attr_reader :sdk_key, :account_id, :version, :collection_prefix,
|
|
20
|
+
attr_reader :sdk_key, :account_id, :usage_stats_account_id, :version, :collection_prefix,
|
|
21
21
|
:features, :campaigns, :campaign_groups, :groups, :poll_interval
|
|
22
22
|
|
|
23
23
|
def initialize(settings)
|
|
24
24
|
@sdk_key = settings["sdkKey"]
|
|
25
25
|
@account_id = settings["accountId"]
|
|
26
|
+
@usage_stats_account_id = settings["usageStatsAccountId"]
|
|
26
27
|
@version = settings["version"]
|
|
27
28
|
@collection_prefix = settings["collectionPrefix"]
|
|
28
29
|
@poll_interval = settings["pollInterval"] || Constants::POLLING_INTERVAL
|
|
@@ -59,6 +60,10 @@ class SettingsModel
|
|
|
59
60
|
@account_id
|
|
60
61
|
end
|
|
61
62
|
|
|
63
|
+
def get_usage_stats_account_id
|
|
64
|
+
@usage_stats_account_id
|
|
65
|
+
end
|
|
66
|
+
|
|
62
67
|
def get_version
|
|
63
68
|
@version
|
|
64
69
|
end
|
|
@@ -15,14 +15,15 @@
|
|
|
15
15
|
require_relative './context_vwo_model'
|
|
16
16
|
|
|
17
17
|
class ContextModel
|
|
18
|
-
attr_accessor :id, :user_agent, :ip_address, :custom_variables, :variation_targeting_variables, :vwo
|
|
18
|
+
attr_accessor :id, :user_agent, :ip_address, :custom_variables, :variation_targeting_variables, :post_segmentation_variables, :vwo
|
|
19
19
|
|
|
20
|
-
def initialize(id = nil, user_agent = nil, ip_address = nil, custom_variables = {}, variation_targeting_variables = {}, vwo = nil)
|
|
20
|
+
def initialize(id = nil, user_agent = nil, ip_address = nil, custom_variables = {}, variation_targeting_variables = {}, post_segmentation_variables = {}, vwo = nil)
|
|
21
21
|
@id = id
|
|
22
22
|
@user_agent = user_agent
|
|
23
23
|
@ip_address = ip_address
|
|
24
24
|
@custom_variables = custom_variables || {}
|
|
25
25
|
@variation_targeting_variables = variation_targeting_variables || {}
|
|
26
|
+
@post_segmentation_variables = post_segmentation_variables || {}
|
|
26
27
|
@vwo = vwo
|
|
27
28
|
end
|
|
28
29
|
|
|
@@ -45,6 +46,7 @@ class ContextModel
|
|
|
45
46
|
@ip_address = context[:ipAddress]
|
|
46
47
|
@custom_variables = context[:customVariables] if context.key?(:customVariables)
|
|
47
48
|
@variation_targeting_variables = context[:variationTargetingVariables] if context.key?(:variationTargetingVariables)
|
|
49
|
+
@post_segmentation_variables = context[:postSegmentationVariables] if context.key?(:postSegmentationVariables)
|
|
48
50
|
@vwo = ContextVWOModel.new.model_from_dictionary(context[:_vwo]) if context.key?(:_vwo)
|
|
49
51
|
|
|
50
52
|
self
|
|
@@ -90,6 +92,14 @@ class ContextModel
|
|
|
90
92
|
@variation_targeting_variables = variation_targeting_variables
|
|
91
93
|
end
|
|
92
94
|
|
|
95
|
+
def get_post_segmentation_variables
|
|
96
|
+
@post_segmentation_variables
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def set_post_segmentation_variables(post_segmentation_variables)
|
|
100
|
+
@post_segmentation_variables = post_segmentation_variables
|
|
101
|
+
end
|
|
102
|
+
|
|
93
103
|
def get_vwo
|
|
94
104
|
@vwo
|
|
95
105
|
end
|
|
@@ -21,9 +21,9 @@ module SegmentOperandRegexEnum
|
|
|
21
21
|
REGEX_MATCH = '^regex\\((.*)\\)'
|
|
22
22
|
STARTING_STAR = '^\\*'
|
|
23
23
|
ENDING_STAR = '\\*$'
|
|
24
|
-
GREATER_THAN_MATCH = '^gt\\((\\d
|
|
25
|
-
GREATER_THAN_EQUAL_TO_MATCH = '^gte\\((\\d
|
|
26
|
-
LESS_THAN_MATCH = '^lt\\((\\d
|
|
27
|
-
LESS_THAN_EQUAL_TO_MATCH = '^lte\\((\\d
|
|
24
|
+
GREATER_THAN_MATCH = '^gt\\((\\d+(?:\\.\\d+)*|\\.\\d+)\\)'
|
|
25
|
+
GREATER_THAN_EQUAL_TO_MATCH = '^gte\\((\\d+(?:\\.\\d+)*|\\.\\d+)\\)'
|
|
26
|
+
LESS_THAN_MATCH = '^lt\\((\\d+(?:\\.\\d+)*|\\.\\d+)\\)'
|
|
27
|
+
LESS_THAN_EQUAL_TO_MATCH = '^lte\\((\\d+(?:\\.\\d+)*|\\.\\d+)\\)'
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -57,6 +57,12 @@ class SegmentEvaluator
|
|
|
57
57
|
SegmentOperandEvaluator.new.evaluate_user_dsl(sub_dsl, properties)
|
|
58
58
|
when SegmentOperatorValueEnum::UA
|
|
59
59
|
SegmentOperandEvaluator.new.evaluate_user_agent_dsl(sub_dsl, @context)
|
|
60
|
+
when SegmentOperatorValueEnum::IP
|
|
61
|
+
SegmentOperandEvaluator.new.evaluate_string_operand_dsl(sub_dsl, @context, SegmentOperatorValueEnum::IP)
|
|
62
|
+
when SegmentOperatorValueEnum::BROWSER_VERSION
|
|
63
|
+
SegmentOperandEvaluator.new.evaluate_string_operand_dsl(sub_dsl, @context, SegmentOperatorValueEnum::BROWSER_VERSION)
|
|
64
|
+
when SegmentOperatorValueEnum::OS_VERSION
|
|
65
|
+
SegmentOperandEvaluator.new.evaluate_string_operand_dsl(sub_dsl, @context, SegmentOperatorValueEnum::OS_VERSION)
|
|
60
66
|
else
|
|
61
67
|
false
|
|
62
68
|
end
|
|
@@ -15,15 +15,22 @@
|
|
|
15
15
|
require_relative '../utils/segment_util'
|
|
16
16
|
require_relative '../enums/segment_operand_value_enum'
|
|
17
17
|
require_relative '../enums/segment_operand_regex_enum'
|
|
18
|
+
require_relative '../enums/segment_operator_value_enum'
|
|
18
19
|
require_relative '../../../utils/data_type_util'
|
|
19
20
|
require_relative '../../../utils/gateway_service_util'
|
|
20
21
|
require_relative '../../../enums/url_enum'
|
|
21
22
|
require_relative '../../../services/logger_service'
|
|
22
23
|
require_relative '../../../models/user/context_model'
|
|
23
24
|
require_relative '../../../enums/log_level_enum'
|
|
25
|
+
|
|
26
|
+
# SegmentOperandEvaluator class provides methods to evaluate different types of DSL (Domain Specific Language)
|
|
27
|
+
# expressions based on the segment conditions defined for custom variables, user IDs, and user agents.
|
|
24
28
|
class SegmentOperandEvaluator
|
|
29
|
+
# Regex pattern to check if a string contains non-numeric characters (except decimal point)
|
|
30
|
+
NON_NUMERIC_PATTERN = /[^0-9.]/
|
|
31
|
+
|
|
25
32
|
# Evaluates the custom variable DSL
|
|
26
|
-
# @param dsl_operand_value [
|
|
33
|
+
# @param dsl_operand_value [Hash] The operand value to evaluate
|
|
27
34
|
# @param properties [Hash] The properties to evaluate the operand against
|
|
28
35
|
# @return [Boolean] True if the operand value matches the tag value, false otherwise
|
|
29
36
|
def evaluate_custom_variable_dsl(dsl_operand_value, properties)
|
|
@@ -42,10 +49,11 @@ class SegmentOperandEvaluator
|
|
|
42
49
|
return false
|
|
43
50
|
end
|
|
44
51
|
|
|
45
|
-
tag_value =
|
|
52
|
+
tag_value = properties[operand_key]
|
|
53
|
+
attribute_value = pre_process_tag_value(tag_value)
|
|
46
54
|
list_id = match[1]
|
|
47
55
|
|
|
48
|
-
query_params_obj = { attribute:
|
|
56
|
+
query_params_obj = { attribute: attribute_value, listId: list_id }
|
|
49
57
|
|
|
50
58
|
begin
|
|
51
59
|
res = get_from_gateway_service(query_params_obj, UrlEnum::ATTRIBUTE_CHECK)
|
|
@@ -60,9 +68,12 @@ class SegmentOperandEvaluator
|
|
|
60
68
|
|
|
61
69
|
false
|
|
62
70
|
else
|
|
63
|
-
tag_value =
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
tag_value = properties[operand_key]
|
|
72
|
+
tag_value = pre_process_tag_value(tag_value)
|
|
73
|
+
processed_operand = pre_process_operand_value(operand)
|
|
74
|
+
processed_values = process_values(processed_operand[:operand_value], tag_value)
|
|
75
|
+
tag_value = processed_values[:tag_value]
|
|
76
|
+
extract_result(processed_operand[:operand_type], processed_values[:operand_value], tag_value)
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
|
|
@@ -80,21 +91,144 @@ class SegmentOperandEvaluator
|
|
|
80
91
|
# @param context [ContextModel] The context to evaluate the operand against
|
|
81
92
|
# @return [Boolean] True if the operand value matches the tag value, false otherwise
|
|
82
93
|
def evaluate_user_agent_dsl(dsl_operand_value, context)
|
|
83
|
-
|
|
94
|
+
operand = dsl_operand_value
|
|
95
|
+
unless context.get_user_agent
|
|
96
|
+
LoggerService.log(LogLevelEnum::INFO, 'To Evaluate UserAgent segmentation, please provide userAgent in context', nil)
|
|
97
|
+
return false
|
|
98
|
+
end
|
|
84
99
|
|
|
85
100
|
tag_value = CGI.unescape(context.get_user_agent)
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
processed_operand = pre_process_operand_value(operand)
|
|
102
|
+
processed_values = process_values(processed_operand[:operand_value], tag_value)
|
|
103
|
+
tag_value = processed_values[:tag_value]
|
|
104
|
+
extract_result(processed_operand[:operand_type], processed_values[:operand_value], tag_value)
|
|
88
105
|
end
|
|
89
106
|
|
|
90
|
-
#
|
|
91
|
-
# @param
|
|
92
|
-
# @
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
# Evaluates a given string tag value against a DSL operand value.
|
|
108
|
+
# @param dsl_operand_value [String] The DSL operand string (e.g., "contains(\"value\")").
|
|
109
|
+
# @param context [ContextModel] The context object containing the value to evaluate.
|
|
110
|
+
# @param operand_type [String] The type of operand being evaluated (ip_address, browser_version, os_version).
|
|
111
|
+
# @return [Boolean] True if tag value matches DSL operand criteria, false otherwise.
|
|
112
|
+
def evaluate_string_operand_dsl(dsl_operand_value, context, operand_type)
|
|
113
|
+
operand = dsl_operand_value.to_s
|
|
114
|
+
|
|
115
|
+
# Determine the tag value based on operand type
|
|
116
|
+
tag_value = get_tag_value_for_operand_type(context, operand_type)
|
|
117
|
+
|
|
118
|
+
if tag_value.nil?
|
|
119
|
+
log_missing_context_error(operand_type)
|
|
120
|
+
return false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
operand_type_and_value = pre_process_operand_value(operand)
|
|
124
|
+
processed_values = process_values(operand_type_and_value[:operand_value], tag_value, operand_type)
|
|
125
|
+
processed_tag_value = processed_values[:tag_value]
|
|
126
|
+
|
|
127
|
+
extract_result(
|
|
128
|
+
operand_type_and_value[:operand_type],
|
|
129
|
+
processed_values[:operand_value].to_s.strip.gsub(/"/, ''),
|
|
130
|
+
processed_tag_value
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Evaluates IP address DSL expression.
|
|
135
|
+
# @param dsl_operand_value [String] The DSL expression for the IP address.
|
|
136
|
+
# @param context [ContextModel] The context object containing the IP address.
|
|
137
|
+
# @return [Boolean] True if the IP address matches the DSL condition, otherwise false.
|
|
138
|
+
def evaluate_ip_dsl(dsl_operand_value, context)
|
|
139
|
+
evaluate_string_operand_dsl(dsl_operand_value, context, SegmentOperatorValueEnum::IP)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Evaluates browser version DSL expression.
|
|
143
|
+
# @param dsl_operand_value [String] The DSL expression for the browser version.
|
|
144
|
+
# @param context [ContextModel] The context object containing the user agent info.
|
|
145
|
+
# @return [Boolean] True if the browser version matches the DSL condition, otherwise false.
|
|
146
|
+
def evaluate_browser_version_dsl(dsl_operand_value, context)
|
|
147
|
+
evaluate_string_operand_dsl(dsl_operand_value, context, SegmentOperatorValueEnum::BROWSER_VERSION)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Evaluates OS version DSL expression.
|
|
151
|
+
# @param dsl_operand_value [String] The DSL expression for the OS version.
|
|
152
|
+
# @param context [ContextModel] The context object containing the user agent info.
|
|
153
|
+
# @return [Boolean] True if the OS version matches the DSL condition, otherwise false.
|
|
154
|
+
def evaluate_os_version_dsl(dsl_operand_value, context)
|
|
155
|
+
evaluate_string_operand_dsl(dsl_operand_value, context, SegmentOperatorValueEnum::OS_VERSION)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Gets the appropriate tag value based on the operand type.
|
|
159
|
+
# @param context [ContextModel] The context object.
|
|
160
|
+
# @param operand_type [String] The type of operand.
|
|
161
|
+
# @return [String, nil] The tag value or nil if not available.
|
|
162
|
+
def get_tag_value_for_operand_type(context, operand_type)
|
|
163
|
+
case operand_type
|
|
164
|
+
when SegmentOperatorValueEnum::IP
|
|
165
|
+
context.get_ip_address
|
|
166
|
+
when SegmentOperatorValueEnum::BROWSER_VERSION
|
|
167
|
+
get_browser_version_from_context(context)
|
|
168
|
+
else
|
|
169
|
+
# Default works for OS version
|
|
170
|
+
get_os_version_from_context(context)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Gets browser version from context.
|
|
175
|
+
# @param context [ContextModel] The context object.
|
|
176
|
+
# @return [String, nil] The browser version or nil if not available.
|
|
177
|
+
def get_browser_version_from_context(context)
|
|
178
|
+
user_agent = context.get_vwo&.get_ua_info
|
|
179
|
+
return nil unless user_agent && user_agent.is_a?(Hash) && !user_agent.empty?
|
|
180
|
+
|
|
181
|
+
# Assuming UserAgent dictionary contains browser_version
|
|
182
|
+
if user_agent.key?('browser_version')
|
|
183
|
+
return user_agent['browser_version']&.to_s
|
|
184
|
+
end
|
|
185
|
+
nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Gets OS version from context.
|
|
189
|
+
# @param context [ContextModel] The context object.
|
|
190
|
+
# @return [String, nil] The OS version or nil if not available.
|
|
191
|
+
def get_os_version_from_context(context)
|
|
192
|
+
user_agent = context.get_vwo&.get_ua_info
|
|
193
|
+
return nil unless user_agent && user_agent.is_a?(Hash) && !user_agent.empty?
|
|
194
|
+
|
|
195
|
+
# Assuming UserAgent dictionary contains os_version
|
|
196
|
+
if user_agent.key?('os_version')
|
|
197
|
+
return user_agent['os_version']&.to_s
|
|
198
|
+
end
|
|
199
|
+
nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Logs appropriate error message for missing context.
|
|
203
|
+
# @param operand_type [String] The type of operand.
|
|
204
|
+
def log_missing_context_error(operand_type)
|
|
205
|
+
case operand_type
|
|
206
|
+
when SegmentOperatorValueEnum::IP
|
|
207
|
+
LoggerService.log(LogLevelEnum::INFO, 'To evaluate IP segmentation, please provide ipAddress in context', nil)
|
|
208
|
+
when SegmentOperatorValueEnum::BROWSER_VERSION
|
|
209
|
+
LoggerService.log(LogLevelEnum::INFO, 'To evaluate browser version segmentation, please provide userAgent in context', nil)
|
|
210
|
+
else
|
|
211
|
+
LoggerService.log(LogLevelEnum::INFO, 'To evaluate OS version segmentation, please provide userAgent in context', nil)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
96
214
|
|
|
97
|
-
|
|
215
|
+
# Pre-processes the tag value to ensure it is in the correct format for evaluation.
|
|
216
|
+
# @param tag_value [Any] The value to be processed.
|
|
217
|
+
# @return [String, Boolean] The processed tag value, either as a string or a boolean.
|
|
218
|
+
def pre_process_tag_value(tag_value)
|
|
219
|
+
# Default to empty string if undefined
|
|
220
|
+
if tag_value.nil?
|
|
221
|
+
tag_value = ''
|
|
222
|
+
end
|
|
223
|
+
# Convert boolean values to boolean type
|
|
224
|
+
if DataTypeUtil.is_boolean(tag_value)
|
|
225
|
+
tag_value = tag_value ? true : false
|
|
226
|
+
end
|
|
227
|
+
# Convert all non-null values to string
|
|
228
|
+
unless tag_value.nil?
|
|
229
|
+
tag_value = tag_value.to_s
|
|
230
|
+
end
|
|
231
|
+
tag_value
|
|
98
232
|
end
|
|
99
233
|
|
|
100
234
|
# Pre-processes the operand value
|
|
@@ -106,16 +240,23 @@ class SegmentOperandEvaluator
|
|
|
106
240
|
{ operand_type: SegmentOperandValueEnum::LOWER_VALUE, operand_value: extract_operand_value(operand, SegmentOperandRegexEnum::LOWER_MATCH) }
|
|
107
241
|
when /#{SegmentOperandRegexEnum::WILDCARD_MATCH}/
|
|
108
242
|
value = extract_operand_value(operand, SegmentOperandRegexEnum::WILDCARD_MATCH)
|
|
109
|
-
|
|
243
|
+
starting_star = match_with_regex(value, SegmentOperandRegexEnum::STARTING_STAR)
|
|
244
|
+
ending_star = match_with_regex(value, SegmentOperandRegexEnum::ENDING_STAR)
|
|
245
|
+
|
|
246
|
+
# Determine specific wildcard type
|
|
247
|
+
if starting_star && ending_star
|
|
110
248
|
type = SegmentOperandValueEnum::STARTING_ENDING_STAR_VALUE
|
|
111
|
-
|
|
112
|
-
elsif value.match?(SegmentOperandRegexEnum::STARTING_STAR)
|
|
249
|
+
elsif starting_star
|
|
113
250
|
type = SegmentOperandValueEnum::STARTING_STAR_VALUE
|
|
114
|
-
|
|
115
|
-
elsif value.match?(SegmentOperandRegexEnum::ENDING_STAR)
|
|
251
|
+
elsif ending_star
|
|
116
252
|
type = SegmentOperandValueEnum::ENDING_STAR_VALUE
|
|
117
|
-
value = value.gsub(/\*$/, '')
|
|
118
253
|
end
|
|
254
|
+
|
|
255
|
+
# Remove wildcard characters from the operand value
|
|
256
|
+
value = value
|
|
257
|
+
.gsub(Regexp.new(SegmentOperandRegexEnum::STARTING_STAR), '')
|
|
258
|
+
.gsub(Regexp.new(SegmentOperandRegexEnum::ENDING_STAR), '')
|
|
259
|
+
|
|
119
260
|
{ operand_type: type, operand_value: value }
|
|
120
261
|
when /#{SegmentOperandRegexEnum::REGEX_MATCH}/
|
|
121
262
|
{ operand_type: SegmentOperandValueEnum::REGEX_VALUE, operand_value: extract_operand_value(operand, SegmentOperandRegexEnum::REGEX_MATCH) }
|
|
@@ -137,29 +278,54 @@ class SegmentOperandEvaluator
|
|
|
137
278
|
# @param regex [String] The regex to match the operand against
|
|
138
279
|
# @return [String] The extracted operand value
|
|
139
280
|
def extract_operand_value(operand, regex)
|
|
140
|
-
|
|
141
|
-
|
|
281
|
+
match_result = match_with_regex(operand, regex)
|
|
282
|
+
match_result && match_result[1] ? match_result[1] : ''
|
|
142
283
|
end
|
|
143
284
|
|
|
144
|
-
# Processes
|
|
145
|
-
# @param
|
|
146
|
-
# @param
|
|
147
|
-
# @param
|
|
148
|
-
# @return [Hash]
|
|
149
|
-
def process_values(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
285
|
+
# Processes numeric values from operand and tag values, converting them to strings.
|
|
286
|
+
# @param operand_value [Any] The operand value to process.
|
|
287
|
+
# @param tag_value [Any] The tag value to process.
|
|
288
|
+
# @param operand_type [String] The type of operand being evaluated (optional).
|
|
289
|
+
# @return [Hash] An object containing the processed operand and tag values as strings.
|
|
290
|
+
def process_values(operand_value, tag_value, operand_type = nil)
|
|
291
|
+
if [SegmentOperatorValueEnum::IP, SegmentOperatorValueEnum::BROWSER_VERSION, SegmentOperatorValueEnum::OS_VERSION].include?(operand_type)
|
|
292
|
+
return {
|
|
293
|
+
operand_value: operand_value,
|
|
294
|
+
tag_value: tag_value
|
|
295
|
+
}
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Convert operand and tag values to floats
|
|
299
|
+
if NON_NUMERIC_PATTERN.match?(tag_value.to_s)
|
|
300
|
+
return {
|
|
301
|
+
operand_value: operand_value,
|
|
302
|
+
tag_value: tag_value
|
|
303
|
+
}
|
|
304
|
+
end
|
|
154
305
|
|
|
155
306
|
processed_operand_value = operand_value.to_f
|
|
156
307
|
processed_tag_value = tag_value.to_f
|
|
157
308
|
|
|
158
|
-
|
|
159
|
-
|
|
309
|
+
# Return original values if conversion fails
|
|
310
|
+
if processed_operand_value == 0 && operand_value.to_s != '0' && operand_value.to_s != '0.0'
|
|
311
|
+
return {
|
|
312
|
+
operand_value: operand_value,
|
|
313
|
+
tag_value: tag_value
|
|
314
|
+
}
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
if processed_tag_value == 0 && tag_value.to_s != '0' && tag_value.to_s != '0.0'
|
|
318
|
+
return {
|
|
319
|
+
operand_value: operand_value,
|
|
320
|
+
tag_value: tag_value
|
|
321
|
+
}
|
|
160
322
|
end
|
|
161
323
|
|
|
162
|
-
|
|
324
|
+
# Convert numeric values back to strings
|
|
325
|
+
{
|
|
326
|
+
operand_value: processed_operand_value.to_s,
|
|
327
|
+
tag_value: processed_tag_value.to_s
|
|
328
|
+
}
|
|
163
329
|
end
|
|
164
330
|
|
|
165
331
|
# Extracts the result from the operand value and tag value
|
|
@@ -168,31 +334,81 @@ class SegmentOperandEvaluator
|
|
|
168
334
|
# @param tag_value [String] The value of the tag
|
|
169
335
|
# @return [Boolean] True if the operand value matches the tag value, false otherwise
|
|
170
336
|
def extract_result(operand_type, operand_value, tag_value)
|
|
337
|
+
result = false
|
|
338
|
+
|
|
339
|
+
return false if tag_value.nil?
|
|
340
|
+
|
|
341
|
+
# Ensure operand_value and tag_value are strings
|
|
342
|
+
operand_value_str = operand_value.to_s
|
|
343
|
+
tag_value_str = tag_value.to_s
|
|
344
|
+
|
|
171
345
|
case operand_type
|
|
172
346
|
when SegmentOperandValueEnum::LOWER_VALUE
|
|
173
|
-
|
|
347
|
+
result = operand_value_str.downcase == tag_value_str.downcase
|
|
174
348
|
when SegmentOperandValueEnum::STARTING_ENDING_STAR_VALUE
|
|
175
|
-
|
|
349
|
+
result = tag_value_str.include?(operand_value_str)
|
|
176
350
|
when SegmentOperandValueEnum::STARTING_STAR_VALUE
|
|
177
|
-
|
|
351
|
+
result = tag_value_str.end_with?(operand_value_str)
|
|
178
352
|
when SegmentOperandValueEnum::ENDING_STAR_VALUE
|
|
179
|
-
|
|
353
|
+
result = tag_value_str.start_with?(operand_value_str)
|
|
180
354
|
when SegmentOperandValueEnum::REGEX_VALUE
|
|
181
355
|
begin
|
|
182
|
-
|
|
356
|
+
pattern = Regexp.new(operand_value_str)
|
|
357
|
+
result = pattern.match?(tag_value_str)
|
|
183
358
|
rescue StandardError
|
|
184
|
-
false
|
|
359
|
+
result = false
|
|
185
360
|
end
|
|
186
361
|
when SegmentOperandValueEnum::GREATER_THAN_VALUE
|
|
187
|
-
|
|
188
|
-
when SegmentOperandValueEnum::LESS_THAN_VALUE
|
|
189
|
-
operand_value.to_f > tag_value.to_f
|
|
362
|
+
result = compare_versions(tag_value_str, operand_value_str) > 0
|
|
190
363
|
when SegmentOperandValueEnum::GREATER_THAN_EQUAL_TO_VALUE
|
|
191
|
-
|
|
364
|
+
result = compare_versions(tag_value_str, operand_value_str) >= 0
|
|
365
|
+
when SegmentOperandValueEnum::LESS_THAN_VALUE
|
|
366
|
+
result = compare_versions(tag_value_str, operand_value_str) < 0
|
|
192
367
|
when SegmentOperandValueEnum::LESS_THAN_EQUAL_TO_VALUE
|
|
193
|
-
|
|
368
|
+
result = compare_versions(tag_value_str, operand_value_str) <= 0
|
|
194
369
|
else
|
|
195
|
-
|
|
370
|
+
# For version-like strings, use version comparison; otherwise use string comparison
|
|
371
|
+
if version_string?(tag_value_str) && version_string?(operand_value_str)
|
|
372
|
+
result = compare_versions(tag_value_str, operand_value_str) == 0
|
|
373
|
+
else
|
|
374
|
+
result = tag_value_str == operand_value_str
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
result
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Checks if a string appears to be a version string (contains only digits and dots).
|
|
382
|
+
# @param str [String] The string to check.
|
|
383
|
+
# @return [Boolean] True if the string appears to be a version string.
|
|
384
|
+
def version_string?(str)
|
|
385
|
+
/^(\d+\.)*\d+$/.match?(str)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Compares two version strings using semantic versioning rules.
|
|
389
|
+
# Supports formats like "1.2.3", "1.0", "2.1.4.5", etc.
|
|
390
|
+
# @param version1 [String] First version string.
|
|
391
|
+
# @param version2 [String] Second version string.
|
|
392
|
+
# @return [Integer] -1 if version1 < version2, 0 if equal, 1 if version1 > version2.
|
|
393
|
+
def compare_versions(version1, version2)
|
|
394
|
+
# Split versions by dots and convert to integers
|
|
395
|
+
parts1 = version1.split('.').map { |part| part.match?(/^\d+$/) ? part.to_i : 0 }
|
|
396
|
+
parts2 = version2.split('.').map { |part| part.match?(/^\d+$/) ? part.to_i : 0 }
|
|
397
|
+
|
|
398
|
+
# Find the maximum length to handle different version formats
|
|
399
|
+
max_length = [parts1.length, parts2.length].max
|
|
400
|
+
|
|
401
|
+
(0...max_length).each do |i|
|
|
402
|
+
part1 = i < parts1.length ? parts1[i] : 0
|
|
403
|
+
part2 = i < parts2.length ? parts2[i] : 0
|
|
404
|
+
|
|
405
|
+
if part1 < part2
|
|
406
|
+
return -1
|
|
407
|
+
elsif part1 > part2
|
|
408
|
+
return 1
|
|
409
|
+
end
|
|
196
410
|
end
|
|
411
|
+
|
|
412
|
+
0 # Versions are equal
|
|
197
413
|
end
|
|
198
414
|
end
|
data/lib/vwo/utils/event_util.rb
CHANGED
|
@@ -33,4 +33,17 @@ def send_sdk_init_event(settings_fetch_time, sdk_init_time)
|
|
|
33
33
|
# Send the constructed payload via POST request
|
|
34
34
|
NetworkUtil.send_event(properties, payload)
|
|
35
35
|
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Sends a usage stats event to VWO.
|
|
39
|
+
# @param usage_stats_account_id - The account id for usage stats.
|
|
40
|
+
def send_sdk_usage_stats_event(usage_stats_account_id)
|
|
41
|
+
# create query parameters
|
|
42
|
+
properties = NetworkUtil.get_events_base_properties(EventEnum::VWO_USAGE_STATS, nil, nil, true, usage_stats_account_id)
|
|
43
|
+
|
|
44
|
+
# create payload
|
|
45
|
+
payload = NetworkUtil.get_sdk_usage_stats_payload_data(EventEnum::VWO_USAGE_STATS, usage_stats_account_id)
|
|
46
|
+
|
|
47
|
+
# send event
|
|
48
|
+
NetworkUtil.send_event(properties, payload)
|
|
36
49
|
end
|
|
@@ -72,7 +72,7 @@ end
|
|
|
72
72
|
def add_is_gateway_service_required_flag(settings)
|
|
73
73
|
pattern = /
|
|
74
74
|
(?!custom_variable\s*:\s*{\s*"name"\s*:\s*") # Prevent matching inside custom_variable
|
|
75
|
-
\b(country|region|city|os|device|device_type|browser_string|ua)\b
|
|
75
|
+
\b(country|region|city|os|device|device_type|browser_string|ua|browser_version|os_version)\b
|
|
76
76
|
|
|
|
77
77
|
"custom_variable"\s*:\s*{\s*"name"\s*:\s*"inlist\([^)]*\)"
|
|
78
78
|
/x
|
|
@@ -35,12 +35,10 @@ def create_and_send_impression_for_variation_shown(settings, campaign_id, variat
|
|
|
35
35
|
|
|
36
36
|
# Construct payload data for tracking the user
|
|
37
37
|
payload = NetworkUtil.get_track_user_payload_data(
|
|
38
|
-
context.get_id,
|
|
39
38
|
EventEnum::VWO_VARIATION_SHOWN,
|
|
40
39
|
campaign_id,
|
|
41
40
|
variation_id,
|
|
42
|
-
context
|
|
43
|
-
context.get_ip_address
|
|
41
|
+
context
|
|
44
42
|
)
|
|
45
43
|
|
|
46
44
|
# check if batching is enabled
|
|
@@ -24,6 +24,7 @@ require_relative '../packages/network_layer/models/response_model'
|
|
|
24
24
|
require_relative '../utils/url_util'
|
|
25
25
|
require_relative '../utils/uuid_util'
|
|
26
26
|
require_relative '../utils/usage_stats_util'
|
|
27
|
+
require_relative '../models/user/context_model'
|
|
27
28
|
|
|
28
29
|
class NetworkUtil
|
|
29
30
|
class << self
|
|
@@ -80,52 +81,81 @@ class NetworkUtil
|
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
# Builds generic properties for different tracking calls
|
|
83
|
-
def get_events_base_properties(event_name, visitor_user_agent = '', ip_address = '')
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
visitor_ip: ip_address || '',
|
|
94
|
-
url: "#{UrlUtil.get_base_url}#{UrlEnum::EVENTS}"
|
|
84
|
+
def get_events_base_properties(event_name, visitor_user_agent = '', ip_address = '', is_usage_stats_event = false, usage_stat_account_id = '')
|
|
85
|
+
properties = {
|
|
86
|
+
en: event_name,
|
|
87
|
+
a: SettingsService.instance.account_id,
|
|
88
|
+
eTime: get_current_unix_timestamp_in_millis,
|
|
89
|
+
random: get_random_number,
|
|
90
|
+
p: 'FS',
|
|
91
|
+
visitor_ua: visitor_user_agent || '',
|
|
92
|
+
visitor_ip: ip_address || '',
|
|
93
|
+
url: "#{UrlUtil.get_base_url}#{UrlEnum::EVENTS}"
|
|
95
94
|
}
|
|
95
|
+
|
|
96
|
+
if !is_usage_stats_event
|
|
97
|
+
# set env key for standard sdk events
|
|
98
|
+
properties[:env] = SettingsService.instance.sdk_key
|
|
99
|
+
else
|
|
100
|
+
# set env key for usage stats events
|
|
101
|
+
properties[:a] = usage_stat_account_id
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
properties
|
|
96
105
|
end
|
|
97
106
|
|
|
98
107
|
# Builds base payload for tracking events
|
|
99
|
-
def _get_event_base_payload(user_id, event_name, visitor_user_agent = '', ip_address = '')
|
|
100
|
-
|
|
108
|
+
def _get_event_base_payload(user_id, event_name, visitor_user_agent = '', ip_address = '', is_usage_stats_event = false, usage_stat_account_id = '')
|
|
109
|
+
account_id = SettingsService.instance.account_id
|
|
110
|
+
|
|
111
|
+
if is_usage_stats_event
|
|
112
|
+
account_id = usage_stat_account_id
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
uuid = UUIDUtil.get_uuid(user_id.to_s, account_id.to_s)
|
|
101
116
|
sdk_key = SettingsService.instance.sdk_key
|
|
102
117
|
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
117
|
-
visitor: {
|
|
118
|
-
props: {
|
|
119
|
-
vwo_fs_environment: sdk_key
|
|
120
|
-
}
|
|
118
|
+
payload = {
|
|
119
|
+
d: {
|
|
120
|
+
msgId: "#{uuid}-#{get_current_unix_timestamp_in_millis}",
|
|
121
|
+
visId: uuid,
|
|
122
|
+
sessionId: get_current_unix_timestamp,
|
|
123
|
+
event: {
|
|
124
|
+
props: {
|
|
125
|
+
vwo_sdkName: Constants::SDK_NAME,
|
|
126
|
+
vwo_sdkVersion: Constants::SDK_VERSION,
|
|
127
|
+
},
|
|
128
|
+
name: event_name,
|
|
129
|
+
time: get_current_unix_timestamp_in_millis
|
|
130
|
+
}
|
|
121
131
|
}
|
|
122
132
|
}
|
|
123
|
-
|
|
133
|
+
|
|
134
|
+
if !is_usage_stats_event
|
|
135
|
+
# set env key for standard sdk events
|
|
136
|
+
payload[:d][:event][:props][:vwo_envKey] = sdk_key
|
|
137
|
+
|
|
138
|
+
# set visitor props for standard sdk events
|
|
139
|
+
payload[:d][:visitor] = {
|
|
140
|
+
props: {
|
|
141
|
+
vwo_fs_environment: sdk_key
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
payload
|
|
124
147
|
end
|
|
125
148
|
|
|
126
149
|
# Builds track-user payload data
|
|
127
|
-
def get_track_user_payload_data(
|
|
150
|
+
def get_track_user_payload_data(event_name, campaign_id, variation_id, context)
|
|
151
|
+
user_id = context.get_id
|
|
152
|
+
visitor_user_agent = context.get_user_agent
|
|
153
|
+
ip_address = context.get_ip_address
|
|
154
|
+
custom_variables = context.get_custom_variables
|
|
155
|
+
post_segmentation_variables = context.get_post_segmentation_variables
|
|
156
|
+
|
|
128
157
|
properties = _get_event_base_payload(user_id, event_name, visitor_user_agent, ip_address)
|
|
158
|
+
|
|
129
159
|
properties[:d][:event][:props][:id] = campaign_id
|
|
130
160
|
properties[:d][:event][:props][:variation] = variation_id
|
|
131
161
|
properties[:d][:event][:props][:isFirst] = 1
|
|
@@ -134,10 +164,20 @@ class NetworkUtil
|
|
|
134
164
|
properties[:d][:visitor_ua] = visitor_user_agent if visitor_user_agent && !visitor_user_agent.empty?
|
|
135
165
|
properties[:d][:visitor_ip] = ip_address if ip_address && !ip_address.empty?
|
|
136
166
|
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
167
|
+
# Add post-segmentation variables if they exist in custom variables
|
|
168
|
+
if post_segmentation_variables&.any? && custom_variables&.any?
|
|
169
|
+
post_segmentation_variables.each do |key|
|
|
170
|
+
# Try to get value using string key first, then symbol key
|
|
171
|
+
value = custom_variables[key] || custom_variables[key.to_sym]
|
|
172
|
+
if value
|
|
173
|
+
properties[:d][:visitor][:props][key] = value
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Add IP address as a standard attribute if available
|
|
179
|
+
if ip_address && !ip_address.empty?
|
|
180
|
+
properties[:d][:visitor][:props][:ip] = ip_address
|
|
141
181
|
end
|
|
142
182
|
|
|
143
183
|
LoggerService.log(LogLevelEnum::DEBUG, "IMPRESSION_FOR_TRACK_USER", {
|
|
@@ -207,6 +247,18 @@ class NetworkUtil
|
|
|
207
247
|
properties
|
|
208
248
|
end
|
|
209
249
|
|
|
250
|
+
# Constructs the payload for usage stats called event.
|
|
251
|
+
# @param event_name - The name of the event.
|
|
252
|
+
# @param usage_stats_account_id - The account id for usage stats.
|
|
253
|
+
# @returns The constructed payload with required fields.
|
|
254
|
+
def get_sdk_usage_stats_payload_data(event_name, usage_stats_account_id)
|
|
255
|
+
user_id = SettingsService.instance.account_id.to_s + "_" + SettingsService.instance.sdk_key
|
|
256
|
+
properties = _get_event_base_payload(user_id, event_name, nil, nil, true, usage_stats_account_id)
|
|
257
|
+
properties[:d][:event][:props][:product] = Constants::PRODUCT_NAME
|
|
258
|
+
properties[:d][:event][:props][:vwoMeta] = UsageStatsUtil.get_usage_stats
|
|
259
|
+
properties
|
|
260
|
+
end
|
|
261
|
+
|
|
210
262
|
# Sends a POST API request with given properties and payload
|
|
211
263
|
def send_post_api_request(properties, payload)
|
|
212
264
|
network_instance = NetworkManager.instance
|
|
@@ -229,16 +281,10 @@ class NetworkUtil
|
|
|
229
281
|
if network_instance.get_client.get_should_use_threading
|
|
230
282
|
network_instance.get_client.get_thread_pool.post {
|
|
231
283
|
response = network_instance.post(request)
|
|
232
|
-
if response.get_status_code == 200
|
|
233
|
-
UsageStatsUtil.clear_usage_stats
|
|
234
|
-
end
|
|
235
284
|
response
|
|
236
285
|
}
|
|
237
286
|
else
|
|
238
287
|
response = network_instance.post(request)
|
|
239
|
-
if response.get_status_code == 200
|
|
240
|
-
UsageStatsUtil.clear_usage_stats
|
|
241
|
-
end
|
|
242
288
|
response
|
|
243
289
|
end
|
|
244
290
|
rescue ResponseModel => err
|
|
@@ -259,15 +305,20 @@ class NetworkUtil
|
|
|
259
305
|
headers[HeadersEnum::USER_AGENT] = payload[:d][:visitor_ua] if payload[:d][:visitor_ua]
|
|
260
306
|
headers[HeadersEnum::IP] = payload[:d][:visitor_ip] if payload[:d][:visitor_ip]
|
|
261
307
|
|
|
308
|
+
url = Constants::HOST_NAME
|
|
309
|
+
if UrlUtil.get_collection_prefix && !UrlUtil.get_collection_prefix.empty?
|
|
310
|
+
url = "#{url}/#{UrlUtil.get_collection_prefix}"
|
|
311
|
+
end
|
|
312
|
+
|
|
262
313
|
request = RequestModel.new(
|
|
263
|
-
|
|
314
|
+
url,
|
|
264
315
|
HttpMethodEnum::POST,
|
|
265
316
|
UrlEnum::EVENTS,
|
|
266
317
|
properties,
|
|
267
318
|
payload,
|
|
268
319
|
headers,
|
|
269
|
-
|
|
270
|
-
|
|
320
|
+
Constants::HTTPS_PROTOCOL,
|
|
321
|
+
nil
|
|
271
322
|
)
|
|
272
323
|
|
|
273
324
|
begin
|
data/lib/vwo/utils/url_util.rb
CHANGED
|
@@ -69,6 +69,8 @@ class UsageStatsUtil
|
|
|
69
69
|
|
|
70
70
|
data = {}
|
|
71
71
|
|
|
72
|
+
data[:a] = SettingsService.instance.account_id
|
|
73
|
+
data[:env] = SettingsService.instance.sdk_key
|
|
72
74
|
data[:ig] = 1 if integrations
|
|
73
75
|
data[:eb] = 1 if event_batching
|
|
74
76
|
data[:gs] = 1 if gateway_service
|
|
@@ -84,7 +86,7 @@ class UsageStatsUtil
|
|
|
84
86
|
data[:ll] = LogLevelToNumber.to_number(logger[:level]) || -1
|
|
85
87
|
end
|
|
86
88
|
|
|
87
|
-
data[:pi] =
|
|
89
|
+
data[:pi] = poll_interval if poll_interval
|
|
88
90
|
|
|
89
91
|
if vwo_meta && vwo_meta.key?(:ea)
|
|
90
92
|
data[:_ea] = 1
|
data/lib/vwo.rb
CHANGED
|
@@ -93,6 +93,14 @@ class VWO
|
|
|
93
93
|
# send the sdk init info to vwo server
|
|
94
94
|
send_sdk_init_event(SettingsService.instance.settings_fetch_time, time_taken_for_init.to_s)
|
|
95
95
|
end
|
|
96
|
+
|
|
97
|
+
# send the usage stats event to vwo server
|
|
98
|
+
# get usage stats account id from settings
|
|
99
|
+
usage_stats_account_id = @@instance.original_settings["usageStatsAccountId"]
|
|
100
|
+
if usage_stats_account_id
|
|
101
|
+
send_sdk_usage_stats_event(usage_stats_account_id)
|
|
102
|
+
end
|
|
103
|
+
|
|
96
104
|
@@instance
|
|
97
105
|
rescue StandardError => e
|
|
98
106
|
puts "[ERROR]: VWO-SDK: Got error while initializing VWO: #{e.message}"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vwo-fme-ruby-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- VWO
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-09-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: uuidtools
|