vwo-sdk 1.15.0 → 1.23.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/vwo/enums.rb CHANGED
@@ -106,13 +106,14 @@ class VWO
106
106
  IMPRESSION_FOR_TRACK_USER = '(%<file>s): Impression built for track-user - %<properties>s'
107
107
  IMPRESSION_FOR_TRACK_GOAL = '(%<file>s): Impression built for track-goal - %<properties>s'
108
108
  GOT_VARIATION_FOR_USER = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_key>s got variationName:%<variation_name>s'
109
- SEGMENTATION_STATUS = '(%<file>s): In API: %<api_name>s, for UserId:%<user_id>s of campaign:%<campaign_key>s with variables:%<custom_variables>s %<status>s %<segmentation_type>s for %<variation_name>s'
109
+ SEGMENTATION_STATUS = '(%<file>s): In API: %<api_name>s, for UserId:%<user_id>s of campaign:%<campaign_key>s with variables:%<custom_variables>s %<status>s %<segmentation_type>s %<variation_name>s'
110
110
  PARAMS_FOR_PUSH_CALL = '(%<file>s): Params for push call - %<properties>s'
111
111
  CAMPAIGN_NOT_ACTIVATED = '(%<file>s): Campaign:%<campaign_key>s for User ID:%<user_id>s is not yet activated for API:%<api_name>s. Use activate API to activate A/B test or isFeatureEnabled API to activate Feature Test.'
112
112
  BATCH_EVENT_LIMIT_EXCEEDED = '(%<file>s): Impression event - %<end_point>s failed due to exceeding payload size. Parameter eventsPerRequest in batchEvents config in launch API has value:%<eventsPerRequest>s for accountId:%<accountId>s. Please read the official documentation for knowing the size limits.'
113
113
  BULK_NOT_PROCESSED = "(%<file>s): Batch events couldn't be received by VWO. Calling Flush Callback with error and data."
114
114
  BEFORE_FLUSHING = '(%<file>s): Flushing events queue %<manually>s having %<length>s events %<timer>s queue summary: %<queue_metadata>s'
115
115
  EVENT_BATCHING_INSUFFICIENT = '(%<file>s): %<key>s not provided, assigning default value'
116
+ GOT_ELIGIBLE_CAMPAIGNS = "(%<file>s): Campaigns:%<eligible_campaigns_key>s are eligible, %<ineligible_campaigns_log_text>s are ineligible from the Group:%<group_name>s for the User ID:%<user_id>s"
116
117
  end
117
118
 
118
119
  # Info Messages
@@ -142,7 +143,7 @@ class VWO
142
143
  USER_PASSED_SEGMENTATION = '(%<file>s): UserId:%<user_id>s of campaign:%<campaign_key>s with custom_variables:%<custom_variables>s passed segmentation'
143
144
  USER_FAILED_SEGMENTATION = '(%<file>s): UserId:%<user_id>s of campaign:%<campaign_key>s with custom_variables:%<custom_variables>s failed segmentation'
144
145
 
145
- NO_CUSTOM_VARIABLES = '(%<file>s): In API: %<api_name>s, for UserId:%<user_id>s preSegments/customVariables are not passed for campaign:%<campaign_key>s and campaign has pre-segmentation'
146
+ NO_CUSTOM_VARIABLES = '(%<file>s): In API: %<api_name>s, for UserId:%<user_id>s customVariables are not passed for campaign:%<campaign_key>s and campaign has pre-segmentation'
146
147
  SKIPPING_SEGMENTATION = '(%<file>s): In API: %<api_name>s, Skipping segmentation:%<variation>s for UserId:%<user_id>s as no valid segments found in campaign:%<campaign_key>s'
147
148
 
148
149
  SEGMENTATION_STATUS = '(%<file>s): In API: %<api_name>s, for UserId:%<user_id>s of campaign:%<campaign_key>s with variables:%<custom_variables>s %<status>s %<segmentation_type>s %<variation_name>s'
