Class: Vanity::Experiment::Base
- Inherits:
-
Object
- Object
- Vanity::Experiment::Base
- Defined in:
- lib/vanity/experiment/base.rb
Overview
Base class that all experiment types are derived from.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#id ⇒ Object
readonly
Unique identifier, derived from name experiment name, e.g.
-
#name ⇒ Object
(also: #to_s)
readonly
Human readable experiment name (first argument you pass when creating a new experiment).
-
#playground ⇒ Object
readonly
Returns the value of attribute playground.
Class Method Summary collapse
-
.load(playground, stack, file) ⇒ Object
Playground uses this to load experiment definitions.
-
.type ⇒ Object
Returns the type of this class as a symbol (e.g. AbTest becomes ab_test).
Instance Method Summary collapse
-
#active? ⇒ Boolean
Returns true if experiment active, false if completed.
-
#complete!(_outcome = nil) ⇒ Object
Force experiment to complete.
-
#complete_if(&block) ⇒ Object
Define experiment completion condition.
-
#completed_at ⇒ Object
Time stamp when experiment was completed.
-
#created_at ⇒ Object
Time stamp when experiment was created.
-
#description(text = nil) ⇒ Object
Sets or returns description.
-
#destroy ⇒ Object
Get rid of all experiment data.
-
#identify(&block) ⇒ Object
Defines how we obtain an identity for the current experiment.
-
#initialize(playground, id, name, options = nil) ⇒ Base
constructor
A new instance of Base.
-
#on_assignment(&block) ⇒ Object
Defines any additional actions to take when a new assignment is made for the current experiment.
-
#reject(&block) ⇒ Object
Define an experiment specific request filter.
-
#save ⇒ Object
Called by Playground to save the experiment definition.
-
#type ⇒ Object
Returns the type of this experiment as a symbol (e.g. :ab_test).
Constructor Details
#initialize(playground, id, name, options = nil) ⇒ Base
Returns a new instance of Base.
38 39 40 41 42 43 44 45 |
# File 'lib/vanity/experiment/base.rb', line 38 def initialize(playground, id, name, = nil) @playground = playground @id = id.to_sym @name = name @options = || {} @identify_block = method(:default_identify) @on_assignment_block = nil end |
Instance Attribute Details
#id ⇒ Object (readonly)
Unique identifier, derived from name experiment name, e.g. “Green Button” becomes :green_button.
54 55 56 |
# File 'lib/vanity/experiment/base.rb', line 54 def id @id end |
#name ⇒ Object (readonly) Also known as: to_s
Human readable experiment name (first argument you pass when creating a new experiment).
49 50 51 |
# File 'lib/vanity/experiment/base.rb', line 49 def name @name end |
#playground ⇒ Object (readonly)
Returns the value of attribute playground.
56 57 58 |
# File 'lib/vanity/experiment/base.rb', line 56 def playground @playground end |
Class Method Details
.load(playground, stack, file) ⇒ Object
Playground uses this to load experiment definitions.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/vanity/experiment/base.rb', line 15 def load(playground, stack, file) raise "Circular dependency detected: #{stack.join('=>')}=>#{file}" if stack.include?(file) source = File.read(file) stack.push file id = File.basename(file, ".rb").downcase.gsub(/\W/, "_").to_sym context = Object.new context.instance_eval do extend Definition experiment = eval(source, context.new_binding(playground, id), file) # rubocop:todo Security/Eval raise NameError.new("Expected #{file} to define experiment #{id}", id) unless playground.experiments[id] return experiment end rescue StandardError error = NameError.exception($!., id) error.set_backtrace $!.backtrace raise error ensure stack.pop end |
.type ⇒ Object
Returns the type of this class as a symbol (e.g. AbTest becomes ab_test).
10 11 12 |
# File 'lib/vanity/experiment/base.rb', line 10 def type name.split("::").last.gsub(/([a-z])([A-Z])/) { "#{Regexp.last_match(1)}_#{Regexp.last_match(2)}" }.gsub(/([A-Z])([A-Z][a-z])/) { "#{Regexp.last_match(1)}_#{Regexp.last_match(2)}" }.downcase end |
Instance Method Details
#active? ⇒ Boolean
Returns true if experiment active, false if completed.
145 146 147 |
# File 'lib/vanity/experiment/base.rb', line 145 def active? !@playground.collecting? || !connection.is_experiment_completed?(@id) end |
#complete!(_outcome = nil) ⇒ Object
Force experiment to complete. outcome of the experiment
131 132 133 134 135 136 137 |
# File 'lib/vanity/experiment/base.rb', line 131 def complete!(_outcome = nil) @playground.logger.info "vanity: completed experiment #{id}" return unless @playground.collecting? connection.set_experiment_completed_at @id, Time.now @completed_at = connection.get_experiment_completed_at(@id) end |
#complete_if(&block) ⇒ Object
Define experiment completion condition. For example:
complete_if do
!score(95).chosen.nil?
end
121 122 123 124 125 126 |
# File 'lib/vanity/experiment/base.rb', line 121 def complete_if(&block) raise ArgumentError, "Missing block" unless block raise "complete_if already called on this experiment" if defined?(@complete_block) @complete_block = block end |
#completed_at ⇒ Object
Time stamp when experiment was completed.
140 141 142 |
# File 'lib/vanity/experiment/base.rb', line 140 def completed_at @completed_at ||= connection.get_experiment_completed_at(@id) end |
#created_at ⇒ Object
Time stamp when experiment was created.
59 60 61 |
# File 'lib/vanity/experiment/base.rb', line 59 def created_at @created_at ||= connection.get_experiment_created_at(@id) end |
#description(text = nil) ⇒ Object
Sets or returns description. For example
ab_test "Simple" do
description "A simple A/B experiment"
end
puts "Just defined: " + experiment(:simple).description
110 111 112 113 |
# File 'lib/vanity/experiment/base.rb', line 110 def description(text = nil) @description = text if text @description if defined?(@description) end |
#destroy ⇒ Object
Get rid of all experiment data.
152 153 154 155 |
# File 'lib/vanity/experiment/base.rb', line 152 def destroy connection.destroy_experiment @id @created_at = @completed_at = nil end |
#identify(&block) ⇒ Object
Defines how we obtain an identity for the current experiment. Usually Vanity gets the identity form a session object (see use_vanity), but there are cases where you want a particular experiment to use a different identity.
For example, if all your experiments use current_user and you need one experiment to use the current project:
ab_test "Project widget" do
alternatives :small, :medium, :large
identify do |controller|
controller.project.id
end
end
81 82 83 84 85 |
# File 'lib/vanity/experiment/base.rb', line 81 def identify(&block) raise "Missing block" unless block @identify_block = block end |
#on_assignment(&block) ⇒ Object
Defines any additional actions to take when a new assignment is made for the current experiment
For example, if you want to use the rails default logger to log whenever an assignment is made:
ab_test "Project widget" do
alternatives :small, :medium, :large
on_assignment do |controller, identity, assignment|
controller.logger.info "made a split test assignment for #{experiment.name}: #{identity} => #{assignment}"
end
end
96 97 98 99 100 |
# File 'lib/vanity/experiment/base.rb', line 96 def on_assignment(&block) raise "Missing block" unless block @on_assignment_block = block end |
#reject(&block) ⇒ Object
172 173 174 175 176 177 |
# File 'lib/vanity/experiment/base.rb', line 172 def reject(&block) raise "Missing block" unless block raise "filter already called on this experiment" if @request_filter_block @request_filter_block = block end |
#save ⇒ Object
Called by Playground to save the experiment definition.
158 159 160 161 162 |
# File 'lib/vanity/experiment/base.rb', line 158 def save return unless @playground.collecting? connection.set_experiment_created_at @id, Time.now end |
#type ⇒ Object
Returns the type of this experiment as a symbol (e.g. :ab_test).
64 65 66 |
# File 'lib/vanity/experiment/base.rb', line 64 def type self.class.type end |