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