lib/split/experiment.rb in split-4.0.1 vs lib/split/experiment.rb in split-4.0.2

- old
+ new

@@ -1,9 +1,7 @@ # frozen_string_literal: true -require 'rubystats' - module Split class Experiment attr_accessor :name attr_accessor :goals attr_accessor :alternative_probabilities @@ -11,11 +9,11 @@ attr_reader :alternatives attr_reader :resettable DEFAULT_OPTIONS = { - :resettable => true + resettable: true } def self.find(name) Split.cache(:experiments, name) do return unless Split.redis.exists?(name) @@ -50,11 +48,11 @@ def extract_alternatives_from_options(options) alts = options[:alternatives] || [] if alts.length == 1 if alts[0].is_a? Hash - alts = alts[0].map{|k, v| {k => v} } + alts = alts[0].map { |k, v| { k => v } } end end if alts.empty? exp_config = Split.configuration.experiment_for(name) @@ -85,20 +83,20 @@ elsif experiment_configuration_has_changed? reset unless Split.configuration.reset_manually persist_experiment_configuration end - redis.hmset(experiment_config_key, :resettable, resettable, + redis.hmset(experiment_config_key, :resettable, resettable.to_s, :algorithm, algorithm.to_s) self end def validate! if @alternatives.empty? && Split.configuration.experiment_for(@name).nil? raise ExperimentNotFound.new("Experiment #{@name} not found") end - @alternatives.each {|a| a.validate! } + @alternatives.each { |a| a.validate! } goals_collection.validate! end def new_record? ExperimentCatalog.find(name).nil? @@ -107,11 +105,11 @@ def ==(obj) self.name == obj.name end def [](name) - alternatives.find{|a| a.name == name} + alternatives.find { |a| a.name == name } end def algorithm @algorithm ||= Split.configuration.algorithm end @@ -119,11 +117,11 @@ def algorithm=(algorithm) @algorithm = algorithm.is_a?(String) ? algorithm.constantize : algorithm end def resettable=(resettable) - @resettable = resettable.is_a?(String) ? resettable == 'true' : resettable + @resettable = resettable.is_a?(String) ? resettable == "true" : resettable end def alternatives=(alts) @alternatives = alts.map do |alternative| if alternative.kind_of?(Split::Alternative) @@ -155,11 +153,11 @@ @has_winner = true Split.configuration.on_experiment_winner_choose.call(self) end def participant_count - alternatives.inject(0){|sum, a| sum + a.participant_count} + alternatives.inject(0) { |sum, a| sum + a.participant_count } end def control alternatives.first end @@ -260,12 +258,12 @@ def load_from_redis exp_config = redis.hgetall(experiment_config_key) options = { - resettable: exp_config['resettable'], - algorithm: exp_config['algorithm'], + resettable: exp_config["resettable"], + algorithm: exp_config["algorithm"], alternatives: load_alternatives_from_redis, goals: Split::GoalsCollection.new(@name).load_from_redis, metadata: load_metadata_from_redis } @@ -326,24 +324,24 @@ def calc_alternative_probabilities(winning_counts, number_of_simulations) alternative_probabilities = {} winning_counts.each do |alternative, wins| alternative_probabilities[alternative] = wins / number_of_simulations.to_f end - return alternative_probabilities + alternative_probabilities end def count_simulated_wins(winning_alternatives) - # initialize a hash to keep track of winning alternative in simulations + # initialize a hash to keep track of winning alternative in simulations winning_counts = {} alternatives.each do |alternative| winning_counts[alternative] = 0 end # count number of times each alternative won, calculate probabilities, place in hash winning_alternatives.each do |alternative| winning_counts[alternative] += 1 end - return winning_counts + winning_counts end def find_simulated_winner(simulated_cr_hash) # figure out which alternative had the highest simulated conversion rate winning_pair = ["", 0.0] @@ -351,25 +349,25 @@ if rate > winning_pair[1] winning_pair = [alternative, rate] end end winner = winning_pair[0] - return winner + winner end def calc_simulated_conversion_rates(beta_params) simulated_cr_hash = {} # create a hash which has the conversion rate pulled from each alternative's beta distribution beta_params.each do |alternative, params| alpha = params[0] beta = params[1] - simulated_conversion_rate = Rubystats::BetaDistribution.new(alpha, beta).rng + simulated_conversion_rate = Split::Algorithms.beta_distribution_rng(alpha, beta) simulated_cr_hash[alternative] = simulated_conversion_rate end - return simulated_cr_hash + simulated_cr_hash end def calc_beta_params(goal = nil) beta_params = {} alternatives.each do |alternative| @@ -379,11 +377,11 @@ params = [alpha, beta] beta_params[alternative] = params end - return beta_params + beta_params end def calc_time=(time) redis.hset(experiment_config_key, :calc_time, time) end @@ -392,15 +390,15 @@ redis.hget(experiment_config_key, :calc_time).to_i end def jstring(goal = nil) js_id = if goal.nil? - name - else - name + "-" + goal - end - js_id.gsub('/', '--') + name + else + name + "-" + goal + end + js_id.gsub("/", "--") end def cohorting_disabled? @cohorting_disabled ||= begin value = redis.hget(experiment_config_key, :cohorting) @@ -408,97 +406,95 @@ end end def disable_cohorting @cohorting_disabled = true - redis.hset(experiment_config_key, :cohorting, true) + redis.hset(experiment_config_key, :cohorting, true.to_s) end def enable_cohorting @cohorting_disabled = false - redis.hset(experiment_config_key, :cohorting, false) + redis.hset(experiment_config_key, :cohorting, false.to_s) end protected + def experiment_config_key + "experiment_configurations/#{@name}" + end - def experiment_config_key - "experiment_configurations/#{@name}" - end + def load_metadata_from_configuration + Split.configuration.experiment_for(@name)[:metadata] + end - def load_metadata_from_configuration - Split.configuration.experiment_for(@name)[:metadata] - end + def load_metadata_from_redis + meta = redis.get(metadata_key) + JSON.parse(meta) unless meta.nil? + end - def load_metadata_from_redis - meta = redis.get(metadata_key) - JSON.parse(meta) unless meta.nil? - end - - def load_alternatives_from_configuration - alts = Split.configuration.experiment_for(@name)[:alternatives] - raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts - if alts.is_a?(Hash) - alts.keys - else - alts.flatten + def load_alternatives_from_configuration + alts = Split.configuration.experiment_for(@name)[:alternatives] + raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts + if alts.is_a?(Hash) + alts.keys + else + alts.flatten + end end - end - def load_alternatives_from_redis - alternatives = redis.lrange(@name, 0, -1) - alternatives.map do |alt| - alt = begin - JSON.parse(alt) - rescue - alt - end - Split::Alternative.new(alt, @name) + def load_alternatives_from_redis + alternatives = redis.lrange(@name, 0, -1) + alternatives.map do |alt| + alt = begin + JSON.parse(alt) + rescue + alt + end + Split::Alternative.new(alt, @name) + end end - end private + def redis + Split.redis + end - def redis - Split.redis - end + def redis_interface + RedisInterface.new + end - def redis_interface - RedisInterface.new - end + def persist_experiment_configuration + redis_interface.add_to_set(:experiments, name) + redis_interface.persist_list(name, @alternatives.map { |alt| { alt.name => alt.weight }.to_json }) + goals_collection.save - def persist_experiment_configuration - redis_interface.add_to_set(:experiments, name) - redis_interface.persist_list(name, @alternatives.map{|alt| {alt.name => alt.weight}.to_json}) - goals_collection.save + if @metadata + redis.set(metadata_key, @metadata.to_json) + else + delete_metadata + end + end - if @metadata - redis.set(metadata_key, @metadata.to_json) - else + def remove_experiment_configuration + @alternatives.each(&:delete) + goals_collection.delete delete_metadata + redis.del(@name) end - end - def remove_experiment_configuration - @alternatives.each(&:delete) - goals_collection.delete - delete_metadata - redis.del(@name) - end + def experiment_configuration_has_changed? + existing_experiment = Experiment.find(@name) - def experiment_configuration_has_changed? - existing_experiment = Experiment.find(@name) + existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) || + existing_experiment.goals != @goals || + existing_experiment.metadata != @metadata + end - existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) || - existing_experiment.goals != @goals || - existing_experiment.metadata != @metadata - end + def goals_collection + Split::GoalsCollection.new(@name, @goals) + end - def goals_collection - Split::GoalsCollection.new(@name, @goals) - end - - def remove_experiment_cohorting - @cohorting_disabled = false - redis.hdel(experiment_config_key, :cohorting) - end + def remove_experiment_cohorting + @cohorting_disabled = false + redis.hdel(experiment_config_key, :cohorting) + end end end