diff --git a/lib/split/experiment.rb b/lib/split/experiment.rb index befdf3d5..0bdd931b 100644 --- a/lib/split/experiment.rb +++ b/lib/split/experiment.rb @@ -90,6 +90,7 @@ def save redis.hset(experiment_config_key, :retain_user_alternatives_after_reset, retain_user_alternatives_after_reset) redis.hset(experiment_config_key, :resettable, resettable) redis.hset(experiment_config_key, :algorithm, algorithm.to_s) + self end diff --git a/lib/split/helper.rb b/lib/split/helper.rb index 4e53a5ec..6064ce55 100644 --- a/lib/split/helper.rb +++ b/lib/split/helper.rb @@ -11,9 +11,11 @@ def ab_test(metric_descriptor, control = nil, *alternatives) alternative = if Split.configuration.enabled && !exclude_visitor? experiment.save raise(Split::InvalidExperimentsFormatError) unless (Split.configuration.experiments || {}).fetch(experiment.name.to_sym, {})[:combined_experiments].nil? + trial = Trial.new(:user => ab_user, :experiment => experiment, :override => override_alternative(experiment.name), :exclude => !is_qualified?, :disabled => split_generically_disabled?) + alt = trial.choose!(self) alt ? alt.name : nil else diff --git a/lib/split/persistence.rb b/lib/split/persistence.rb index a464c463..dbfdaf70 100644 --- a/lib/split/persistence.rb +++ b/lib/split/persistence.rb @@ -6,6 +6,7 @@ module Persistence require 'split/persistence/dual_adapter' require 'split/persistence/redis_adapter' require 'split/persistence/session_adapter' + require 'split/persistence/mysql_adapter' ADAPTERS = { :cookie => Split::Persistence::CookieAdapter, diff --git a/lib/split/persistence/mysql_adapter.rb b/lib/split/persistence/mysql_adapter.rb new file mode 100644 index 00000000..bf44aa80 --- /dev/null +++ b/lib/split/persistence/mysql_adapter.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true +module Split + module Persistence + class MysqlAdapter + DEFAULT_CONFIG = {:namespace => 'persistence'}.freeze + + attr_reader :account_id + + def initialize(context, acc_id = nil) + # Split::Persistence.adapter.new(nil, 347381).test_create_or_update('exp123:1', 'control') + if acc_id + @account_id = acc_id + elsif lookup_by = self.class.config[:lookup_by] + if lookup_by.respond_to?(:call) + key_frag = lookup_by.call(context) + else + key_frag = context.send(lookup_by) + end + @account_id = key_frag + else + raise "Please configure lookup_by" + end + end + + #-------------- + + def test_get(property) + search_for_record(property) + end + + def test_create_or_update(property, value) + record = Manage::ExperimentPersistence.where( + experiment_id: experiment_id_parser(property), + account_id: account_id, + property: property + ).first_or_initialize + record.update!(value: value) + end + + #------------- + + def [](property) + search_for_record(property) + end + + def []=(property, value) + record = Manage::ExperimentPersistence.where( + experiment_id: experiment_id_parser(property), + account_id: account_id, + property: property + ).first_or_initialize + record.update!(value: value) + end + + def delete(field) + record_to_delete = search_for_record(property) + record_to_delete.destroy! + end + + # -------- + + def keys + Split.redis.hkeys(redis_key) + end + + def self.with_config(options={}) + self.config.merge!(options) + self + end + + def self.config + @config ||= DEFAULT_CONFIG.dup + end + + def self.reset_config! + @config = DEFAULT_CONFIG.dup + end + + def experiment_id_parser(property) + property.split(":").first + end + + def search_for_record(property) + Manage::ExperimentPersistence.find_by(experiment_id: experiment_id_parser(property), property: property, account_id: account_id) + end + + + end + end +end diff --git a/lib/split/redis_interface.rb b/lib/split/redis_interface.rb index 1796a980..0a55a1b8 100644 --- a/lib/split/redis_interface.rb +++ b/lib/split/redis_interface.rb @@ -41,7 +41,7 @@ def make_list_length(list_name, new_length) def add_to_set(set_name, value) redis.sadd(set_name, value) unless redis.sismember(set_name, value) - end + end`` private diff --git a/lib/split/trial.rb b/lib/split/trial.rb index c561e36a..fbcc607f 100644 --- a/lib/split/trial.rb +++ b/lib/split/trial.rb @@ -55,13 +55,17 @@ def complete!(context = nil) # method is guaranteed to only run once, and will skip the alternative choosing process if run # a second time. def choose!(context = nil) + @user.cleanup_old_experiments! # Only run the process once + return alternative if @alternative_choosen + user_experiment_key = @experiment.retain_user_alternatives_after_reset ? @user.alternative_key_for_experiment(@experiment) : @experiment.key new_participant = @user[user_experiment_key].nil? if override_is_alternative? + self.alternative = @options[:override] if should_store_alternative? && !@user[user_experiment_key] self.alternative.increment_participation @@ -72,11 +76,11 @@ def choose!(context = nil) self.alternative = @experiment.winner else cleanup_old_versions unless experiment.retain_user_alternatives_after_reset - if exclude_user? self.alternative = @experiment.control else self.alternative = @user[user_experiment_key] + if alternative.nil? if @experiment.cohorting_disabled? self.alternative = @experiment.control @@ -87,17 +91,21 @@ def choose!(context = nil) self.alternative.increment_participation save_time_that_user_is_assigned + run_callback context, Split.configuration.on_trial_choose end end end end + new_participant_and_cohorting_disabled = new_participant && @experiment.cohorting_disabled? @user[user_experiment_key] = alternative.name unless @experiment.has_winner? || !should_store_alternative? || new_participant_and_cohorting_disabled + @alternative_choosen = true run_callback context, Split.configuration.on_trial unless @options[:disabled] || Split.configuration.disabled? || new_participant_and_cohorting_disabled + alternative end