lib/split/experiment.rb in split-2.1.0 vs lib/split/experiment.rb in split-2.2.0

- old
+ new

@@ -78,33 +78,19 @@ def save validate! if new_record? - Split.redis.sadd(:experiments, name) start unless Split.configuration.start_manually - @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name)} - goals_collection.save - save_metadata - else - existing_alternatives = load_alternatives_from_redis - existing_goals = Split::GoalsCollection.new(@name).load_from_redis - existing_metadata = load_metadata_from_redis - unless existing_alternatives == @alternatives.map(&:name) && existing_goals == @goals && existing_metadata == @metadata - reset - @alternatives.each(&:delete) - goals_collection.delete - delete_metadata - Split.redis.del(@name) - @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name)} - goals_collection.save - save_metadata - end + elsif experiment_configuration_has_changed? + reset unless Split.configuration.reset_manually end - Split.redis.hset(experiment_config_key, :resettable, resettable) - Split.redis.hset(experiment_config_key, :algorithm, algorithm.to_s) + persist_experiment_configuration if new_record? || experiment_configuration_has_changed? + + redis.hset(experiment_config_key, :resettable, resettable) + redis.hset(experiment_config_key, :algorithm, algorithm.to_s) self end def validate! if @alternatives.empty? && Split.configuration.experiment_for(@name).nil? @@ -113,11 +99,11 @@ @alternatives.each {|a| a.validate! } goals_collection.validate! end def new_record? - !Split.redis.exists(name) + !redis.exists(name) end def ==(obj) self.name == obj.name end @@ -147,23 +133,24 @@ end end end def winner - if w = Split.redis.hget(:experiment_winner, name) - Split::Alternative.new(w, name) + experiment_winner = redis.hget(:experiment_winner, name) + if experiment_winner + Split::Alternative.new(experiment_winner, name) else nil end end def has_winner? !winner.nil? end def winner=(winner_name) - Split.redis.hset(:experiment_winner, name, winner_name.to_s) + redis.hset(:experiment_winner, name, winner_name.to_s) end def participant_count alternatives.inject(0){|sum,a| sum + a.participant_count} end @@ -171,25 +158,25 @@ def control alternatives.first end def reset_winner - Split.redis.hdel(:experiment_winner, name) + redis.hdel(:experiment_winner, name) end def start - Split.redis.hset(:experiment_start_times, @name, Time.now.to_i) + redis.hset(:experiment_start_times, @name, Time.now.to_i) end def start_time - t = Split.redis.hget(:experiment_start_times, @name) + t = redis.hget(:experiment_start_times, @name) if t # Check if stored time is an integer if t =~ /^[-+]?[0-9]+$/ - t = Time.at(t.to_i) + Time.at(t.to_i) else - t = Time.parse(t) + Time.parse(t) end end end def next_alternative @@ -203,15 +190,15 @@ alternatives.first end end def version - @version ||= (Split.redis.get("#{name.to_s}:version").to_i || 0) + @version ||= (redis.get("#{name}:version").to_i || 0) end def increment_version - @version = Split.redis.incr("#{name}:version") + @version = redis.incr("#{name}:version") end def key if version.to_i > 0 "#{name}:#{version}" @@ -245,28 +232,25 @@ end def delete Split.configuration.on_before_experiment_delete.call(self) if Split.configuration.start_manually - Split.redis.hdel(:experiment_start_times, @name) + redis.hdel(:experiment_start_times, @name) end - alternatives.each(&:delete) reset_winner - Split.redis.srem(:experiments, name) - Split.redis.del(name) - goals_collection.delete - delete_metadata + redis.srem(:experiments, name) + remove_experiment_configuration Split.configuration.on_experiment_delete.call(self) increment_version end def delete_metadata - Split.redis.del(metadata_key) + redis.del(metadata_key) end def load_from_redis - exp_config = Split.redis.hgetall(experiment_config_key) + exp_config = redis.hgetall(experiment_config_key) options = { resettable: exp_config['resettable'], algorithm: exp_config['algorithm'], alternatives: load_alternatives_from_redis, @@ -295,12 +279,10 @@ self.save end end def estimate_winning_alternative(goal = nil) - # TODO - refactor out functionality to work with and without goals - # initialize a hash of beta distributions based on the alternatives' conversion rates beta_params = calc_beta_params(goal) winning_alternatives = [] @@ -393,15 +375,15 @@ end return beta_params end def calc_time=(time) - Split.redis.hset(experiment_config_key, :calc_time, time) + redis.hset(experiment_config_key, :calc_time, time) end def calc_time - Split.redis.hget(experiment_config_key, :calc_time).to_i + redis.hget(experiment_config_key, :calc_time).to_i end def jstring(goal = nil) js_id = if goal.nil? name @@ -416,15 +398,15 @@ def experiment_config_key "experiment_configurations/#{@name}" end def load_metadata_from_configuration - metadata = Split.configuration.experiment_for(@name)[:metadata] + Split.configuration.experiment_for(@name)[:metadata] end def load_metadata_from_redis - meta = Split.redis.get(metadata_key) + meta = redis.get(metadata_key) JSON.parse(meta) unless meta.nil? end def load_alternatives_from_configuration alts = Split.configuration.experiment_for(@name)[:alternatives] @@ -435,25 +417,52 @@ alts.flatten end end def load_alternatives_from_redis - case Split.redis.type(@name) + case redis.type(@name) when 'set' # convert legacy sets to lists - alts = Split.redis.smembers(@name) - Split.redis.del(@name) - alts.reverse.each {|a| Split.redis.lpush(@name, a) } - Split.redis.lrange(@name, 0, -1) + alts = redis.smembers(@name) + redis.del(@name) + alts.reverse.each {|a| redis.lpush(@name, a) } + redis.lrange(@name, 0, -1) else - Split.redis.lrange(@name, 0, -1) + redis.lrange(@name, 0, -1) end end - def save_metadata - Split.redis.set(metadata_key, @metadata.to_json) unless @metadata.nil? + private + + def redis + Split.redis end - private + def redis_interface + RedisInterface.new + end + + def persist_experiment_configuration + redis_interface.add_to_set(:experiments, name) + redis_interface.persist_list(name, @alternatives.map(&:name)) + goals_collection.save + redis.set(metadata_key, @metadata.to_json) unless @metadata.nil? + end + + def remove_experiment_configuration + @alternatives.each(&:delete) + goals_collection.delete + delete_metadata + redis.del(@name) + end + + def experiment_configuration_has_changed? + existing_alternatives = load_alternatives_from_redis + existing_goals = Split::GoalsCollection.new(@name).load_from_redis + existing_metadata = load_metadata_from_redis + existing_alternatives != @alternatives.map(&:name) || + existing_goals != @goals || + existing_metadata != @metadata + end def goals_collection Split::GoalsCollection.new(@name, @goals) end end