Class: TrailGuide::Variant

Inherits:
Object
  • Object
show all
Defined in:
lib/trail_guide/variant.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(experiment, name, metadata: {}, weight: 1, control: false) ⇒ Variant

Returns a new instance of Variant.



9
10
11
12
13
14
15
# File 'lib/trail_guide/variant.rb', line 9

def initialize(experiment, name, metadata: {}, weight: 1, control: false)
  @experiment = experiment
  @name = name.to_s.underscore.to_sym
  @metadata = 
  @weight = weight
  @control = control
end

Instance Attribute Details

#experimentObject (readonly)

Returns the value of attribute experiment.



3
4
5
# File 'lib/trail_guide/variant.rb', line 3

def experiment
  @experiment
end

#metadataObject (readonly)

Returns the value of attribute metadata.



3
4
5
# File 'lib/trail_guide/variant.rb', line 3

def 
  @metadata
end

#nameObject (readonly)

Returns the value of attribute name.



3
4
5
# File 'lib/trail_guide/variant.rb', line 3

def name
  @name
end

#weightObject (readonly)

Returns the value of attribute weight.



3
4
5
# File 'lib/trail_guide/variant.rb', line 3

def weight
  @weight
end

Instance Method Details

#==(other) ⇒ Object



21
22
23
24
25
26
27
28
29
# File 'lib/trail_guide/variant.rb', line 21

def ==(other)
  if other.is_a?(self.class)
    # TODO eventually remove the experiment requirement here once we start
    # taking advantage of === below
    return name == other.name && experiment == other.experiment
  elsif other.is_a?(String) || other.is_a?(Symbol)
    return name == other.to_s.underscore.to_sym
  end
end

#===(other) ⇒ Object



31
32
33
34
# File 'lib/trail_guide/variant.rb', line 31

def ===(other)
  return false unless other.is_a?(self.class)
  return name == other.name && experiment == other.experiment
end

#adapterObject



17
18
19
# File 'lib/trail_guide/variant.rb', line 17

def adapter
  @adapter ||= TrailGuide::Adapters::Variants::Redis.new(self)
end

#as_json(opts = {}) ⇒ Object

export the variant state (not config) as json



115
116
117
118
119
120
121
122
123
124
# File 'lib/trail_guide/variant.rb', line 115

def as_json(opts={})
  if experiment.goals.empty?
    conversions = converted
  else
    conversions = experiment.goals.map { |g| [g.name, converted(g)] }.to_h
  end

  { name => { participants: participants,
              converted: conversions } }
end

#control!Object

mark this variant as the control



40
41
42
# File 'lib/trail_guide/variant.rb', line 40

def control!
  @control = true
end

#control?Boolean

check if this variant is the control

Returns:

  • (Boolean)


45
46
47
# File 'lib/trail_guide/variant.rb', line 45

def control?
  !!@control
end

#converted(checkpoint = nil) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/trail_guide/variant.rb', line 75

def converted(checkpoint=nil)
  if experiment.goals.empty?
    raise InvalidGoalError, "You provided the checkpoint `#{checkpoint}` but the experiment `#{experiment.experiment_name}` does not have any goals defined." unless checkpoint.nil?
    (adapter.get(:converted) || 0).to_i
  elsif !checkpoint.nil?
    goal = experiment.goals.find { |g| g == checkpoint }
    raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for experiment `#{experiment.experiment_name}`." if goal.nil?
    (adapter.get(goal.name) || 0).to_i
  else
    experiment.goals.sum do |goal|
      (adapter.get(goal.name) || 0).to_i
    end
  end
end

#delete!Object



62
63
64
# File 'lib/trail_guide/variant.rb', line 62

def delete!
  adapter.destroy
end

#dup(experiment) ⇒ Object



5
6
7
# File 'lib/trail_guide/variant.rb', line 5

def dup(experiment)
  self.class.new(experiment, name, metadata: , weight: weight, control: control?)
end

#increment_conversion!(checkpoint = nil) ⇒ Object



105
106
107
108
109
110
111
112
# File 'lib/trail_guide/variant.rb', line 105

def increment_conversion!(checkpoint=nil)
  if checkpoint.nil?
    checkpoint = :converted
  else
    checkpoint = experiment.goals.find { |g| g == checkpoint }.name
  end
  adapter.increment(checkpoint)
end

#increment_participation!Object



101
102
103
# File 'lib/trail_guide/variant.rb', line 101

def increment_participation!
  adapter.increment(:participants)
end

#measure(goal = nil, against = nil) ⇒ Object



94
95
96
97
98
99
# File 'lib/trail_guide/variant.rb', line 94

def measure(goal=nil, against=nil)
  superset = against ? converted(against) : participants
  converts = converted(goal)
  return 0 if superset.zero? || converts.zero?
  converts.to_f / superset.to_f
end

#participantsObject



71
72
73
# File 'lib/trail_guide/variant.rb', line 71

def participants
  (adapter.get(:participants) || 0).to_i
end

#persisted?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/trail_guide/variant.rb', line 54

def persisted?
  adapter.persisted?
end

#reset!Object



66
67
68
69
# File 'lib/trail_guide/variant.rb', line 66

def reset!
  delete!
  save!
end

#save!Object



58
59
60
# File 'lib/trail_guide/variant.rb', line 58

def save!
  adapter.setnx(:name, name)
end

#storage_keyObject



130
131
132
# File 'lib/trail_guide/variant.rb', line 130

def storage_key
  "#{experiment.experiment_name}:#{name}"
end

#to_sObject



126
127
128
# File 'lib/trail_guide/variant.rb', line 126

def to_s
  name.to_s
end

#unconvertedObject



90
91
92
# File 'lib/trail_guide/variant.rb', line 90

def unconverted
  participants - converted
end

#variant!Object

unmark this variant as the control



50
51
52
# File 'lib/trail_guide/variant.rb', line 50

def variant!
  @control = false
end