optimizely-sdk 1.5.0 → 2.0.0.beta

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fae777d6ce539eb2e0809dbf38d4784130c8ade
4
- data.tar.gz: f65bee6796830490f4ee8f1a86da20ed7b4f01fb
3
+ metadata.gz: b6da10a1ad97624a7523adcdc6841b2999ab35d3
4
+ data.tar.gz: 3865376c1962511db8d8350041fbc830b29b16c6
5
5
  SHA512:
6
- metadata.gz: b8e6f2732d692b1844d3f0c990010aba9858c93b82244803962810d33c78b945f925936874097272705a1a7e724f9d3abad1c4c66ba358573ba32c8bd10d8515
7
- data.tar.gz: a8804feff56093f399f87425869a733e9b91b74f502b075a1d790364f47da5e524c36ecde23a626d2b8c977c344e5bd2fb5c93e742ceb5a2cfcf4a50710b9444
6
+ metadata.gz: bf35c547babd0be1d576bf8926e23fa9e9292b47bf6a74df9a8cda19b00bd2a3da974c7a840dba7d6ac3aa3132b192caa692b727bc5bdfdf3a3a85c933ef58da
7
+ data.tar.gz: b587d153eb46d6b779ebc298dfde188824f7c5f3fe1cc9c2b4e890878a9c190e9d9051baccbc8d18329670d81c5e5437e53f2d69d4c790a785b8fcb8fa2ac2e3
data/lib/optimizely.rb CHANGED
@@ -24,7 +24,6 @@ require_relative 'optimizely/helpers/group'
24
24
  require_relative 'optimizely/helpers/validator'
25
25
  require_relative 'optimizely/helpers/variable_type'
26
26
  require_relative 'optimizely/logger'
27
- require_relative 'optimizely/notification_center'
28
27
  require_relative 'optimizely/project_config'
29
28
 
30
29
  module Optimizely
@@ -39,7 +38,6 @@ module Optimizely
39
38
  attr_reader :event_builder
40
39
  attr_reader :event_dispatcher
41
40
  attr_reader :logger
42
- attr_reader :notification_center
43
41
 
44
42
  def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = nil, skip_json_validation = false, user_profile_service = nil)
45
43
  # Constructor for Projects.
@@ -85,7 +83,6 @@ module Optimizely
85
83
 
86
84
  @decision_service = DecisionService.new(@config, @user_profile_service)
87
85
  @event_builder = EventBuilder.new(@config)
88
- @notification_center = NotificationCenter.new(@logger, @error_handler)
89
86
  end
90
87
 
91
88
  def activate(experiment_key, user_id, attributes = nil)
@@ -134,11 +131,6 @@ module Optimizely
134
131
  return nil
135
132
  end
136
133
 
137
- unless user_id.is_a? String
138
- @logger.log(Logger::ERROR, "User id: #{user_id} is not a string")
139
- return nil
140
- end
141
-
142
134
  unless user_inputs_valid?(attributes)
143
135
  @logger.log(Logger::INFO, "Not activating user '#{user_id}.")
144
136
  return nil
@@ -155,36 +147,6 @@ module Optimizely
155
147
  nil
156
148
  end
157
149
 
158
- def set_forced_variation(experiment_key, user_id, variation_key)
159
- # Force a user into a variation for a given experiment.
160
- #
161
- # experiment_key - String - key identifying the experiment.
162
- # user_id - String - The user ID to be used for bucketing.
163
- # variation_key - The variation key specifies the variation which the user will
164
- # be forced into. If nil, then clear the existing experiment-to-variation mapping.
165
- #
166
- # Returns - Boolean - indicates if the set completed successfully.
167
-
168
- @config.set_forced_variation(experiment_key, user_id, variation_key);
169
- end
170
-
171
- def get_forced_variation(experiment_key, user_id)
172
- # Gets the forced variation for a given user and experiment.
173
- #
174
- # experiment_key - String - Key identifying the experiment.
175
- # user_id - String - The user ID to be used for bucketing.
176
- #
177
- # Returns String|nil The forced variation key.
178
-
179
- forced_variation_key = nil
180
- forced_variation = @config.get_forced_variation(experiment_key, user_id);
181
- if forced_variation
182
- forced_variation_key = forced_variation['key']
183
- end
184
-
185
- forced_variation_key
186
- end
187
-
188
150
  def track(event_key, user_id, attributes = nil, event_tags = nil)
