Class: Split::Experiment
- Inherits:
-
Object
- Object
- Split::Experiment
- Defined in:
- lib/split/experiment.rb
Instance Attribute Summary collapse
- #algorithm ⇒ Object
-
#name ⇒ Object
Returns the value of attribute name.
-
#resettable ⇒ Object
Returns the value of attribute resettable.
Class Method Summary collapse
- .all ⇒ Object
- .all_experiment_names_from_configuration ⇒ Object
- .all_experiment_names_from_redis ⇒ Object
- .experiment_config_key(name) ⇒ Object
- .find(name) ⇒ Object
- .find_or_create(key, *alternatives) ⇒ Object
- .initialize_alternatives(alternatives, name) ⇒ Object
- .load_alternatives_for(name) ⇒ Object
- .load_alternatives_from_configuration_for(name) ⇒ Object
- .load_alternatives_from_redis_for(name) ⇒ Object
- .load_from_configuration(name) ⇒ Object
- .load_from_redis(name) ⇒ Object
Instance Method Summary collapse
- #==(obj) ⇒ Object
- #[](name) ⇒ Object
- #alternative_names ⇒ Object
- #alternatives ⇒ Object
- #control ⇒ Object
- #delete ⇒ Object
- #finished_key ⇒ Object
- #increment_version ⇒ Object
-
#initialize(name, options = {}) ⇒ Experiment
constructor
A new instance of Experiment.
- #key ⇒ Object
- #new_record? ⇒ Boolean
- #next_alternative ⇒ Object
- #participant_count ⇒ Object
- #random_alternative ⇒ Object
- #reset ⇒ Object
- #reset_winner ⇒ Object
- #resettable? ⇒ Boolean
- #save ⇒ Object
- #start_time ⇒ Object
- #version ⇒ Object
- #winner ⇒ Object
- #winner=(winner_name) ⇒ Object
Constructor Details
#initialize(name, options = {}) ⇒ Experiment
Returns a new instance of Experiment.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/split/experiment.rb', line 7 def initialize(name, = {}) = { :resettable => true, }.merge() @name = name.to_s @alternatives = [:alternatives] if ![:alternatives].nil? if ![:algorithm].nil? @algorithm = [:algorithm].is_a?(String) ? [:algorithm].constantize : [:algorithm] end if ![:resettable].nil? @resettable = [:resettable].is_a?(String) ? [:resettable] == 'true' : [:resettable] end if ![:alternative_names].nil? @alternatives = [:alternative_names].map do |alternative| Split::Alternative.new(alternative, name) end end end |
Instance Attribute Details
#algorithm ⇒ Object
32 33 34 |
# File 'lib/split/experiment.rb', line 32 def algorithm @algorithm ||= Split.configuration.algorithm end |
#name ⇒ Object
Returns the value of attribute name.
3 4 5 |
# File 'lib/split/experiment.rb', line 3 def name @name end |
#resettable ⇒ Object
Returns the value of attribute resettable.
5 6 7 |
# File 'lib/split/experiment.rb', line 5 def resettable @resettable end |
Class Method Details
.all ⇒ Object
198 199 200 |
# File 'lib/split/experiment.rb', line 198 def self.all Array(all_experiment_names_from_redis + all_experiment_names_from_configuration).map {|e| find(e)} end |
.all_experiment_names_from_configuration ⇒ Object
206 207 208 |
# File 'lib/split/experiment.rb', line 206 def self.all_experiment_names_from_configuration Split.configuration.experiments ? Split.configuration.experiments.keys : [] end |
.all_experiment_names_from_redis ⇒ Object
202 203 204 |
# File 'lib/split/experiment.rb', line 202 def self.all_experiment_names_from_redis Split.redis.smembers(:experiments) end |
.experiment_config_key(name) ⇒ Object
194 195 196 |
# File 'lib/split/experiment.rb', line 194 def self.experiment_config_key(name) "experiment_configurations/#{name}" end |
.find(name) ⇒ Object
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/split/experiment.rb', line 211 def self.find(name) if Split.configuration.experiment_for(name) obj = load_from_configuration(name) elsif Split.redis.exists(name) obj = load_from_redis(name) else obj = nil end obj end |
.find_or_create(key, *alternatives) ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/split/experiment.rb', line 222 def self.find_or_create(key, *alternatives) name = key.to_s.split(':')[0] if alternatives.length == 1 if alternatives[0].is_a? Hash alternatives = alternatives[0].map{|k,v| {k => v} } end end alts = initialize_alternatives(alternatives, name) if Split.redis.exists(name) existing_alternatives = load_alternatives_for(name) if existing_alternatives == alts.map(&:name) experiment = self.new(name, :alternative_names => alternatives) else exp = self.new(name, :alternative_names => existing_alternatives) exp.reset exp.alternatives.each(&:delete) experiment = self.new(name, :alternative_names =>alternatives) experiment.save end else experiment = self.new(name, :alternative_names => alternatives) experiment.save end return experiment end |
.initialize_alternatives(alternatives, name) ⇒ Object
252 253 254 255 256 257 258 259 260 261 |
# File 'lib/split/experiment.rb', line 252 def self.initialize_alternatives(alternatives, name) unless alternatives.all? { |a| Split::Alternative.valid?(a) } raise ArgumentError, 'Alternatives must be strings' end alternatives.map do |alternative| Split::Alternative.new(alternative, name) end end |
.load_alternatives_for(name) ⇒ Object
150 151 152 153 154 155 156 |
# File 'lib/split/experiment.rb', line 150 def self.load_alternatives_for(name) if Split.configuration.experiment_for(name) load_alternatives_from_configuration_for(name) else load_alternatives_from_redis_for(name) end end |
.load_alternatives_from_configuration_for(name) ⇒ Object
158 159 160 161 162 163 164 165 166 |
# File 'lib/split/experiment.rb', line 158 def self.load_alternatives_from_configuration_for(name) alts = Split.configuration.experiment_for(name)[:alternatives] raise ArgumentError, "Experiment configuration is missing :alternatives array" if alts.nil? if alts.is_a?(Hash) alts.keys else alts.flatten end end |
.load_alternatives_from_redis_for(name) ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/split/experiment.rb', line 168 def self.load_alternatives_from_redis_for(name) case Split.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) else Split.redis.lrange(name, 0, -1) end end |
.load_from_configuration(name) ⇒ Object
180 181 182 183 184 185 |
# File 'lib/split/experiment.rb', line 180 def self.load_from_configuration(name) exp_config = Split.configuration.experiment_for(name) || {} self.new(name, :alternative_names => load_alternatives_for(name), :resettable => exp_config[:resettable], :algorithm => exp_config[:algorithm]) end |
.load_from_redis(name) ⇒ Object
187 188 189 190 191 192 |
# File 'lib/split/experiment.rb', line 187 def self.load_from_redis(name) exp_config = Split.redis.hgetall(experiment_config_key(name)) self.new(name, :alternative_names => load_alternatives_for(name), :resettable => exp_config['resettable'], :algorithm => exp_config['algorithm']) end |
Instance Method Details
#==(obj) ⇒ Object
36 37 38 |
# File 'lib/split/experiment.rb', line 36 def ==(obj) self.name == obj.name end |
#[](name) ⇒ Object
69 70 71 |
# File 'lib/split/experiment.rb', line 69 def [](name) alternatives.find{|a| a.name == name} end |
#alternative_names ⇒ Object
77 78 79 |
# File 'lib/split/experiment.rb', line 77 def alternative_names @alternatives.map(&:name) end |
#alternatives ⇒ Object
73 74 75 |
# File 'lib/split/experiment.rb', line 73 def alternatives @alternatives.dup end |
#control ⇒ Object
52 53 54 |
# File 'lib/split/experiment.rb', line 52 def control alternatives.first end |
#delete ⇒ Object
123 124 125 126 127 128 129 |
# File 'lib/split/experiment.rb', line 123 def delete alternatives.each(&:delete) reset_winner Split.redis.srem(:experiments, name) Split.redis.del(name) increment_version end |
#finished_key ⇒ Object
109 110 111 |
# File 'lib/split/experiment.rb', line 109 def finished_key "#{key}:finished" end |
#increment_version ⇒ Object
97 98 99 |
# File 'lib/split/experiment.rb', line 97 def increment_version @version = Split.redis.incr("#{name}:version") end |
#key ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/split/experiment.rb', line 101 def key if version.to_i > 0 "#{name}:#{version}" else name end end |
#new_record? ⇒ Boolean
131 132 133 |
# File 'lib/split/experiment.rb', line 131 def new_record? !Split.redis.exists(name) end |
#next_alternative ⇒ Object
81 82 83 |
# File 'lib/split/experiment.rb', line 81 def next_alternative winner || random_alternative end |
#participant_count ⇒ Object
48 49 50 |
# File 'lib/split/experiment.rb', line 48 def participant_count alternatives.inject(0){|sum,a| sum + a.participant_count} end |
#random_alternative ⇒ Object
85 86 87 88 89 90 91 |
# File 'lib/split/experiment.rb', line 85 def random_alternative if alternatives.length > 1 Split.configuration.algorithm.choose_alternative(self) else alternatives.first end end |
#reset ⇒ Object
117 118 119 120 121 |
# File 'lib/split/experiment.rb', line 117 def reset alternatives.each(&:reset) reset_winner increment_version end |
#reset_winner ⇒ Object
56 57 58 |
# File 'lib/split/experiment.rb', line 56 def reset_winner Split.redis.hdel(:experiment_winner, name) end |
#resettable? ⇒ Boolean
113 114 115 |
# File 'lib/split/experiment.rb', line 113 def resettable? resettable end |
#save ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/split/experiment.rb', line 135 def save if new_record? Split.redis.sadd(:experiments, name) Split.redis.hset(:experiment_start_times, @name, Time.now) @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name) } else Split.redis.del(name) @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name) } end config_key = Split::Experiment.experiment_config_key(name) Split.redis.hset(config_key, :resettable, resettable) Split.redis.hset(config_key, :algorithm, algorithm.to_s) self end |
#start_time ⇒ Object
64 65 66 67 |
# File 'lib/split/experiment.rb', line 64 def start_time t = Split.redis.hget(:experiment_start_times, @name) Time.parse(t) if t end |
#version ⇒ Object
93 94 95 |
# File 'lib/split/experiment.rb', line 93 def version @version ||= (Split.redis.get("#{name.to_s}:version").to_i || 0) end |
#winner ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/split/experiment.rb', line 40 def winner if w = Split.redis.hget(:experiment_winner, name) Split::Alternative.new(w, name) else nil end end |