diff --git a/README.mdown b/README.mdown index 7667b3d8..1f429558 100644 --- a/README.mdown +++ b/README.mdown @@ -218,6 +218,35 @@ Split.configure do |config| end ``` +### Trial Event Hooks + +You can define methods that will be called at the same time as experiment +alternative participation and goal completion. + +For example: + +``` ruby +Split.configure do |config| + config.on_trial_choose = :log_trial_choice + config.on_trial_complete = :log_trial_complete +end +``` + +Set these attributes to a method name available in the same context as the +`ab_test` method. These methods should accept one argument, a `Trial` instance. + +``` ruby +def log_trial_choose(trial) + logger.info "experiment=%s alternative=%s user=%s" % + [ trial.experiment.name, trial.alternative, current_user.id ] +end + +def log_trial_complete(trial) + logger.info "experiment=%s alternative=%s user=%s complete=true" % + [ trial.experiment.name, trial.alternative, current_user.id ] +end +``` + ## Web Interface Split comes with a Sinatra-based front end to get an overview of how your experiments are doing. diff --git a/lib/split/configuration.rb b/lib/split/configuration.rb index 0da88361..f584b9e8 100644 --- a/lib/split/configuration.rb +++ b/lib/split/configuration.rb @@ -12,6 +12,8 @@ class Configuration attr_accessor :persistence attr_accessor :algorithm attr_accessor :store_override + attr_accessor :on_trial_choose + attr_accessor :on_trial_complete attr_reader :experiments diff --git a/lib/split/helper.rb b/lib/split/helper.rb index 8e57de6f..f8c1c03a 100644 --- a/lib/split/helper.rb +++ b/lib/split/helper.rb @@ -63,6 +63,8 @@ def finish_experiment(experiment, options = {:reset => true}) alternative_name = ab_user[experiment.key] trial = Trial.new(:experiment => experiment, :alternative => alternative_name, :goals => options[:goals]) trial.complete! + call_trial_complete_hook(trial) + if should_reset reset!(experiment) else @@ -178,6 +180,7 @@ def start_trial(trial) ret = ab_user[experiment.key] else trial.choose! + call_trial_choose_hook(trial) ret = begin_experiment(experiment, trial.alternative.name) end end @@ -186,6 +189,14 @@ def start_trial(trial) ret end + def call_trial_choose_hook(trial) + send(Split.configuration.on_trial_choose, trial) if Split.configuration.on_trial_choose + end + + def call_trial_complete_hook(trial) + send(Split.configuration.on_trial_complete, trial) if Split.configuration.on_trial_complete + end + def keys_without_experiment(keys, experiment_key) keys.reject { |k| k.match(Regexp.new("^#{experiment_key}(:finished)?$")) } end diff --git a/spec/helper_spec.rb b/spec/helper_spec.rb index 31f0cd9b..b2eb1334 100755 --- a/spec/helper_spec.rb +++ b/spec/helper_spec.rb @@ -112,6 +112,14 @@ end end + context "when on_trial_choose is set" do + before { Split.configuration.on_trial_choose = :some_method } + it "should call the method" do + self.should_receive(:some_method) + ab_test('link_color', 'blue', 'red') + end + end + it "should allow passing a block" do alt = ab_test('link_color', 'blue', 'red') ret = ab_test('link_color', 'blue', 'red') { |alternative| "shared/#{alternative}" } @@ -247,6 +255,14 @@ doing_other_tests?(@experiment.key).should be false end + context "when on_trial_complete is set" do + before { Split.configuration.on_trial_complete = :some_method } + it "should call the method" do + self.should_receive(:some_method) + finished(@experiment_name) + end + end + end context "finished with config" do