@@ -152,9 +153,13 @@ class VWO
152
153
  CAMPAIGN_NOT_ACTIVATED = '(%<file>s): Activate the campaign:%<campaign_key>s for User ID:%<user_id>s to %<reason>s.'
153
154
  GOAL_ALREADY_TRACKED = '(%<file>s): Goal:%<goal_identifier>s of Campaign:%<campaign_key>s for User ID:%<user_id>s has already been tracked earlier. Skipping now'
154
155
  USER_ALREADY_TRACKED = '(%<file>s): User ID:%<user_id>s for Campaign:%<campaign_key>s has already been tracked earlier for "%<api_name>s" API. Skipping now'
155
- API_CALLED = '(%<file>s): API: {api_name} called for UserId:%<user_id>s'
156
+ API_CALLED = '(%<file>s): API: %<api_name>s called for UserId:%<user_id>s'
156
157
  BULK_IMPRESSION_SUCCESS = '(%<file>s): Impression event - %<end_point>s was successfully received by VWO having accountId:%<a>s'
157
158
  AFTER_FLUSHING = '(%<file>s): Events queue having %<length>s events has been flushed %<manually>s queue summary: %<queue_metadata>s'
159
+ GOT_WINNER_CAMPAIGN = "(%<file>s): Campaign:%<campaign_key>s is selected from the mutually exclusive group:%<group_name>s for the User ID:%<user_id>s"
160
+ GOT_ELIGIBLE_CAMPAIGNS = "(%<file>s): Got %<no_of_eligible_campaigns>s eligible winners out of %<no_of_group_campaigns>s from the Group:%<group_name>s and for User ID:%<user_id>s"
161
+ CALLED_CAMPAIGN_NOT_WINNER = "(%<file>s): Campaign:%<campaign_key>s does not qualify from the mutually exclusive group:%<group_name>s for User ID:%<user_id>s"
162
+ OTHER_CAMPAIGN_SATISFIES_WHITELISTING_OR_STORAGE = "(%<file>s): Campaign:%<campaign_key>s of Group:%<group_name>s satisfies %<type>s for User ID:%<user_id>s"
158
163
  end
159
164
 
160
165
  # Warning Messages
data/lib/vwo/logger.rb CHANGED
@@ -28,8 +28,10 @@ class VWO
28
28
  end
29
29
 
30
30
  # Override this method to handle logs in a custom manner
31
- def log(level, message)
32
- @@logger_instance.log(level, message)
31
+ def log(level, message, disable_logs = false)
32
+ unless disable_logs
33
+ @@logger_instance.log(level, message)
34
+ end
33
35
  end
34
36
 
35
37
  def instance
@@ -54,7 +54,13 @@ class VWO
54
54
  },
55
55
  weight: {
56
56
  type: %w[number string]
57
- }
57
+ },
58
+ variables: {
59
+ type: 'array',
60
+ items: {
61
+ '$ref' => '#/definitions/variables_schema'
62
+ }
63
+ }
58
64
  },
59
65
  required: %w[id name weight]
60
66
  },
@@ -79,9 +85,34 @@ class VWO
79
85
  '$ref' => '#/definitions/campaign_variation_schema'
80
86
  }
81
87
  },
88
+ variables: {
89
+ type: 'array',
90
+ items: {
91
+ '$ref' => '#/definitions/variables_schema'
92
+ }
93
+ },
94
+ isBucketingSeedEnabled: ['boolean'],
82
95
  minItems: 2
83
96
  }
84
97
  },