189
151
  # Send conversion event to Optimizely.
190
152
  #
@@ -234,10 +196,6 @@ module Optimizely
234
196
  rescue => e
235
197
  @logger.log(Logger::ERROR, "Unable to dispatch conversion event. Error: #{e}")
236
198
  end
237
- @notification_center.send_notifications(
238
- NotificationCenter::NOTIFICATION_TYPES[:TRACK],
239
- event_key, user_id, attributes, event_tags, conversion_event
240
- )
241
199
  end
242
200
 
243
201
  def is_feature_enabled(feature_flag_key, user_id, attributes = nil)
@@ -519,11 +477,6 @@ module Optimizely
519
477
  rescue => e
520
478
  @logger.log(Logger::ERROR, "Unable to dispatch impression event. Error: #{e}")
521
479
  end
522
- variation = @config.get_variation_from_id(experiment_key, variation_id)
523
- @notification_center.send_notifications(
524
- NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE],
525
- experiment,user_id, attributes, variation, impression_event
526
- )
527
480
  end
528
481
  end
529
482
  end
@@ -20,7 +20,7 @@ module Optimizely
20
20
  class Bucketer
21
21
  # Optimizely bucketing algorithm that evenly distributes visitors.
22
22
 
23
- BUCKETING_ID_TEMPLATE = '%{bucketing_id}%{entity_id}'
23
+ BUCKETING_ID_TEMPLATE = '%{user_id}%{entity_id}'
24
24
  HASH_SEED = 1
25
25
  MAX_HASH_VALUE = 2**32
26
26
  MAX_TRAFFIC_VALUE = 10_000
@@ -35,15 +35,13 @@ module Optimizely
35
35
  @config = config
36
36
  end
37
37
 
38
- def bucket(experiment, bucketing_id, user_id)
38
+ def bucket(experiment, user_id)
39
39
  # Determines ID of variation to be shown for a given experiment key and user ID.
40
40
  #
41
41
  # experiment - Experiment for which visitor is to be bucketed.
42
- # bucketing_id - String A customer-assigned value used to generate the bucketing key
43
42
  # user_id - String ID for user.
44
43
  #
45
44
  # Returns variation in which visitor with ID user_id has been placed. Nil if no variation.
46
- return nil if experiment.nil?
47
45
 
48
46
  # check if experiment is in a group; if so, check if user is bucketed into specified experiment
49
47
  experiment_id = experiment['id']
@@ -53,7 +51,7 @@ module Optimizely
53
51
  group = @config.group_key_map.fetch(group_id)
54
52
  if Helpers::Group.random_policy?(group)
55
53
  traffic_allocations = group.fetch('trafficAllocation')
56
- bucketed_experiment_id = find_bucket(bucketing_id, user_id, group_id, traffic_allocations)
54
+ bucketed_experiment_id = find_bucket(user_id, group_id, traffic_allocations)
57
55
  # return if the user is not bucketed into any experiment
58
56
  unless bucketed_experiment_id
59
57
  @config.logger.log(Logger::INFO, "User '#{user_id}' is in no experiment.")
@@ -78,7 +76,7 @@ module Optimizely
78
76
  end
79
77
 
80
78
  traffic_allocations = experiment['trafficAllocation']
81
- variation_id = find_bucket(bucketing_id, user_id, experiment_id, traffic_allocations)
79
+ variation_id = find_bucket(user_id, experiment_id, traffic_allocations)
82
80
  if variation_id && variation_id != ''
83
81
  variation = @config.get_variation_from_id(experiment_key, variation_id)
