lib/split/trial.rb in split-1.0.0 vs lib/split/trial.rb in split-1.1.0

- old
+ new

@@ -1,49 +1,111 @@ module Split class Trial attr_accessor :experiment - attr_accessor :goals def initialize(attrs = {}) - self.experiment = attrs[:experiment] if !attrs[:experiment].nil? - self.alternative = attrs[:alternative] if !attrs[:alternative].nil? - self.goals = attrs[:goals].nil? ? [] : attrs[:goals] + self.experiment = attrs.delete(:experiment) + self.alternative = attrs.delete(:alternative) + + @user = attrs.delete(:user) + @options = attrs + + @alternative_choosen = false end def alternative - @alternative ||= if experiment.has_winner? - experiment.winner + @alternative ||= if @experiment.has_winner? + @experiment.winner end end - def complete! + def alternative=(alternative) + @alternative = if alternative.kind_of?(Split::Alternative) + alternative + else + @experiment.alternatives.find{|a| a.name == alternative } + end + end + + def complete!(goals, context = nil) + goals = goals || [] + if alternative - if self.goals.empty? + if goals.empty? alternative.increment_completion else - self.goals.each {|g| alternative.increment_completion(g)} + goals.each {|g| alternative.increment_completion(g) } end + + context.send(Split.configuration.on_trial_complete, self) \ + if Split.configuration.on_trial_complete && context end end - def choose! - choose - record! - end + # Choose an alternative, add a participant, and save the alternative choice on the user. This + # method is guaranteed to only run once, and will skip the alternative choosing process if run + # a second time. + def choose!(context = nil) + # Only run the process once + return alternative if @alternative_choosen - def record! - alternative.increment_participation - end + if @options[:override] + self.alternative = @options[:override] + elsif @options[:disabled] || !Split.configuration.enabled + self.alternative = @experiment.control + elsif @experiment.has_winner? + self.alternative = @experiment.winner + else + cleanup_old_versions - def choose - self.alternative = experiment.next_alternative + if exclude_user? + self.alternative = @experiment.control + elsif @user[@experiment.key] + self.alternative = @user[@experiment.key] + else + self.alternative = @experiment.next_alternative + + # Increment the number of participants since we are actually choosing a new alternative + self.alternative.increment_participation + + # Run the post-choosing hook on the context + context.send(Split.configuration.on_trial_choose, self) \ + if Split.configuration.on_trial_choose && context + end + end + + @user[@experiment.key] = alternative.name if should_store_alternative? + @alternative_choosen = true + alternative end - def alternative=(alternative) - @alternative = if alternative.kind_of?(Split::Alternative) - alternative + private + + def should_store_alternative? + if @options[:override] || @options[:disabled] + Split.configuration.store_override else - self.experiment.alternatives.find{|a| a.name == alternative } + !exclude_user? end + end + + def cleanup_old_versions + if @experiment.version > 0 + keys = @user.keys.select { |k| k.match(Regexp.new(@experiment.name)) } + keys_without_experiment(keys).each { |key| @user.delete(key) } + end + end + + def exclude_user? + @options[:exclude] || @experiment.start_time.nil? || max_experiments_reached? + end + + def max_experiments_reached? + !Split.configuration.allow_multiple_experiments && + keys_without_experiment(@user.keys).length > 0 + end + + def keys_without_experiment(keys) + keys.reject { |k| k.match(Regexp.new("^#{@experiment.key}(:finished)?$")) } end end end