vwo-fme-ruby-sdk 1.5.0 → 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/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/gateway_service_util.rb +1 -1
- data/lib/vwo/utils/impression_util.rb +1 -3
- data/lib/vwo/utils/network_util.rb +25 -1
- 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
|
@@ -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
|
@@ -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
|
@@ -146,8 +147,15 @@ class NetworkUtil
|
|
146
147
|
end
|
147
148
|
|
148
149
|
# Builds track-user payload data
|
149
|
-
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
|
+
|
150
157
|
properties = _get_event_base_payload(user_id, event_name, visitor_user_agent, ip_address)
|
158
|
+
|
151
159
|
properties[:d][:event][:props][:id] = campaign_id
|
152
160
|
properties[:d][:event][:props][:variation] = variation_id
|
153
161
|
properties[:d][:event][:props][:isFirst] = 1
|
@@ -156,6 +164,22 @@ class NetworkUtil
|
|
156
164
|
properties[:d][:visitor_ua] = visitor_user_agent if visitor_user_agent && !visitor_user_agent.empty?
|
157
165
|
properties[:d][:visitor_ip] = ip_address if ip_address && !ip_address.empty?
|
158
166
|
|
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
|
181
|
+
end
|
182
|
+
|
159
183
|
LoggerService.log(LogLevelEnum::DEBUG, "IMPRESSION_FOR_TRACK_USER", {
|
160
184
|
accountId: SettingsService.instance.account_id,
|
161
185
|
userId: user_id,
|
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
|