84
82
  variation_key = variation ? variation['key'] : nil
@@ -98,18 +96,18 @@ module Optimizely
98
96
  nil
99
97
  end
100
98
 
101
- def find_bucket(bucketing_id, user_id, parent_id, traffic_allocations)
99
+ def find_bucket(user_id, parent_id, traffic_allocations)
102
100
  # Helper function to find the matching entity ID for a given bucketing value in a list of traffic allocations.
103
101
  #
104
- # bucketing_id - String A customer-assigned value user to generate bucketing key
105
102
  # user_id - String ID for user
106
103
  # parent_id - String entity ID to use for bucketing ID
107
104
  # traffic_allocations - Array of traffic allocations
108
105
  #
109
106
  # Returns entity ID corresponding to the provided bucket value or nil if no match is found.
110
- bucketing_key = sprintf(BUCKETING_ID_TEMPLATE, bucketing_id: bucketing_id, entity_id: parent_id)
111
- bucket_value = generate_bucket_value(bucketing_key)
112
- @config.logger.log(Logger::DEBUG, "Assigned bucket #{bucket_value} to user '#{user_id}' with bucketing ID: '#{bucketing_id}'.")
107
+
108
+ bucketing_id = sprintf(BUCKETING_ID_TEMPLATE, user_id: user_id, entity_id: parent_id)
109
+ bucket_value = generate_bucket_value(bucketing_id)
110
+ @config.logger.log(Logger::DEBUG, "Assigned bucket #{bucket_value} to user '#{user_id}'.")
113
111
 
114
112
  traffic_allocations.each do |traffic_allocation|
115
113
  current_end_of_range = traffic_allocation['endOfRange']
@@ -124,25 +122,25 @@ module Optimizely
124
122
 
125
123
  private
126
124
 
127
- def generate_bucket_value(bucketing_key)
125
+ def generate_bucket_value(bucketing_id)
128
126
  # Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE).
129
127
  #
130
- # bucketing_key - String - Value used to generate bucket value
128
+ # bucketing_id - String ID for bucketing.
131
129
  #
132
- # Returns bucket value corresponding to the provided bucketing key.
130
+ # Returns bucket value corresponding to the provided bucketing ID.
133
131
 
134
- ratio = (generate_unsigned_hash_code_32_bit(bucketing_key)).to_f / MAX_HASH_VALUE
132
+ ratio = (generate_unsigned_hash_code_32_bit(bucketing_id)).to_f / MAX_HASH_VALUE
135
133
  (ratio * MAX_TRAFFIC_VALUE).to_i
136
134
  end
137
135
 
138
- def generate_unsigned_hash_code_32_bit(bucketing_key)
136
+ def generate_unsigned_hash_code_32_bit(bucketing_id)
139
137
  # Helper function to retreive hash code
140
138
  #
141
- # bucketing_key - String - Value used for the key of the murmur hash
139
+ # bucketing_id - String ID for bucketing.
142
140
  #
143
141
  # Returns hash code which is a 32 bit unsigned integer.
144
142
 
145
- MurmurHash3::V32.str_hash(bucketing_key, @bucket_seed) & UNSIGNED_MAX_32_BIT_VALUE
143
+ MurmurHash3::V32.str_hash(bucketing_id, @bucket_seed) & UNSIGNED_MAX_32_BIT_VALUE
146
144
  end
147
145
  end
148
146
  end
@@ -16,31 +16,27 @@
16
16
  require_relative './bucketer'
17
17
 
18
18
  module Optimizely
19
-
20
- RESERVED_ATTRIBUTE_KEY_BUCKETING_ID = "\$opt_bucketing_id".freeze
21
-
22
19
  class DecisionService
23
20
  # Optimizely's decision service that determines into which variation of an experiment a user will be allocated.
24
21
  #
25
22
  # The decision service contains all logic relating to how a user bucketing decisions is made.
26
23
  # This includes all of the following (in order):
27
24
  #
