lib/vanity/experiment/ab_test.rb in vanity-1.3.0 vs lib/vanity/experiment/ab_test.rb in vanity-1.4.0.beta
- old
+ new
@@ -91,11 +91,10 @@
end
def initialize(*args)
super
- @alternatives = [false, true]
end
# -- Metric --
@@ -128,26 +127,22 @@
#
# @example Find out which alternatives this test uses
# alts = experiment(:background_color).alternatives
# puts "#{alts.count} alternatives, with the colors: #{alts.map(&:value).join(", ")}"
def alternatives(*args)
- unless args.empty?
- @alternatives = args.clone
- end
+ @alternatives = args.empty? ? [true, false] : args.clone
class << self
define_method :alternatives, instance_method(:_alternatives)
end
- alternatives
+ nil
end
def _alternatives
alts = []
@alternatives.each_with_index do |value, i|
- participants = redis.scard(key("alts:#{i}:participants")).to_i
- converted = redis.scard(key("alts:#{i}:converted")).to_i
- conversions = redis[key("alts:#{i}:conversions")].to_i
- alts << Alternative.new(self, i, value, participants, converted, conversions)
+ counts = @playground.collecting? ? connection.ab_counts(@id, i) : Hash.new(0)
+ alts << Alternative.new(self, i, value, counts[:participants], counts[:converted], counts[:conversions])
end
alts
end
private :_alternatives
@@ -184,20 +179,26 @@
# between different identities.
#
# @example
# color = experiment(:which_blue).choose
def choose
- if active?
- identity = identity()
- index = redis[key("participant:#{identity}:show")]
- unless index
- index = alternative_for(identity)
- redis.sadd key("alts:#{index}:participants"), identity
- check_completion!
+ if @playground.collecting?
+ if active?
+ identity = identity()
+ index = connection.ab_showing(@id, identity)
+ unless index
+ index = alternative_for(identity)
+ connection.ab_add_participant @id, index, identity
+ check_completion!
+ end
+ else
+ index = connection.ab_get_outcome(@id) || alternative_for(identity)
end
else
- index = redis[key("outcome")] || alternative_for(identity)
+ identity = identity()
+ @showing ||= {}
+ @showing[identity] ||= alternative_for(identity)
end
@alternatives[index.to_i]
end
# Returns fingerprint (hash) for given alternative. Can be used to lookup
@@ -225,25 +226,34 @@
# @example Use nil to clear selection
# teardown do
# experiment(:green_button).select(nil)
# end
def chooses(value)
- if value.nil?
- redis.del key("participant:#{identity}:show")
+ if @playground.collecting?
+ if value.nil?
+ connection.ab_not_showing @id, identity
+ else
+ index = @alternatives.index(value)
+ raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
+ connection.ab_show @id, identity, index
+ end
else
- index = @alternatives.index(value)
- raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
- redis[key("participant:#{identity}:show")] = index
+ @showing ||= {}
+ @showing[identity] = value.nil? ? nil : @alternatives.index(value)
end
self
end
# True if this alternative is currently showing (see #chooses).
def showing?(alternative)
identity = identity()
- index = redis[key("participant:#{identity}:show")]
- index && index.to_i == alternative.id
+ if @playground.collecting?
+ connection.ab_showing(@id, identity) == alternative.id
+ else
+ @showing ||= {}
+ @showing[identity] == alternative.id
+ end
end
# -- Reporting --
@@ -356,16 +366,17 @@
@outcome_is = block
end
# Alternative chosen when this experiment completed.
def outcome
- outcome = redis[key("outcome")]
- outcome && alternatives[outcome.to_i]
+ return unless @playground.collecting?
+ outcome = connection.ab_get_outcome(@id)
+ outcome && _alternatives[outcome]
end
def complete!
- return unless active?
+ return unless @playground.collecting? && active?
super
if @outcome_is
begin
result = @outcome_is.call
outcome = result.id if result && result.experiment == self
@@ -375,28 +386,24 @@
else
best = score.best
outcome = best.id if best
end
# TODO: logging
- redis.setnx key("outcome"), outcome || 0
+ connection.ab_set_outcome @id, outcome || 0
end
# -- Store/validate --
def destroy
- @alternatives.size.times do |i|
- redis.del key("alts:#{i}:participants")
- redis.del key("alts:#{i}:converted")
- redis.del key("alts:#{i}:conversions")
- end
- redis.del key(:outcome)
+ connection.destroy_experiment @id
super
end
def save
- fail "Experiment #{name} needs at least two alternatives" unless alternatives.size >= 2
+ true_false unless @alternatives
+ fail "Experiment #{name} needs at least two alternatives" unless @alternatives.size >= 2
super
if @metrics.nil? || @metrics.empty?
warn "Please use metrics method to explicitly state which metric you are measuring against."
metric = @playground.metrics[id] ||= Vanity::Metric.new(@playground, name)
@metrics = [metric]
@@ -409,14 +416,13 @@
# Called when tracking associated metric.
def track!(metric_id, timestamp, count, *args)
return unless active?
identity = identity() rescue nil
if identity
- return if redis[key("participants:#{identity}:show")]
+ return if connection.ab_showing(@id, identity)
index = alternative_for(identity)
- redis.sadd key("alts:#{index}:converted"), identity if redis.sismember(key("alts:#{index}:participants"), identity)
- redis.incrby key("alts:#{index}:conversions"), count
+ connection.ab_add_conversion @id, index, identity, count
check_completion!
end
end
# If you are not embarrassed by the first version of your product, you’ve
@@ -430,16 +436,15 @@
values.each do |value, (participants, conversions)|
conversions ||= participants
participants.times do |identity|
index = @alternatives.index(value)
raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
- redis.sadd key("alts:#{index}:participants"), identity
+ connection.ab_add_participant @id, index, "#{index}:#{identity}"
end
conversions.times do |identity|
index = @alternatives.index(value)
raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
- redis.sadd key("alts:#{index}:converted"), identity
- redis.incr key("alts:#{index}:conversions")
+ connection.ab_add_conversion @id, index, "#{index}:#{identity}"
end
end
end
# Chooses an alternative for the identity and returns its index. This