Class: Laboratory::Experiment
- Inherits:
-
Object
- Object
- Laboratory::Experiment
- Defined in:
- lib/laboratory/experiment.rb,
lib/laboratory/experiment/event.rb,
lib/laboratory/experiment/variant.rb,
lib/laboratory/experiment/changelog_item.rb,
lib/laboratory/experiment/event/recording.rb,
lib/laboratory/experiment/analysis_summary.rb
Overview
rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: AnalysisSummary, ChangelogItem, ClashingExperimentIdError, Event, IncorrectPercentageTotalError, InvalidExperimentVariantFormatError, MissingExperimentAlgorithmError, MissingExperimentIdError, UserNotInExperimentError, Variant
Instance Attribute Summary collapse
-
#_original_algorithm ⇒ Object
readonly
Returns the value of attribute _original_algorithm.
-
#_original_id ⇒ Object
readonly
Returns the value of attribute _original_id.
-
#algorithm ⇒ Object
Returns the value of attribute algorithm.
-
#changelog ⇒ Object
readonly
Returns the value of attribute changelog.
-
#id ⇒ Object
Returns the value of attribute id.
-
#variants ⇒ Object
readonly
Returns the value of attribute variants.
Class Method Summary collapse
- .all ⇒ Object
- .clear_overrides! ⇒ Object
- .create(id:, variants:, algorithm: Algorithms::Random) ⇒ Object
- .find(id) ⇒ Object
- .find_or_create(id:, variants:, algorithm: Algorithms::Random) ⇒ Object
- .override!(overrides) ⇒ Object
- .overrides ⇒ Object
Instance Method Summary collapse
- #analysis_summary_for(event_id) ⇒ Object
- #assign_to_variant(variant_id, user: Laboratory.config.current_user) ⇒ Object
- #delete ⇒ Object
-
#initialize(id:, variants:, algorithm: Algorithms::Random, changelog: []) ⇒ Experiment
constructor
rubocop:disable Metrics/MethodLength.
-
#record_event!(event_id, user: Laboratory.config.current_user) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength.
- #reset ⇒ Object
- #save ⇒ Object
-
#valid? ⇒ Boolean
rubocop:disable Metrics/AbcSize.
-
#variant(user: Laboratory.config.current_user) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength.
Constructor Details
#initialize(id:, variants:, algorithm: Algorithms::Random, changelog: []) ⇒ Experiment
rubocop:disable Metrics/MethodLength
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/laboratory/experiment.rb', line 32 def initialize(id:, variants:, algorithm: Algorithms::Random, changelog: []) # rubocop:disable Metrics/MethodLength @id = id @algorithm = algorithm @changelog = changelog # We want to allow users to input Variant objects, or simple hashes. # This also helps when decoding from adapters @variants = if variants.all? { |variant| variant.instance_of?(Experiment::Variant) } variants elsif variants.all? { |variant| variant.instance_of?(Hash) } variants.map do |variant| Variant.new( id: variant[:id], percentage: variant[:percentage], participant_ids: [], events: [] ) end end @_original_id = id @_original_algorithm = algorithm end |
Instance Attribute Details
#_original_algorithm ⇒ Object (readonly)
Returns the value of attribute _original_algorithm.
25 26 27 |
# File 'lib/laboratory/experiment.rb', line 25 def _original_algorithm @_original_algorithm end |
#_original_id ⇒ Object (readonly)
Returns the value of attribute _original_id.
25 26 27 |
# File 'lib/laboratory/experiment.rb', line 25 def _original_id @_original_id end |
#algorithm ⇒ Object
Returns the value of attribute algorithm.
24 25 26 |
# File 'lib/laboratory/experiment.rb', line 24 def algorithm @algorithm end |
#changelog ⇒ Object (readonly)
Returns the value of attribute changelog.
25 26 27 |
# File 'lib/laboratory/experiment.rb', line 25 def changelog @changelog end |
#id ⇒ Object
Returns the value of attribute id.
24 25 26 |
# File 'lib/laboratory/experiment.rb', line 24 def id @id end |
#variants ⇒ Object (readonly)
Returns the value of attribute variants.
25 26 27 |
# File 'lib/laboratory/experiment.rb', line 25 def variants @variants end |
Class Method Details
.all ⇒ Object
58 59 60 |
# File 'lib/laboratory/experiment.rb', line 58 def self.all Laboratory.adapter.read_all end |
.clear_overrides! ⇒ Object
12 13 14 |
# File 'lib/laboratory/experiment.rb', line 12 def clear_overrides! Thread.current[:experiment_overrides] = {} end |
.create(id:, variants:, algorithm: Algorithms::Random) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/laboratory/experiment.rb', line 62 def self.create(id:, variants:, algorithm: Algorithms::Random) raise ClashingExperimentIdError if find(id) experiment = Experiment.new( id: id, variants: variants, algorithm: algorithm ) experiment.save experiment end |
.find(id) ⇒ Object
75 76 77 |
# File 'lib/laboratory/experiment.rb', line 75 def self.find(id) Laboratory.adapter.read(id) end |
.find_or_create(id:, variants:, algorithm: Algorithms::Random) ⇒ Object
79 80 81 |
# File 'lib/laboratory/experiment.rb', line 79 def self.find_or_create(id:, variants:, algorithm: Algorithms::Random) find(id) || create(id: id, variants: variants, algorithm: algorithm) end |
.override!(overrides) ⇒ Object
8 9 10 |
# File 'lib/laboratory/experiment.rb', line 8 def override!(overrides) Thread.current[:experiment_overrides] = overrides end |
.overrides ⇒ Object
4 5 6 |
# File 'lib/laboratory/experiment.rb', line 4 def overrides Thread.current[:experiment_overrides] || {} end |
Instance Method Details
#analysis_summary_for(event_id) ⇒ Object
156 157 158 |
# File 'lib/laboratory/experiment.rb', line 156 def analysis_summary_for(event_id) Experiment::AnalysisSummary.new(self, event_id) end |
#assign_to_variant(variant_id, user: Laboratory.config.current_user) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/laboratory/experiment.rb', line 119 def assign_to_variant(variant_id, user: Laboratory.config.current_user) variants.each do |variant| variant.participant_ids.delete(user.id) end variant = variants.find { |s| s.id == variant_id } variant.add_participant(user) Laboratory.config.on_assignment_to_variant&.call(self, variant, user) save variant end |
#delete ⇒ Object
83 84 85 86 |
# File 'lib/laboratory/experiment.rb', line 83 def delete Laboratory.adapter.delete(id) nil end |
#record_event!(event_id, user: Laboratory.config.current_user) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/laboratory/experiment.rb', line 133 def record_event!(event_id, user: Laboratory.config.current_user) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength variant = variants.find { |s| s.participant_ids.include?(user.id) } raise UserNotInExperimentError unless variant maybe_event = variant.events.find { |event| event.id == event_id } event = if !maybe_event.nil? maybe_event else e = Event.new(id: event_id) variant.events << e e end event_recording = Event::Recording.new(user_id: user.id) event.event_recordings << event_recording Laboratory.config.on_event_recorded&.call(self, variant, user, event) save event_recording end |
#reset ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/laboratory/experiment.rb', line 88 def reset @variants = variants.map do |variant| Variant.new( id: variant.id, percentage: variant.percentage, participant_ids: [], events: [] ) end save end |
#save ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/laboratory/experiment.rb', line 160 def save raise errors.first unless valid? unless changeset.empty? changelog_item = Laboratory::Experiment::ChangelogItem.new( changes: changeset, timestamp: Time.now, actor: Laboratory.config.actor ) @changelog << changelog_item end Laboratory.adapter.write(self) end |
#valid? ⇒ Boolean
rubocop:disable Metrics/AbcSize
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/laboratory/experiment.rb', line 175 def valid? # rubocop:disable Metrics/AbcSize valid_variants = variants.all? do |variant| !variant.id.nil? && !variant.percentage.nil? end valid_percentage_amounts = variants.map(&:percentage).sum == 100 !id.nil? && !algorithm.nil? && valid_variants && valid_percentage_amounts end |
#variant(user: Laboratory.config.current_user) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/laboratory/experiment.rb', line 100 def variant(user: Laboratory.config.current_user) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength return variant_overridden_with if overridden? selected_variant = variants.find do |variant| variant.participant_ids.include?(user.id) end return selected_variant unless selected_variant.nil? variant = algorithm.pick!(variants) variant.add_participant(user) Laboratory.config.on_assignment_to_variant&.call(self, variant, user) save variant end |