98
+ variables_schema: {
99
+ type: 'object',
100
+ properties: {
101
+ id: {
102
+ type: %w[number string]
103
+ },
104
+ key: {
105
+ type: ['string']
106
+ },
107
+ type: {
108
+ type: ['string']
109
+ },
110
+ value: {
111
+ type: %w[number string boolean double object]
112
+ }
113
+ },
114
+ required: %w[id key type value]
115
+ },
85
116
  required: %w[
86
117
  id
87
118
  key
@@ -91,11 +91,10 @@ class VWO
91
91
  # @return [Boolean]
92
92
  def evaluate_custom_variable?(operand, custom_variables)
93
93
  # Extract custom_variable_key and custom_variables_value from operand
94
-
95
94
  operand_key, operand = get_key_value(operand)
96
95
 
97
96
  # Retrieve corresponding custom_variable value from custom_variables
98
- custom_variables_value = custom_variables[operand_key]
97
+ custom_variables_value = custom_variables[operand_key.to_sym]
99
98
 
100
99
  # Pre process custom_variable value
101
100
  custom_variables_value = process_custom_variables_value(custom_variables_value)
@@ -113,7 +112,7 @@ class VWO
113
112
  def evaluate_user?(operand, custom_variables)
114
113
  users = operand.split(',')
115
114
  users.each do |user|
116
- return true if user.strip == custom_variables['_vwo_user_id']
115
+ return true if user.strip == custom_variables[:_vwo_user_id]
117
116
  end
118
117
  false
119
118
  end
@@ -63,10 +63,11 @@ class VWO
63
63
  # @param[String] :user_id Unique user identifier
64
64
  # @param[Hash] :dsl Segments provided in the settings_file
65
65
  # @param[Hash] :custom_variables Custom variables provided in the apis
66
+ # @param[Boolean] :disable_logs disable logs if True
66
67
  #
67
68
  # @return[Boolean] true if user passed pre-segmentation, else false
68
69
  #
69
- def evaluate(campaign_key, user_id, dsl, custom_variables)
70
+ def evaluate(campaign_key, user_id, dsl, custom_variables, disable_logs = false)
70
71
  result = evaluate_util(dsl, custom_variables) if valid_value?(dsl)
71
72
  result
72
73
  rescue StandardError => e
@@ -79,7 +80,8 @@ class VWO
79
80
  campaign_key: campaign_key,
80
81
  custom_variables: custom_variables,
81
82
  error_message: e
82
- )
83
+ ),
84
+ disable_logs
83
85
  )
84
86
  false
85
87
  end
@@ -57,6 +57,26 @@ class VWO
57
57
  end
58
58
  end
59
59
 
60
+ # Sets campaign allocation range in the provided campaigns list
61
+ #
62
+ # @param [Array]: Array of Campaigns
63
+ def set_campaign_allocation(campaigns)
64
+ current_allocation = 0
65
+ campaigns.each do |campaign|
66
+ step_factor = get_variation_bucketing_range(campaign['weight'])
67
+ if step_factor > 0
68
+ start_range = current_allocation + 1
69
+ end_range = current_allocation + step_factor
70
+ campaign['min_range'] = start_range
71
+ campaign['max_range'] = end_range
72
+ current_allocation += step_factor
73
+ else
74
+ campaign['min_range'] = -1
75
+ campaign['max_range'] = -1
76
+ end
77
+ end
78
+ end
79
+
60
80
  # Returns goal from given campaign_key and gaol_identifier.
61
81
  # @param[String] :campaign Campaign object
62
82
  # @param[String] :goal_identifier Goal identifier
@@ -249,6 +269,44 @@ class VWO
249
269
  return campaign
250
270
  end
251
271
 
272
+ # Checks whether a campaign is part of a group.
273
+ #
274
+ # @param[Hash] :settings_file Settings file for the project
275
+ # @param[Integer] :campaign_id Id of campaign which is to be checked
276
+ # @return[Boolean]
277
+ def is_part_of_group(settings_file, campaign_id)
278
+ if settings_file["campaignGroups"] && (settings_file["campaignGroups"].has_key?(campaign_id.to_s))
279
+ return true
280
+ end
281
+ false
282
+ end
283
+
284
+ # Returns campaigns which are part of given group using group_id.
285
+ #
286
+ # @param[Hash] :settings_file Settings file for the project
287
+ # @param[Integer] :group_id id of group whose campaigns are to be return
288
+ # @return[Array]
289
+ def get_group_campaigns(settings_file, group_id)
290
+ group_campaign_ids = []
291
+ group_campaigns = []
292
+ groups = settings_file["groups"]
293
+
294
+ if groups && groups.has_key?(group_id.to_s)
295
+ group_campaign_ids = groups[group_id.to_s]["campaigns"]
296
+ end
297
+
298
+ if group_campaign_ids
299
+ group_campaign_ids.each do |campaign_id|
300
+ settings_file["campaigns"].each do |campaign|
301
+ if campaign["id"] == campaign_id && campaign["status"] == STATUS_RUNNING
302
+ group_campaigns.push(campaign)
303
+ end
304
+ end
305
+ end
306
+ end
307
+ group_campaigns
308
+ end
309
+
252
310
  end
