Skip to content
This repository was archived by the owner on Jan 1, 2024. It is now read-only.

Commit c7900b9

Browse files
authored
Invoke on_assignment callback after assignment is stored in database; add global on_assignment callback (#368)
* Default callback configurations for on_assignment and after_assignment callbacks Added after_assignment callback and specs Minor fixes * Get assignment earlier in callbacks * Deleted after_assignment callback Call on_assignment after connection.ab_add_participant * Added test_calls_on_assignment_defined_in_experiment
1 parent 4363695 commit c7900b9

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed

lib/vanity/configuration.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def default_request_filter(request) # :nodoc:
4949
on_datastore_error: ->(error, klass, method, arguments) {
5050
default_on_datastore_error(error, klass, method, arguments)
5151
},
52+
on_assignment: nil,
5253
request_filter: ->(request) { default_request_filter(request) },
5354
templates_path: File.expand_path(File.join(File.dirname(__FILE__), 'templates')),
5455
use_js: false,
@@ -181,6 +182,9 @@ def default_request_filter(request) # :nodoc:
181182
# Cookie path. If true, cookie will not be available to JS. By default false.
182183
attr_writer :cookie_httponly
183184

185+
# Default callback on assigment
186+
attr_writer :on_assignment
187+
184188
# We independently list each attr_accessor to includes docs, otherwise
185189
# something like DEFAULTS.each { |key, value| attr_accessor key } would be
186190
# shorter.

lib/vanity/experiment/ab_test.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -617,13 +617,15 @@ def alternative_for(identity)
617617
# Saves the assignment of an alternative to a person and performs the
618618
# necessary housekeeping. Ignores repeat identities and filters using
619619
# Playground#request_filter.
620-
def save_assignment(identity, index, request)
620+
def save_assignment(identity, index, _request)
621621
return if index == connection.ab_showing(@id, identity)
622+
return if connection.ab_seen @id, identity, index
622623

623-
call_on_assignment_if_available(identity, index)
624624
rebalance_if_necessary!
625625

626626
connection.ab_add_participant(@id, index, identity)
627+
call_on_assignment_if_available(identity, index)
628+
627629
check_completion!
628630
end
629631

@@ -633,13 +635,18 @@ def filter_visitor?(request, identity)
633635
end
634636

635637
def call_on_assignment_if_available(identity, index)
638+
assignment = alternatives[index]
639+
636640
# if we have an on_assignment block, call it on new assignments
637641
if defined?(@on_assignment_block) && @on_assignment_block
638-
assignment = alternatives[index]
639-
if !connection.ab_seen @id, identity, index
640-
@on_assignment_block.call(Vanity.context, identity, assignment, self)
641-
end
642+
@on_assignment_block.call(Vanity.context, identity, assignment, self)
643+
644+
return
642645
end
646+
647+
return unless Vanity.configuration.on_assignment.is_a?(Proc)
648+
649+
Vanity.configuration.on_assignment.call(Vanity.context, identity, assignment, self)
643650
end
644651

645652
def rebalance_if_necessary!
@@ -688,10 +695,8 @@ def build_alternatives(args)
688695
# We're really only interested in 90%, 95%, 99% and 99.9%.
689696
Z_TO_PROBABILITY = [90, 95, 99, 99.9].map { |pct| [norm_dist.find { |x,a| a >= pct }.first, pct] }.reverse
690697
end
691-
692698
end
693699

694-
695700
module Definition
696701
# Define an A/B test with the given name. For example:
697702
# ab_test "New Banner" do
@@ -701,6 +706,5 @@ def ab_test(name, &block)
701706
define name, :ab_test, &block
702707
end
703708
end
704-
705709
end
706710
end

test/experiment/ab_test.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,46 @@ def test_returns_the_same_alternative_consistently_when_on_assignment_is_set
520520
end
521521
end
522522

523+
def test_calls_default_on_assignment
524+
on_assignment_called_times = 0
525+
526+
Vanity.configuration.on_assignment = proc do |_controller, _identity, _assignment|
527+
on_assignment_called_times += 1
528+
end
529+
530+
new_ab_test :foobar do
531+
alternatives "foo", "bar"
532+
default "foo"
533+
identify { "6e98ec" }
534+
metrics :coolness
535+
end
536+
537+
2.times { experiment(:foobar).chooses("foo") }
538+
assert_equal 1, on_assignment_called_times
539+
end
540+
541+
def test_calls_on_assignment_defined_in_experiment
542+
expected_value = 0
543+
544+
Vanity.configuration.on_assignment = proc do |_controller, _identity, _assignment|
545+
expected_value = 1
546+
end
547+
548+
new_ab_test :foobar do
549+
alternatives "foo", "bar"
550+
default "foo"
551+
identify { "6e98ec" }
552+
metrics :coolness
553+
554+
on_assignment do |_controller, _identity, _assignment|
555+
expected_value = 20
556+
end
557+
end
558+
559+
2.times { experiment(:foobar).chooses("foo") }
560+
assert_equal 20, expected_value
561+
end
562+
523563
# -- ab_assigned --
524564

525565
def test_ab_assigned

test/playground_test.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,4 @@
130130
assert_equal [[Vanity.playground.experiment(:foobar), alt]], Vanity.playground.participant_info("abcdef")
131131
end
132132
end
133-
134133
end

0 commit comments

Comments
 (0)