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