28
- # 1. Check experiment status
29
- # 2. Check forced bucketing
30
- # 3. Check whitelisting
31
- # 4. Check user profile service for past bucketing decisions (sticky bucketing)
32
- # 5. Check audience targeting
33
- # 6. Use Murmurhash3 to bucket the user
34
-
25
+ # 1. Checking experiment status
26
+ # 2. Checking whitelisting
27
+ # 3. Checking user profile service for past bucketing decisions (sticky bucketing)
28
+ # 3. Checking audience targeting
29
+ # 4. Using Murmurhash3 to bucket the user
30
+
35
31
  attr_reader :bucketer
36
32
  attr_reader :config
37
-
33
+
38
34
  def initialize(config, user_profile_service = nil)
39
35
  @config = config
40
36
  @user_profile_service = user_profile_service
41
37
  @bucketer = Bucketer.new(@config)
42
38
  end
43
-
39
+
44
40
  def get_variation(experiment_key, user_id, attributes = nil)
45
41
  # Determines variation into which user will be bucketed.
46
42
  #
@@ -49,65 +45,52 @@ module Optimizely
49
45
  # attributes - Hash representing user attributes
50
46
  #
51
47
  # Returns variation ID where visitor will be bucketed (nil if experiment is inactive or user does not meet audience conditions)
52
-
53
- # By default, the bucketing ID should be the user ID
54
- bucketing_id = user_id;
55
-
56
- # If the bucketing ID key is defined in attributes, then use that in place of the userID
57
- if attributes and attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID].is_a? String
58
- unless attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID].empty?
59
- bucketing_id = attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID]
60
- @config.logger.log(Logger::DEBUG, "Setting the bucketing ID '#{bucketing_id}'")
61
- end
62
- end
63
-
48
+
64
49
  # Check to make sure experiment is active
65
50
  experiment = @config.get_experiment_from_key(experiment_key)
66
- return nil if experiment.nil?
67
-
51
+ if experiment.nil?
52
+ return nil
53
+ end
54
+
68
55
  experiment_id = experiment['id']
69
56
  unless @config.experiment_running?(experiment)
70
57
  @config.logger.log(Logger::INFO, "Experiment '#{experiment_key}' is not running.")
71
58
  return nil
72
59
  end
73
-
74
- # Check if a forced variation is set for the user
75
- forced_variation = @config.get_forced_variation(experiment_key, user_id)
76
- return forced_variation['id'] if forced_variation
77
-
78
- # Check if user is in a white-listed variation
79
- whitelisted_variation_id = get_whitelisted_variation_id(experiment_key, user_id)
80
- return whitelisted_variation_id if whitelisted_variation_id
81
-
60
+
61
+ # Check if user is in a forced variation
62
+ forced_variation_id = get_forced_variation_id(experiment_key, user_id)
63
+ return forced_variation_id if forced_variation_id
64
+
82
65
  # Check for saved bucketing decisions
83
66
  user_profile = get_user_profile(user_id)
84
67
  saved_variation_id = get_saved_variation_id(experiment_id, user_profile)
85
68
  if saved_variation_id
86
69
  @config.logger.log(
87
- Logger::INFO,
88
- "Returning previously activated variation ID #{saved_variation_id} of experiment '#{experiment_key}' for user '#{user_id}' from user profile."
70
+ Logger::INFO,
71
+ "Returning previously activated variation ID #{saved_variation_id} of experiment '#{experiment_key}' for user '#{user_id}' from user profile."
89
72
  )
90
73
  return saved_variation_id
91
74
  end
92
-
75
+
93
76
  # Check audience conditions
94
77
  unless Audience.user_in_experiment?(@config, experiment, attributes)
95
78
  @config.logger.log(
96
- Logger::INFO,
97
- "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'."
79
+ Logger::INFO,
80
+ "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'."
98
81
  )
99
82
  return nil
100
83
  end
101
-
84
+
102
85
  # Bucket normally