253
311
  end
254
312
  end
@@ -38,6 +38,8 @@ class VWO
38
38
  return value.to_f if variable_type == VariableTypes::DOUBLE
39
39
 
40
40
  return !value || value == 0 ? false : true if variable_type == VariableTypes.BOOLEAN
41
+
42
+ return value if variable_type == VariableTypes::JSON
41
43
  rescue StandardError => _e
42
44
  VWO::Logger.get_instance.log(
43
45
  LogLevelEnum::ERROR,
@@ -0,0 +1,40 @@
1
+ # Copyright 2019-2021 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 './validations'
17
+
18
+ # Generic utility module
19
+ class VWO
20
+ module Utils
21
+ module Utility
22
+ include Validations
23
+
24
+ # converting hash with keys as strings into hash with keys as strings
25
+ # @param[Hash]
26
+ # @return[Hash]
27
+ def convert_to_symbol_hash(hashObject)
28
+ convertedHash = {}
29
+ hashObject.each do |key, value|
30
+ if valid_hash?(value)
31
+ convertedHash[key.to_sym] = convert_to_symbol_hash(value)
32
+ else
33
+ convertedHash[key.to_sym] = value
34
+ end
35
+ end
36
+ convertedHash
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/vwo.rb CHANGED
@@ -23,6 +23,7 @@ require_relative 'vwo/utils/campaign'
23
23
  require_relative 'vwo/utils/impression'
24
24
  require_relative 'vwo/utils/feature'
25
25
  require_relative 'vwo/utils/custom_dimensions'
26
+ require_relative 'vwo/utils/utility'
26
27
  require_relative 'vwo/constants'
27
28
  require_relative 'vwo/core/variation_decider'
28
29
  require_relative 'vwo/services/batch_events_dispatcher'
@@ -39,6 +40,7 @@ class VWO
39
40
  include Utils::CustomDimensions
40
41
  include Utils::Campaign
41
42
  include Utils::Impression
43
+ include Utils::Utility
42
44
  include CONSTANTS
43
45
 
44
46
  FILE = FileNameEnum::VWO
@@ -62,6 +64,7 @@ class VWO
62
64
  settings_file = nil,
63
65
  options = {}
64
66
  )
67
+ options = convert_to_symbol_hash(options)
65
68
  @account_id = account_id
66
69
  @sdk_key = sdk_key
67
70
  @user_storage = user_storage
@@ -295,9 +298,10 @@ class VWO
295
298
  return
296
299
  end
297
300
 
301
+ options = convert_to_symbol_hash(options)
298
302
  # Retrieve custom variables
299
- custom_variables = options['custom_variables'] || options[:custom_variables]
300
- variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
303
+ custom_variables = options[:custom_variables]
304
+ variation_targeting_variables = options[:variation_targeting_variables]
301
305
 
302
306
  should_track_returning_user = get_should_track_returning_user(options)
303
307
  # Validate input parameters
@@ -413,7 +417,6 @@ class VWO
413
417
  )
414
418
  end
415
419
  end
416
- variation['name']
417
420
  else
418
421
  @logger.log(
419
422
  LogLevelEnum::INFO,
@@ -425,8 +428,8 @@ class VWO
425
428
  api_name: ApiMethods::ACTIVATE
426
429
  )
427
430
  )
428
- nil
429
431
  end
432
+ variation['name']
430
433
  rescue StandardError => e
431
434
  @logger.log(
432
435
  LogLevelEnum::ERROR,
@@ -469,9 +472,10 @@ class VWO
469
472
  )
