11#
2- # Copyright 2016, Optimizely and contributors
2+ # Copyright 2016-2017 , Optimizely and contributors
33#
44# Licensed under the Apache License, Version 2.0 (the "License");
55# you may not use this file except in compliance with the License.
@@ -37,11 +37,6 @@ class Project
3737 attr_accessor :logger
3838 attr_accessor :error_handler
3939
40- EVENT_BUILDERS_BY_VERSION = {
41- Optimizely ::V1_CONFIG_VERSION => EventBuilderV1 ,
42- Optimizely ::V2_CONFIG_VERSION => EventBuilderV2
43- }
44-
4540 def initialize ( datafile , event_dispatcher = nil , logger = nil , error_handler = nil , skip_json_validation = false )
4641 # Constructor for Projects.
4742 #
@@ -58,7 +53,7 @@ def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = n
5853 @event_dispatcher = event_dispatcher || EventDispatcher . new
5954
6055 begin
61- validate_inputs ( datafile , skip_json_validation )
56+ validate_instantiation_options ( datafile , skip_json_validation )
6257 rescue InvalidInputError => e
6358 @is_valid = false
6459 logger = SimpleLogger . new
@@ -75,14 +70,15 @@ def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = n
7570 return
7671 end
7772
78- begin
79- @bucketer = Bucketer . new ( @config )
80- @event_builder = EVENT_BUILDERS_BY_VERSION [ @config . version ] . new ( @config , @bucketer )
81- rescue
73+ unless @config . parsing_succeeded?
8274 @is_valid = false
8375 logger = SimpleLogger . new
84- logger . log ( Logger ::ERROR , InvalidDatafileVersionError . new )
76+ logger . log ( Logger ::ERROR , InvalidDatafileVersionError . new . message )
77+ return
8578 end
79+
80+ @bucketer = Bucketer . new ( @config )
81+ @event_builder = EventBuilderV2 . new ( @config )
8682 end
8783
8884 def activate ( experiment_key , user_id , attributes = nil )
@@ -101,24 +97,15 @@ def activate(experiment_key, user_id, attributes = nil)
10197 return nil
10298 end
10399
104- if attributes && !attributes_valid? ( attributes )
105- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
106- return nil
107- end
108-
109- unless preconditions_valid? ( experiment_key , user_id , attributes )
110- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
111- return nil
112- end
113-
114- variation_id = @bucketer . bucket ( experiment_key , user_id )
100+ variation_key = get_variation ( experiment_key , user_id , attributes )
115101
116- if not variation_id
102+ if variation_key . nil?
117103 @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
118104 return nil
119105 end
120106
121107 # Create and dispatch impression event
108+ variation_id = @config . get_variation_id_from_key ( experiment_key , variation_key )
122109 impression_event = @event_builder . create_impression_event ( experiment_key , variation_id , user_id , attributes )
123110 @logger . log ( Logger ::INFO ,
124111 'Dispatching impression event to URL %s with params %s.' % [ impression_event . url ,
@@ -129,7 +116,7 @@ def activate(experiment_key, user_id, attributes = nil)
129116 @logger . log ( Logger ::ERROR , "Unable to dispatch impression event. Error: #{ e } " )
130117 end
131118
132- @config . get_variation_key_from_id ( experiment_key , variation_id )
119+ variation_key
133120 end
134121
135122 def get_variation ( experiment_key , user_id , attributes = nil )
@@ -148,24 +135,34 @@ def get_variation(experiment_key, user_id, attributes = nil)
148135 return nil
149136 end
150137
151- if attributes && ! attributes_valid? ( attributes )
138+ unless preconditions_valid? ( experiment_key , attributes )
152139 @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } ." )
153140 return nil
154141 end
155142
156- unless preconditions_valid? ( experiment_key , user_id , attributes )
157- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } ." )
143+ variation_id = @bucketer . get_forced_variation_id ( experiment_key , user_id )
144+
145+ unless variation_id . nil?
146+ return @config . get_variation_key_from_id ( experiment_key , variation_id )
147+ end
148+
149+ unless Audience . user_in_experiment? ( @config , experiment_key , attributes )
150+ @logger . log ( Logger ::INFO ,
151+ "User '#{ user_id } ' does not meet the conditions to be in experiment '#{ experiment_key } '." )
158152 return nil
159153 end
160154
161155 variation_id = @bucketer . bucket ( experiment_key , user_id )
162- @config . get_variation_key_from_id ( experiment_key , variation_id )
156+ unless variation_id . nil?
157+ return @config . get_variation_key_from_id ( experiment_key , variation_id )
158+ end
159+ nil
163160 end
164161
165162 def track ( event_key , user_id , attributes = nil , event_tags = nil )
166163 # Send conversion event to Optimizely.
167164 #
168- # event_key - Goal key representing the event which needs to be recorded.
165+ # event_key - Event key representing the event which needs to be recorded.
169166 # user_id - String ID for user.
170167 # attributes - Hash representing visitor attributes and values which need to be recorded.
171168 # event_tags - Hash representing metadata associated with the event.
@@ -183,34 +180,26 @@ def track(event_key, user_id, attributes = nil, event_tags = nil)
183180 @logger . log ( Logger ::WARN , 'Event value is deprecated in track call. Use event tags to pass in revenue value instead.' )
184181 end
185182
186- return nil if attributes && !attributes_valid? ( attributes )
187- return nil if event_tags && !event_tags_valid? ( event_tags )
183+ return nil unless user_inputs_valid? ( attributes , event_tags )
188184
189- experiment_ids = @config . get_experiment_ids_for_goal ( event_key )
185+ experiment_ids = @config . get_experiment_ids_for_event ( event_key )
190186 if experiment_ids . empty?
191187 @config . logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } '." )
192188 return nil
193189 end
194190
195191 # Filter out experiments that are not running or that do not include the user in audience conditions
196- valid_experiment_keys = [ ]
197- experiment_ids . each do |experiment_id |
198- experiment_key = @config . experiment_id_map [ experiment_id ] [ 'key' ]
199- unless preconditions_valid? ( experiment_key , user_id , attributes )
200- @config . logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } ' for experiment '#{ experiment_key } '." )
201- next
202- end
203- valid_experiment_keys . push ( experiment_key )
204- end
192+
193+ experiment_variation_map = get_valid_experiments_for_event ( event_key , user_id , attributes )
205194
206195 # Don't track events without valid experiments attached
207- if valid_experiment_keys . empty?
196+ if experiment_variation_map . empty?
208197 @logger . log ( Logger ::INFO , "There are no valid experiments for event '#{ event_key } ' to track." )
209198 return nil
210199 end
211200
212201 conversion_event = @event_builder . create_conversion_event ( event_key , user_id , attributes ,
213- event_tags , valid_experiment_keys )
202+ event_tags , experiment_variation_map )
214203 @logger . log ( Logger ::INFO ,
215204 'Dispatching conversion event to URL %s with params %s.' % [ conversion_event . url ,
216205 conversion_event . params ] )
@@ -223,7 +212,35 @@ def track(event_key, user_id, attributes = nil, event_tags = nil)
223212
224213 private
225214
226- def preconditions_valid? ( experiment_key , user_id , attributes )
215+ def get_valid_experiments_for_event ( event_key , user_id , attributes )
216+ # Get the experiments that we should be tracking for the given event.
217+ #
218+ # event_key - Event key representing the event which needs to be recorded.
219+ # user_id - String ID for user.
220+ # attributes - Map of attributes of the user.
221+ #
222+ # Returns Map where each object contains the ID of the experiment to track and the ID of the variation the user
223+ # is bucketed into.
224+
225+ valid_experiments = { }
226+ experiment_ids = @config . get_experiment_ids_for_event ( event_key )
227+ experiment_ids . each do |experiment_id |
228+ experiment_key = @config . get_experiment_key ( experiment_id )
229+ variation_key = get_variation ( experiment_key , user_id , attributes )
230+
231+ if variation_key . nil?
232+ @logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } ' for experiment '#{ experiment_key } '." )
233+ next
234+ end
235+
236+ variation_id = @config . get_variation_id_from_key ( experiment_key , variation_key )
237+ valid_experiments [ experiment_id ] = variation_id
238+ end
239+
240+ valid_experiments
241+ end
242+
243+ def preconditions_valid? ( experiment_key , attributes = nil , event_tags = nil )
227244 # Validates preconditions for bucketing a user.
228245 #
229246 # experiment_key - String key for an experiment.
@@ -232,18 +249,29 @@ def preconditions_valid?(experiment_key, user_id, attributes)
232249 #
233250 # Returns boolean representing whether all preconditions are valid.
234251
252+ return false unless user_inputs_valid? ( attributes , event_tags )
253+
235254 unless @config . experiment_running? ( experiment_key )
236255 @logger . log ( Logger ::INFO , "Experiment '#{ experiment_key } ' is not running." )
237256 return false
238257 end
239258
240- if @config . user_in_forced_variation? ( experiment_key , user_id )
241- return true
259+ true
260+ end
261+
262+ def user_inputs_valid? ( attributes = nil , event_tags = nil )
263+ # Helper method to validate user inputs.
264+ #
265+ # attributes - Dict representing user attributes.
266+ # event_tags - Dict representing metadata associated with an event.
267+ #
268+ # Returns boolean True if inputs are valid. False otherwise.
269+
270+ if !attributes . nil? && !attributes_valid? ( attributes )
271+ return false
242272 end
243273
244- unless Audience . user_in_experiment? ( @config , experiment_key , attributes )
245- @logger . log ( Logger ::INFO ,
246- "User '#{ user_id } ' does not meet the conditions to be in experiment '#{ experiment_key } '." )
274+ if !event_tags . nil? && !event_tags_valid? ( event_tags )
247275 return false
248276 end
249277
@@ -268,7 +296,7 @@ def event_tags_valid?(event_tags)
268296 true
269297 end
270298
271- def validate_inputs ( datafile , skip_json_validation )
299+ def validate_instantiation_options ( datafile , skip_json_validation )
272300 unless skip_json_validation
273301 raise InvalidInputError . new ( 'datafile' ) unless Helpers ::Validator . datafile_valid? ( datafile )
274302 end
0 commit comments