lib/field_test/experiment.rb in field_test-0.2.4 vs lib/field_test/experiment.rb in field_test-0.3.0
- old
+ new
@@ -11,10 +11,11 @@
@weights = @variants.size.times.map { |i| attributes[:weights].to_a[i] || 1 }
@winner = attributes[:winner]
@started_at = Time.zone.parse(attributes[:started_at].to_s) if attributes[:started_at]
@ended_at = Time.zone.parse(attributes[:ended_at].to_s) if attributes[:ended_at]
@goals = attributes[:goals] || ["conversion"]
+ @goals_defined = !attributes[:goals].nil?
@use_events = attributes[:use_events]
end
def variant(participants, options = {})
return winner if winner
@@ -28,29 +29,22 @@
membership.variant = options[:variant]
else
membership.variant ||= weighted_variant
end
+ participant = participants.first
+
# upgrade to preferred participant
- membership.participant = participants.first
+ membership.participant = participant.participant if membership.respond_to?(:participant=)
+ membership.participant_type = participant.type if membership.respond_to?(:participant_type=)
+ membership.participant_id = participant.id if membership.respond_to?(:participant_id=)
if membership.changed?
begin
membership.save!
-
- # log it!
- info = {
- experiment: id,
- variant: membership.variant,
- participant: membership.participant
- }.merge(options.slice(:ip, :user_agent))
-
- # sorta logfmt :)
- info = info.map { |k, v| v = "\"#{v}\"" if k == :user_agent; "#{k}=#{v}" }.join(" ")
- Rails.logger.info "[field test] #{info}"
rescue ActiveRecord::RecordNotUnique
- membership = memberships.find_by(participant: participants.first)
+ membership = memberships.find_by(participant.where_values)
end
end
membership.try(:variant) || variants.first
end
@@ -98,20 +92,33 @@
relation = memberships.group(:variant)
relation = relation.where("field_test_memberships.created_at >= ?", started_at) if started_at
relation = relation.where("field_test_memberships.created_at <= ?", ended_at) if ended_at
- if use_events?
+ if use_events? && @goals_defined
data = {}
- sql =
- relation.joins("LEFT JOIN field_test_events ON field_test_events.field_test_membership_id = field_test_memberships.id")
- .select("variant, COUNT(DISTINCT participant) AS participated, COUNT(DISTINCT field_test_membership_id) AS converted")
- .where(field_test_events: {name: [goal, nil]})
- FieldTest::Membership.connection.select_all(sql).each do |row|
- data[[row["variant"], true]] = row["converted"].to_i
- data[[row["variant"], false]] = row["participated"].to_i - row["converted"].to_i
+ participated = relation.count
+
+ adapter_name = relation.connection.adapter_name
+ column =
+ if FieldTest.legacy_participants
+ :participant
+ elsif adapter_name =~ /postg/i # postgres
+ "(participant_type, participant_id)"
+ elsif adapter_name =~ /mysql/i
+ "participant_type, participant_id"
+ else
+ # not perfect, but it'll do
+ "COALESCE(participant_type, '') || ':' || participant_id"
+ end
+
+ converted = events.merge(relation).where(field_test_events: {name: goal}).distinct.count(column)
+
+ (participated.keys + converted.keys).uniq.each do |variant|
+ data[[variant, true]] = converted[variant].to_i
+ data[[variant, false]] = participated[variant].to_i - converted[variant].to_i
end
else
data = relation.group(:converted).count
end
@@ -123,10 +130,11 @@
participated: participated,
converted: converted,
conversion_rate: participated > 0 ? converted.to_f / participated : nil
}
end
+
case variants.size
when 1, 2, 3
total = 0.0
(variants.size - 1).times do |i|
@@ -187,34 +195,39 @@
end
end
private
- def check_participants(participants)
- raise FieldTest::UnknownParticipant, "Use the :participant option to specify a participant" if participants.empty?
- end
+ def check_participants(participants)
+ raise FieldTest::UnknownParticipant, "Use the :participant option to specify a participant" if participants.empty?
+ end
- def membership_for(participants)
- memberships = self.memberships.where(participant: participants).index_by(&:participant)
- participants.map { |part| memberships[part] }.compact.first
+ # TODO fetch in single query
+ def membership_for(participants)
+ membership = nil
+ participants.each do |participant|
+ membership = self.memberships.find_by(participant.where_values)
+ break if membership
end
+ membership
+ end
- def weighted_variant
- total = weights.sum.to_f
- pick = rand
- n = 0
- weights.map { |w| w / total }.each_with_index do |w, i|
- n += w
- return variants[i] if n >= pick
- end
- variants.last
+ def weighted_variant
+ total = weights.sum.to_f
+ pick = rand
+ n = 0
+ weights.map { |w| w / total }.each_with_index do |w, i|
+ n += w
+ return variants[i] if n >= pick
end
+ variants.last
+ end
- def cache_fetch(key)
- if FieldTest.cache
- Rails.cache.fetch(key.join("/")) { yield }
- else
- yield
- end
+ def cache_fetch(key)
+ if FieldTest.cache
+ Rails.cache.fetch(key.join("/")) { yield }
+ else
+ yield
end
+ end
end
end