103
- variation = @bucketer.bucket(experiment, bucketing_id, user_id)
86
+ variation = @bucketer.bucket(experiment, user_id)
104
87
  variation_id = variation ? variation['id'] : nil
105
-
88
+
106
89
  # Persist bucketing decision
107
90
  save_user_profile(user_profile, experiment_id, variation_id)
108
91
  variation_id
109
92
  end
110
-
93
+
111
94
  def get_variation_for_feature(feature_flag, user_id, attributes = nil)
112
95
  # Get the variation the user is bucketed into for the given FeatureFlag.
113
96
  #
@@ -116,33 +99,35 @@ module Optimizely
116
99
  # attributes - Hash representing user attributes
117
100
  #
118
101
  # Returns hash with the experiment and variation where visitor will be bucketed (nil if the user is not bucketed into any of the experiments on the feature)
119
-
102
+
120
103
  # check if the feature is being experiment on and whether the user is bucketed into the experiment
121
104
  decision = get_variation_for_feature_experiment(feature_flag, user_id, attributes)
122
- return decision unless decision.nil?
123
-
105
+ unless decision.nil?
106
+ return decision
107
+ end
108
+
124
109
  feature_flag_key = feature_flag['key']
125
110
  variation = get_variation_for_feature_rollout(feature_flag, user_id, attributes)
126
111
  if variation
127
112
  @config.logger.log(
128
- Logger::INFO,
129
- "User '#{user_id}' is in the rollout for feature flag '#{feature_flag_key}'."
113
+ Logger::INFO,
114
+ "User '#{user_id}' is in the rollout for feature flag '#{feature_flag_key}'."
130
115
  )
131
116
  # return decision with nil experiment so we don't track impressions for it
132
117
  return {
133
- 'experiment' => nil,
134
- 'variation' => variation
118
+ 'experiment' => nil,
119
+ 'variation' => variation
135
120
  }
136
121
  else
137
122
  @config.logger.log(
138
- Logger::INFO,
139
- "User '#{user_id}' is not in the rollout for feature flag '#{feature_flag_key}'."
123
+ Logger::INFO,
124
+ "User '#{user_id}' is not in the rollout for feature flag '#{feature_flag_key}'."
140
125
  )
141
126
  end
142
-
127
+
143
128
  return nil
144
129
  end
145
-
130
+
146
131
  def get_variation_for_feature_experiment(feature_flag, user_id, attributes = nil)
147
132
  # Gets the variation the user is bucketed into for the feature flag's experiment.
148
133
  #
@@ -152,7 +137,7 @@ module Optimizely
152
137
  #
153
138
  # Returns a hash with the experiment and variation where visitor will be bucketed
154
139
  # or nil if the user is not bucketed into any of the experiments on the feature
155
-
140
+
156
141
  feature_flag_key = feature_flag['key']
157
142
  unless feature_flag['experimentIds'].empty?
158
143
  # check if experiment is part of mutex group
@@ -160,12 +145,12 @@ module Optimizely
160
145
  experiment = @config.experiment_id_map[experiment_id]
161
146
  unless experiment
162
147
  @config.logger.log(
163
- Logger::DEBUG,
164
- "Feature flag experiment with ID '#{experiment_id}' is not in the datafile."
148
+ Logger::DEBUG,
149
+ "Feature flag experiment with ID '#{experiment_id}' is not in the datafile."
165
150
  )
166
151
  return nil
167
152
  end
168
-
153
+
169
154
  group_id = experiment['groupId']
170
155
  # if experiment is part of mutex group we first determine which experiment (if any) in the group the user is part of
171
156
  if group_id and @config.group_key_map.has_key?(group_id)
@@ -173,15 +158,15 @@ module Optimizely
173
158
  bucketed_experiment_id = @bucketer.find_bucket(user_id, group_id, group['trafficAllocation'])
174
159
  if bucketed_experiment_id.nil?