470
473
  return
471
474
  end
475
+ options = convert_to_symbol_hash(options)
472
476
  # Retrieve custom variables
473
- custom_variables = options['custom_variables'] || options[:custom_variables]
474
- variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
477
+ custom_variables = options[:custom_variables]
478
+ variation_targeting_variables = options[:variation_targeting_variables]
475
479
 
476
480
  # Validate input parameters
477
481
  unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
@@ -580,9 +584,10 @@ class VWO
580
584
  return false
581
585
  end
582
586
 
583
- revenue_value = options['revenue_value'] || options[:revenue_value]
584
- custom_variables = options['custom_variables'] || options[:custom_variables]
585
- variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
587
+ options = convert_to_symbol_hash(options)
588
+ revenue_value = options[:revenue_value]
589
+ custom_variables = options[:custom_variables]
590
+ variation_targeting_variables = options[:variation_targeting_variables]
586
591
  should_track_returning_user = get_should_track_returning_user(options)
587
592
  goal_type_to_track = get_goal_type_to_track(options)
588
593
 
@@ -676,7 +681,7 @@ class VWO
676
681
  if !identifiers.include? goal_identifier
677
682
  updated_goal_identifier = variation['goal_identifier']
678
683
  updated_goal_identifier += VWO_DELIMITER + goal_identifier
679
- @variation_decider.save_user_storage(user_id, campaign['key'], variation['name'], updated_goal_identifier) if variation['name']
684
+ @variation_decider.save_user_storage(user_id, campaign['key'], campaign['name'], variation['name'], updated_goal_identifier) if variation['name']
680
685
  # set variation at user storage
681
686
  elsif !should_track_returning_user
