Skip to content

Commit 4b35109

Browse files
authored
Refactor experiment key map (#55)
1 parent 412eb9c commit 4b35109

12 files changed

+103
-183
lines changed

lib/optimizely.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ def activate(experiment_key, user_id, attributes = nil)
108108

109109
# Create and dispatch impression event
110110
variation_id = @config.get_variation_id_from_key(experiment_key, variation_key)
111-
impression_event = @event_builder.create_impression_event(experiment_key, variation_id, user_id, attributes)
111+
experiment = @config.get_experiment_from_key(experiment_key)
112+
impression_event = @event_builder.create_impression_event(experiment, variation_id, user_id, attributes)
112113
@logger.log(Logger::INFO,
113114
'Dispatching impression event to URL %s with params %s.' % [impression_event.url,
114115
impression_event.params])

lib/optimizely/audience.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ module Optimizely
2020
module Audience
2121
module_function
2222

23-
def user_in_experiment?(config, experiment_key, attributes)
23+
def user_in_experiment?(config, experiment, attributes)
2424
# Determine for given experiment if user satisfies the audiences for the experiment.
2525
#
2626
# config - Representation of the Optimizely project config.
27-
# experiment_key - Key representing experiment for which visitor is to be bucketed.
27+
# experiment - Experiment for which visitor is to be bucketed.
2828
# attributes - Hash representing user attributes which will be used in determining if
2929
# the audience conditions are met.
3030
#
3131
# Returns boolean representing if user satisfies audience conditions for any of the audiences or not.
3232

33-
audience_ids = config.get_audience_ids_for_experiment(experiment_key)
33+
audience_ids = experiment['audienceIds']
3434

3535
# Return true if there are no audiences
3636
return true if audience_ids.empty?

lib/optimizely/bucketer.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,18 @@ def initialize(config)
3535
@config = config
3636
end
3737

38-
def bucket(experiment_key, user_id)
38+
def bucket(experiment, user_id)
3939
# Determines ID of variation to be shown for a given experiment key and user ID.
4040
#
41-
# experiment_key - String Key representing experiment for which visitor is to be bucketed.
41+
# experiment - Experiment for which visitor is to be bucketed.
4242
# user_id - String ID for user.
4343
#
4444
# Returns String variation ID in which visitor with ID user_id has been placed. Nil if no variation.
4545

4646
# check if experiment is in a group; if so, check if user is bucketed into specified experiment
47-
experiment_id = @config.get_experiment_id(experiment_key)
48-
group_id = @config.get_experiment_group_id(experiment_key)
47+
experiment_id = experiment['id']
48+
experiment_key = experiment['key']
49+
group_id = experiment['groupId']
4950
if group_id
5051
group = @config.group_key_map.fetch(group_id)
5152
if Helpers::Group.random_policy?(group)
@@ -74,7 +75,7 @@ def bucket(experiment_key, user_id)
7475
end
7576
end
7677

77-
traffic_allocations = @config.get_traffic_allocation(experiment_key)
78+
traffic_allocations = experiment['trafficAllocation']
7879
variation_id = find_bucket(user_id, experiment_id, traffic_allocations)
7980
if variation_id && variation_id != ''
8081
variation_key = @config.get_variation_key_from_id(experiment_key, variation_id)

lib/optimizely/decision_service.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,16 @@ def get_variation(experiment_key, user_id, attributes = nil)
4747
# Returns variation ID where visitor will be bucketed (nil if experiment is inactive or user does not meet audience conditions)
4848

4949
# Check to make sure experiment is active
50-
unless @config.experiment_running?(experiment_key)
51-
@config.logger.log(Logger::INFO, "Experiment '#{experiment_key}' is not running.")
50+
experiment = @config.get_experiment_from_key(experiment_key)
51+
if experiment.nil?
5252
return nil
5353
end
5454

55-
experiment_id = @config.get_experiment_id(experiment_key)
55+
experiment_id = experiment['id']
56+
unless @config.experiment_running?(experiment)
57+
@config.logger.log(Logger::INFO, "Experiment '#{experiment_key}' is not running.")
58+
return nil
59+
end
5660

5761
# Check if user is in a forced variation
5862
forced_variation_id = get_forced_variation_id(experiment_key, user_id)
@@ -70,7 +74,7 @@ def get_variation(experiment_key, user_id, attributes = nil)
7074
end
7175

7276
# Check audience conditions
73-
unless Audience.user_in_experiment?(@config, experiment_key, attributes)
77+
unless Audience.user_in_experiment?(@config, experiment, attributes)
7478
@config.logger.log(
7579
Logger::INFO,
7680
"User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'."
@@ -79,7 +83,7 @@ def get_variation(experiment_key, user_id, attributes = nil)
7983
end
8084

8185
# Bucket normally
82-
variation_id = @bucketer.bucket(experiment_key, user_id)
86+
variation_id = @bucketer.bucket(experiment, user_id)
8387

8488
# Persist bucketing decision
8589
save_user_profile(user_profile, experiment_id, variation_id)

lib/optimizely/event_builder.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ class EventBuilder < BaseEventBuilder
7171
IMPRESSION_EVENT_ENDPOINT = 'https://logx.optimizely.com/log/decision'
7272
POST_HEADERS = { 'Content-Type' => 'application/json' }
7373

74-
def create_impression_event(experiment_key, variation_id, user_id, attributes)
74+
def create_impression_event(experiment, variation_id, user_id, attributes)
7575
# Create conversion Event to be sent to the logging endpoint.
7676
#
77-
# experiment_key - Experiment for which impression needs to be recorded.
77+
# experiment - Experiment for which impression needs to be recorded.
7878
# variation_id - ID for variation which would be presented to user.
7979
# user_id - ID for user.
8080
# attributes - Hash representing user attributes and values which need to be recorded.
@@ -83,7 +83,7 @@ def create_impression_event(experiment_key, variation_id, user_id, attributes)
8383

8484
@params = {}
8585
add_common_params(user_id, attributes)
86-
add_decision(experiment_key, variation_id)
86+
add_decision(experiment, variation_id)
8787
add_attributes(attributes)
8888
Event.new(:post, IMPRESSION_EVENT_ENDPOINT, @params, POST_HEADERS)
8989
end
@@ -151,8 +151,9 @@ def add_attributes(attributes)
151151
end
152152
end
153153

154-
def add_decision(experiment_key, variation_id)
155-
experiment_id = @config.get_experiment_id(experiment_key)
154+
def add_decision(experiment, variation_id)
155+
experiment_key = experiment['key']
156+
experiment_id = experiment['id']
156157
@params['layerId'] = @config.experiment_key_map[experiment_key]['layerId']
157158
@params['decision'] = {
158159
'variationId' => variation_id,

lib/optimizely/project_config.rb

Lines changed: 6 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -122,28 +122,24 @@ def initialize(datafile, logger, error_handler)
122122
@parsing_succeeded = true
123123
end
124124

125-
def experiment_running?(experiment_key)
125+
def experiment_running?(experiment)
126126
# Determine if experiment corresponding to given key is running
127127
#
128-
# experiment_key - String key representing the experiment
128+
# experiment - Experiment
129129
#
130130
# Returns true if experiment is running
131-
experiment = @experiment_key_map[experiment_key]
132-
return RUNNING_EXPERIMENT_STATUS.include?(experiment['status']) if experiment
133-
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
134-
@error_handler.handle_error InvalidExperimentError
135-
nil
131+
return RUNNING_EXPERIMENT_STATUS.include?(experiment['status'])
136132
end
137133

138-
def get_experiment_id(experiment_key)
134+
def get_experiment_from_key(experiment_key)
139135
# Retrieves experiment ID for a given key
140136
#
141137
# experiment_key - String key representing the experiment
142138
#
143-
# Returns String ID
139+
# Returns Experiment
144140

145141
experiment = @experiment_key_map[experiment_key]
146-
return experiment['id'] if experiment
142+
return experiment if experiment
147143
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
148144
@error_handler.handle_error InvalidExperimentError
149145
nil
@@ -177,34 +173,6 @@ def get_experiment_ids_for_event(event_key)
177173
[]
178174
end
179175

180-
def get_traffic_allocation(experiment_key)
181-
# Retrieves traffic allocation for a given experiment Key
182-
#
183-
# experiment_key - String Key representing the experiment
184-
#
185-
# Returns traffic allocation for the experiment or nil
186-
187-
experiment = @experiment_key_map[experiment_key]
188-
return experiment['trafficAllocation'] if experiment
189-
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
190-
@error_handler.handle_error InvalidExperimentError
191-
nil
192-
end
193-
194-
def get_audience_ids_for_experiment(experiment_key)
195-
# Get audience IDs for the experiment
196-
#
197-
# experiment_key - Experiment key for which audience IDs are to be determined
198-
#
199-
# Returns audience IDs corresponding to the experiment.
200-
201-
experiment = @experiment_key_map[experiment_key]
202-
return experiment['audienceIds'] if experiment
203-
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
204-
@error_handler.handle_error InvalidExperimentError
205-
nil
206-
end
207-
208176
def get_audience_conditions_from_id(audience_id)
209177
# Get audience conditions for the provided audience ID
210178
#
@@ -276,13 +244,6 @@ def get_forced_variations(experiment_key)
276244
@error_handler.handle_error InvalidExperimentError
277245
end
278246

279-
def get_experiment_group_id(experiment_key)
280-
experiment = @experiment_key_map[experiment_key]
281-
return experiment['groupId'] if experiment
282-
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
283-
@error_handler.handle_error InvalidExperimentError
284-
end
285-
286247
def get_attribute_id(attribute_key)
287248
attribute = @attribute_key_map[attribute_key]
288249
return attribute['id'] if attribute
@@ -291,19 +252,6 @@ def get_attribute_id(attribute_key)
291252
nil
292253
end
293254

294-
def user_in_forced_variation?(experiment_key, user_id)
295-
# Determines if a given user is in a forced variation
296-
#
297-
# experiment_key - String experiment key
298-
# user_id - String user ID
299-
#
300-
# Returns true if user is in a forced variation
301-
302-
forced_variations = get_forced_variations(experiment_key)
303-
return forced_variations.include?(user_id) if forced_variations
304-
false
305-
end
306-
307255
def parsing_succeeded?
308256
# Helper method to determine if parsing the datafile was successful.
309257
#
@@ -330,8 +278,6 @@ def variation_id_exists?(experiment_id, variation_id)
330278
return false
331279
end
332280

333-
@logger.log Logger::ERROR, "Experiment ID '#{experiment_id}' is not in datafile."
334-
@error_handler.handle_error InvalidExperimentError
335281
false
336282
end
337283

spec/audience_spec.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,26 @@
2626
end
2727

2828
it 'should return true for user_in_experiment? if there are no audiences and no attributes' do
29-
expect(@project_instance.config).to receive(:get_audience_ids_for_experiment)
30-
.with('test_experiment').and_return([])
29+
experiment = @project_instance.config.experiment_key_map['test_experiment']
3130
expect(Optimizely::Audience.user_in_experiment?(@project_instance.config,
32-
'test_experiment',
31+
experiment,
3332
nil)).to be true
3433
end
3534

3635
it 'should return true for user_in_experiment? if there are no audiences and there are attributes' do
37-
expect(@project_instance.config).to receive(:get_audience_ids_for_experiment)
38-
.with('test_experiment').and_return([])
36+
experiment = @project_instance.config.experiment_key_map['test_experiment']
3937
user_attributes = {
4038
'browser_type' => 'firefox'
4139
}
4240
expect(Optimizely::Audience.user_in_experiment?(@project_instance.config,
43-
'test_experiment',
41+
experiment,
4442
user_attributes)).to be true
4543
end
4644

4745
it 'should return false for user_in_experiment? if there are audiences but no attributes' do
46+
experiment = @project_instance.config.experiment_key_map['test_experiment_with_audience']
4847
expect(Optimizely::Audience.user_in_experiment?(@project_instance.config,
49-
'test_experiment_with_audience',
48+
experiment,
5049
nil)).to be false
5150
end
5251

@@ -55,18 +54,19 @@
5554
'browser_type' => 'firefox'
5655
}
5756

57+
experiment = @project_instance.config.experiment_key_map['test_experiment_with_audience']
5858
expect(Optimizely::Audience.user_in_experiment?(@project_instance.config,
59-
'test_experiment_with_audience',
59+
experiment,
6060
user_attributes)).to be true
6161
end
6262

6363
it 'should return false for user_in_experiment? if the audience conditions are not met' do
6464
user_attributes = {
6565
'browser_type' => 'chrome'
6666
}
67-
67+
experiment = @project_instance.config.experiment_key_map['test_experiment_with_audience']
6868
expect(Optimizely::Audience.user_in_experiment?(@project_instance.config,
69-
'test_experiment_with_audience',
69+
experiment,
7070
user_attributes)).to be false
7171
end
7272
end

0 commit comments

Comments
 (0)