175
160
  @config.logger.log(
176
- Logger::INFO,
177
- "The user '#{user_id}' is not bucketed into any of the experiments on the feature '#{feature_flag_key}'."
161
+ Logger::INFO,
162
+ "The user '#{user_id}' is not bucketed into any of the experiments on the feature '#{feature_flag_key}'."
178
163
  )
179
164
  return nil
180
165
  end
181
166
  else
182
167
  bucketed_experiment_id = experiment_id
183
168
  end
184
-
169
+
185
170
  if feature_flag['experimentIds'].include?(bucketed_experiment_id)
186
171
  experiment = @config.experiment_id_map[bucketed_experiment_id]
187
172
  experiment_key = experiment['key']
@@ -189,30 +174,30 @@ module Optimizely
189
174
  unless variation_id.nil?
190
175
  variation = @config.variation_id_map[experiment_key][variation_id]
191
176
  @config.logger.log(
192
- Logger::INFO,
193
- "The user '#{user_id}' is bucketed into experiment '#{experiment_key}' of feature '#{feature_flag_key}'."
177
+ Logger::INFO,
178
+ "The user '#{user_id}' is bucketed into experiment '#{experiment_key}' of feature '#{feature_flag_key}'."
194
179
  )
195
180
  return {
196
- 'variation' => variation,
197
- 'experiment' => experiment
181
+ 'variation' => variation,
182
+ 'experiment' => experiment
198
183
  }
199
184
  else
200
185
  @config.logger.log(
201
- Logger::INFO,
202
- "The user '#{user_id}' is not bucketed into any of the experiments on the feature '#{feature_flag_key}'."
186
+ Logger::INFO,
187
+ "The user '#{user_id}' is not bucketed into any of the experiments on the feature '#{feature_flag_key}'."
203
188
  )
204
189
  end
205
190
  end
206
191
  else
207
192
  @config.logger.log(
208
- Logger::DEBUG,
209
- "The feature flag '#{feature_flag_key}' is not used in any experiments."
193
+ Logger::DEBUG,
194
+ "The feature flag '#{feature_flag_key}' is not used in any experiments."
210
195
  )
211
196
  end
212
-
197
+
213
198
  return nil
214
199
  end
215
-
200
+
216
201
  def get_variation_for_feature_rollout(feature_flag, user_id, attributes = nil)
217
202
  # Determine which variation the user is in for a given rollout.
218
203
  # Returns the variation of the first experiment the user qualifies for.
@@ -222,110 +207,110 @@ module Optimizely
222
207
  # attributes - Hash representing user attributes
223
208
  #
224
209
  # Returns the variation the user is bucketed into or nil if not bucketed into any of the targeting rules
225
-
210
+
226
211
  rollout_id = feature_flag['rolloutId']
212
+ feature_flag_key = feature_flag['key']
227
213
  if rollout_id.nil? or rollout_id.empty?
228
- feature_flag_key = feature_flag['key']
229
214
  @config.logger.log(
230
- Logger::DEBUG,
231
- "Feature flag '#{feature_flag_key}' is not part of a rollout."
215
+ Logger::DEBUG,
216
+ "Feature flag '#{feature_flag_key}' is not part of a rollout."
232
217
  )
233
218
  return nil
234
219
  end
235
-
220
+
236
221
  rollout = @config.get_rollout_from_id(rollout_id)
237
222
  unless rollout.nil? or rollout['experiments'].empty?
238
223
  rollout_experiments = rollout['experiments']
239
224
  number_of_rules = rollout_experiments.length - 1
240
-
225
+
241
226
  # Go through each experiment in order and try to get the variation for the user
242
227
  for index in (0...number_of_rules)
243
228
  experiment = rollout_experiments[index]
244
229
  experiment_key = experiment['key']
245
-
230
+
246
231
  # Check that user meets audience conditions for targeting rule
247
232
  unless Audience.user_in_experiment?(@config, experiment, attributes)
248
233
  @config.logger.log(
249
- Logger::DEBUG,
250
- "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'."
234
+ Logger::DEBUG,
235
+ "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}' of rollout with feature flag '#{feature_flag_key}'."
251
236
  )