682
687
  @logger.log(
@@ -800,9 +805,10 @@ class VWO
800
805
  return false
801
806
  end
802
807
 
808
+ options = convert_to_symbol_hash(options)
803
809
  # Retrieve custom variables
804
- custom_variables = options['custom_variables'] || options[:custom_variables]
805
- variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
810
+ custom_variables = options[:custom_variables]
811
+ variation_targeting_variables = options[:variation_targeting_variables]
806
812
  should_track_returning_user = get_should_track_returning_user(options)
807
813
  @logger.log(
808
814
  LogLevelEnum::INFO,
@@ -870,80 +876,85 @@ class VWO
870
876
  return false unless variation
871
877
 
872
878
  # if campaign type is feature_test Send track call to server
873
- if campaign_type == CampaignTypes::FEATURE_TEST
874
- if is_eligible_to_send_impression(should_track_returning_user)
875
- if defined?(@batch_events)
876
- impression = create_bulk_event_impression(
877
- @settings_file,
878
- campaign['id'],
879
- variation['id'],
880
- user_id
881
- )
882
- @batch_events_queue.enqueue(impression)
883
- else
884
- impression = create_impression(
885
- @settings_file,
886
- campaign['id'],
887
- variation['id'],
888
- user_id,
889
- @sdk_key,
890
- goal_id: nil,
891
- revenue: nil,
892
- usage_stats: @usage_stats.usage_stats
893
- )
894
879
 
895
- @event_dispatcher.dispatch(impression)
896
- @logger.log(
897
- LogLevelEnum::INFO,
898
- format(
899
- LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
900
- file: FILE,
901
- campaign_id: impression[:experiment_id],
902
- sdk_key: @sdk_key,
903
- account_id: impression[:account_id],
904
- variation_id: impression[:combination]
905
- )
906
- )
907
- end
908
- result = variation['isFeatureEnabled']
909
- if result
910
- @logger.log(
911
- LogLevelEnum::INFO,
912
- format(
913
- LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
914
- file: FILE,
915
- user_id: user_id,
916
- feature_key: campaign_key,
917
- api_name: ApiMethods::IS_FEATURE_ENABLED
918
- )
919
- )
920
- else
921
- @logger.log(
922
- LogLevelEnum::INFO,
923
- format(
924
- LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
925
- file: FILE,
926
- user_id: user_id,
927
- feature_key: campaign_key,
928
- api_name: ApiMethods::IS_FEATURE_ENABLED
929
- )
930
- )
931
- end
932
- return result
880
+ if is_eligible_to_send_impression(should_track_returning_user)
881
+ if defined?(@batch_events)
882
+ impression = create_bulk_event_impression(
883
+ @settings_file,
884
+ campaign['id'],
885
+ variation['id'],
886
+ user_id
887
+ )
888
+ @batch_events_queue.enqueue(impression)
933
889
  else
890
+ impression = create_impression(
891
+ @settings_file,
892
+ campaign['id'],
893
+ variation['id'],
894
+ user_id,
895
+ @sdk_key,
896
+ nil,
897
+ nil,
898
+ usage_stats: @usage_stats.usage_stats
899
+ )
900
+
901
+ @event_dispatcher.dispatch(impression)
934
902
  @logger.log(
935
903
  LogLevelEnum::INFO,
936
904
  format(
937
- LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
905
+ LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
938
906
  file: FILE,
939
- user_id: user_id,
940
- campaign_key: campaign_key,
941
- api_name: ApiMethods::IS_FEATURE_ENABLED
907
+ campaign_id: impression[:experiment_id],
908
+ sdk_key: @sdk_key,
909
+ account_id: impression[:account_id],
910
+ variation_id: impression[:combination]
942
911
  )
943
912
  )
944
913
  end
914
+
915
+ else
916
+ @logger.log(
917
+ LogLevelEnum::INFO,
918
+ format(
919
+ LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
920
+ file: FILE,
921
+ user_id: user_id,
922
+ campaign_key: campaign_key,
923
+ api_name: ApiMethods::IS_FEATURE_ENABLED
924
+ )
925
+ )
945
926
  end
946
- true
927
+ if campaign_type == CampaignTypes::FEATURE_ROLLOUT
928
+ result = true
929
+ else
930
+ result = variation['isFeatureEnabled']
931
+ end
932
+
933
+ if result
934
+ @logger.log(
935
+ LogLevelEnum::INFO,
936
+ format(
937
+ LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
938
+ file: FILE,
939
+ user_id: user_id,
940
+ feature_key: campaign_key,
941
+ api_name: ApiMethods::IS_FEATURE_ENABLED
942
+ )
943
+ )
944
+ else
945
+ @logger.log(
946
+ LogLevelEnum::INFO,
947
+ format(
948
+ LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
949
+ file: FILE,
950
+ user_id: user_id,
951
+ feature_key: campaign_key,
952
+ api_name: ApiMethods::IS_FEATURE_ENABLED
953
+ )
954
+ )
955
+ end
956
+
957
+ result
947
958
  rescue StandardError => e
948
959
  @logger.log(
949
960
  LogLevelEnum::ERROR,
@@ -989,9 +1000,10 @@ class VWO
989
1000
  return
990
1001
  end
991
1002
 
1003
+ options = convert_to_symbol_hash(options)
992
1004
  # Retrieve custom variables
993
- custom_variables = options['custom_variables'] || options[:custom_variables]
994
- variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
1005
+ custom_variables = options[:custom_variables]
1006
+ variation_targeting_variables = options[:variation_targeting_variables]
995
1007
 
996
1008
  unless valid_string?(campaign_key) && valid_string?(variable_key) && valid_string?(user_id) &&
997
1009
  (custom_variables.nil? || valid_hash?(custom_variables)) && (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vwo-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0
4
+ version: 1.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - VWO
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-23 00:00:00.000000000 Z
11
+ date: 2021-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codecov
@@ -111,6 +111,7 @@ files:
111
111
  - lib/vwo/utils/impression.rb
112
112
  - lib/vwo/utils/request.rb
113
113
  - lib/vwo/utils/segment.rb
114
+ - lib/vwo/utils/utility.rb
114
115
  - lib/vwo/utils/uuid.rb
115
116
  - lib/vwo/utils/validations.rb
116
117
  homepage: https://vwo.com/fullstack/server-side-testing/