252
237
  # move onto the next targeting rule
253
238
  next
254
239
  end
255
-
240
+
256
241
  @config.logger.log(
257
- Logger::DEBUG,
258
- "User '#{user_id}' meets conditions for targeting rule '#{index + 1}'."
242
+ Logger::DEBUG,
243
+ "User '#{user_id}' meets conditions for targeting rule '#{index + 1}'."
259
244
  )
260
245
  variation = @bucketer.bucket(experiment, user_id)
261
246
  unless variation.nil?
262
247
  variation_key = variation['key']
263
248
  return variation
264
249
  end
265
-
250
+
266
251
  # User failed traffic allocation, jump to Everyone Else rule
267
252
  @config.logger.log(
268
- Logger::DEBUG,
269
- "User '#{user_id}' is not in the traffic group for the targeting rule. Checking 'Eveyrone Else' rule now."
253
+ Logger::DEBUG,
254
+ "User '#{user_id}' is not in the traffic group for the targeting rule. Checking 'Eveyrone Else' rule now."
270
255
  )
271
256
  break
272
257
  end
273
-
258
+
274
259
  # Evalute the "Everyone Else" rule, which is the last rule.
275
260
  everyone_else_experiment = rollout_experiments[number_of_rules]
276
261
  variation = @bucketer.bucket(everyone_else_experiment, user_id)
277
262
  unless variation.nil?
278
263
  @config.logger.log(
279
- Logger::DEBUG,
280
- "User '#{user_id}' meets conditions for targeting rule 'Everyone Else'."
264
+ Logger::DEBUG,
265
+ "User '#{user_id}' meets conditions for targeting rule 'Everyone Else' of rollout with feature flag '#{feature_flag_key}'."
281
266
  )
282
267
  return variation
283
268
  end
284
-
269
+
285
270
  @config.logger.log(
286
- Logger::DEBUG,
287
- "User '#{user_id}' does not meet conditions for targeting rule 'Everyone Else'."
271
+ Logger::DEBUG,
272
+ "User '#{user_id}' does not meet conditions for targeting rule 'Everyone Else' of rollout with feature flag '#{feature_flag_key}'."
288
273
  )
289
274
  end
290
-
275
+
291
276
  return nil
292
277
  end
293
-
278
+
294
279
  private
295
-
296
- def get_whitelisted_variation_id(experiment_key, user_id)
297
- # Determine if a user is whitelisted into a variation for the given experiment and return the ID of that variation
280
+
281
+ def get_forced_variation_id(experiment_key, user_id)
282
+ # Determine if a user is forced into a variation for the given experiment and return the ID of that variation
298
283
  #
299
284
  # experiment_key - Key representing the experiment for which user is to be bucketed
300
285
  # user_id - ID for the user
301
286
  #
302
- # Returns variation ID into which user_id is whitelisted (nil if no variation)
303
-
304
- whitelisted_variations = @config.get_whitelisted_variations(experiment_key)
305
-
306
- return nil unless whitelisted_variations
307
-
308
- whitelisted_variation_key = whitelisted_variations[user_id]
309
-
310
- return nil unless whitelisted_variation_key
311
-
312
- whitelisted_variation_id = @config.get_variation_id_from_key(experiment_key, whitelisted_variation_key)
313
-
314
- unless whitelisted_variation_id
287
+ # Returns variation ID into which user_id is forced (nil if no variation)
288
+
289
+ forced_variations = @config.get_forced_variations(experiment_key)
290
+
291
+ return nil unless forced_variations
292
+
293
+ forced_variation_key = forced_variations[user_id]
294
+
295
+ return nil unless forced_variation_key
296
+
297
+ forced_variation_id = @config.get_variation_id_from_key(experiment_key, forced_variation_key)
298
+
299
+ unless forced_variation_id
315
300
  @config.logger.log(
316
- Logger::INFO,
317
- "User '#{user_id}' is whitelisted into variation '#{whitelisted_variation_key}', which is not in the datafile."
301
+ Logger::INFO,
302
+ "User '#{user_id}' is whitelisted into variation '#{forced_variation_key}', which is not in the datafile."
318
303
  )
319
304
  return nil
320
305
  end
321
-
306
+
322
307
  @config.logger.log(
323
- Logger::INFO,
324
- "User '#{user_id}' is whitelisted into variation '#{whitelisted_variation_key}' of experiment '#{experiment_key}'."
308
+ Logger::INFO,
309
+ "User '#{user_id}' is whitelisted into variation '#{forced_variation_key}' of experiment '#{experiment_key}'."
325
310
  )
326
- whitelisted_variation_id
311
+ forced_variation_id
327
312
  end
328
-
313
+
329
314
  def get_saved_variation_id(experiment_id, user_profile)
330
315
  # Retrieve variation ID of stored bucketing decision for a given experiment from a given user profile
331
316
  #
@@ -334,56 +319,56 @@ module Optimizely
334
319
  #
335
320
  # Returns string variation ID (nil if no decision is found)
336
321
  return nil unless user_profile[:experiment_bucket_map]
337
-
322
+
338
323
  decision = user_profile[:experiment_bucket_map][experiment_id]
339
324
  return nil unless decision
340
325
  variation_id = decision[:variation_id]
341
326
  return variation_id if @config.variation_id_exists?(experiment_id, variation_id)
342
-
327
+
343
328
  @config.logger.log(
344
- Logger::INFO,
345
- "User '#{user_profile['user_id']}' was previously bucketed into variation ID '#{variation_id}' for experiment '#{experiment_id}', but no matching variation was found. Re-bucketing user."
329
+ Logger::INFO,
330
+ "User '#{user_profile['user_id']}' was previously bucketed into variation ID '#{variation_id}' for experiment '#{experiment_id}', but no matching variation was found. Re-bucketing user."
346
331
  )
347
332
  nil
348
333
  end
349
-
334
+
350
335
  def get_user_profile(user_id)
351
336
  # Determine if a user is forced into a variation for the given experiment and return the ID of that variation
352
337
  #
353
338
  # user_id - String ID for the user
354
339
  #
355
340
  # Returns Hash stored user profile (or a default one if lookup fails or user profile service not provided)
356
-
341
+
357
342
  user_profile = {
358
- :user_id => user_id,
359
- :experiment_bucket_map => {}
343
+ :user_id => user_id,
344
+ :experiment_bucket_map => {}
360
345
  }
361
-
346
+
362
347
  return user_profile unless @user_profile_service
363
-
348
+
364
349
  begin
365
350
  user_profile = @user_profile_service.lookup(user_id) || user_profile
366
351
  rescue => e
367
352
  @config.logger.log(Logger::ERROR, "Error while looking up user profile for user ID '#{user_id}': #{e}.")
368
353
  end
369
-
354
+
370
355
  user_profile
371
356
  end
372
-
373
-
357
+
358
+
374
359
  def save_user_profile(user_profile, experiment_id, variation_id)
375
360
  # Save a given bucketing decision to a given user profile
376
361
  #
377
362
  # user_profile - Hash user profile
378
363
  # experiment_id - String experiment ID
379
364
  # variation_id - String variation ID
380
-
365
+
381
366
  return unless @user_profile_service
382
-
367
+
383
368
  user_id = user_profile[:user_id]
384
369
  begin
385
370
  user_profile[:experiment_bucket_map][experiment_id] = {
386
- :variation_id => variation_id
371
+ :variation_id => variation_id
387
372
  }
388
373
  @user_profile_service.save(user_profile)
389
374
  @config.logger.log(Logger::INFO, "Saved variation ID #{variation_id} of experiment ID #{experiment_id} for user '#{user_id}'.")
@@ -392,4 +377,4 @@ module Optimizely
392
377
  end
393
378
  end
394
379
  end
395
- end
380
+ end