Class: TrailGuide::Experiments::Config

Inherits:
Canfig::Config
  • Object
show all
Defined in:
lib/trail_guide/experiments/config.rb

Direct Known Subclasses

CombinedConfig

Constant Summary collapse

DEFAULT_KEYS =
[
  :name, :summary, :preview_url, :algorithm, :groups, :variants, :goals,
  :start_manually, :reset_manually, :store_participation, :store_override,
  :track_override, :combined, :allow_multiple_conversions,
  :allow_multiple_goals, :track_winner_conversions, :skip_request_filter,
  :target_sample_size, :can_resume, :enable_calibration
].freeze
CALLBACK_KEYS =
[
  :on_start, :on_schedule, :on_stop, :on_pause, :on_resume, :on_winner,
  :on_reset, :on_delete, :on_choose, :on_use, :on_convert,
  :on_redis_failover, :allow_participation, :allow_conversion,
  :rollout_winner
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(experiment, *args, **opts, &block) ⇒ Config

Returns a new instance of Config.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/trail_guide/experiments/config.rb', line 37

def initialize(experiment, *args, **opts, &block)
  @experiment = experiment
  opts = opts.merge(default_config)
  ancestor = opts.delete(:inherit)
  if ancestor.present?
    keys = opts.keys.dup.concat(args).concat(DEFAULT_KEYS).concat(CALLBACK_KEYS).uniq
    opts = opts.merge(ancestor.to_h.slice(*keys))
    opts[:name] = nil
    opts[:goals] = ancestor.goals.dup
    opts[:combined] = ancestor.combined.dup
    opts[:variants] = ancestor.variants.map { |var| var.dup(experiment) }
    opts = opts.merge(ancestor.callbacks.map { |k,v| [k,[v].flatten.compact] }.to_h)
  end
  super(*args, **opts, &block)
end

Instance Attribute Details

#experimentObject (readonly)

Returns the value of attribute experiment.



35
36
37
# File 'lib/trail_guide/experiments/config.rb', line 35

def experiment
  @experiment
end

Instance Method Details

#algorithmObject



118
119
120
# File 'lib/trail_guide/experiments/config.rb', line 118

def algorithm
  @algorithm ||= TrailGuide::Algorithms.algorithm(self[:algorithm])
end

#allow_conversion(meth = nil, &block) ⇒ Object



271
272
273
274
# File 'lib/trail_guide/experiments/config.rb', line 271

def allow_conversion(meth=nil, &block)
  self[:allow_conversion] ||= []
  self[:allow_conversion] << (meth || block)
end

#allow_multiple_conversions?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/trail_guide/experiments/config.rb', line 65

def allow_multiple_conversions?
  !!allow_multiple_conversions
end

#allow_multiple_goals?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/trail_guide/experiments/config.rb', line 69

def allow_multiple_goals?
  !!allow_multiple_goals
end

#allow_participation(meth = nil, &block) ⇒ Object



266
267
268
269
# File 'lib/trail_guide/experiments/config.rb', line 266

def allow_participation(meth=nil, &block)
  self[:allow_participation] ||= []
  self[:allow_participation] << (meth || block)
end

#callback_configObject



29
30
31
32
33
# File 'lib/trail_guide/experiments/config.rb', line 29

def callback_config
  CALLBACK_KEYS.map do |key|
    [key, []]
  end.to_h
end

#callbacksObject



202
203
204
# File 'lib/trail_guide/experiments/config.rb', line 202

def callbacks
  to_h.slice(*CALLBACK_KEYS).map { |k,v| [k, [v].flatten.compact] }.to_h
end

#can_resume?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/trail_guide/experiments/config.rb', line 81

def can_resume?
  !!can_resume
end

#combined?Boolean

Returns:

  • (Boolean)


194
195
196
# File 'lib/trail_guide/experiments/config.rb', line 194

def combined?
  !combined.empty?
end

#controlObject



131
132
133
# File 'lib/trail_guide/experiments/config.rb', line 131

def control
  return variants.find { |var| var.control? } || variants.first
end

#control=(name) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/trail_guide/experiments/config.rb', line 135

def control=(name)
  variants.each(&:variant!)
  var_idx = variants.index { |var| var == name }

  if var_idx.nil?
    variant = Variant.new(experiment, name, control: true)
    variants.push(variant)
  else
    variant = variants[var_idx]
    variant.control!
  end

  variant
end

#default_configObject



19
20
21
22
23
24
25
26
27
# File 'lib/trail_guide/experiments/config.rb', line 19

def default_config
  DEFAULT_KEYS.map do |key|
    [key, nil]
  end.to_h.merge({
    variants: [],
    goals: [],
    combined: []
  }).merge(callback_config)
end

#enable_calibration?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/trail_guide/experiments/config.rb', line 85

def enable_calibration?
  !!enable_calibration
end

#goal(name, **config, &block) ⇒ Object Also known as: funnel



150
151
152
# File 'lib/trail_guide/experiments/config.rb', line 150

def goal(name, **config, &block)
  goals << Metrics::Goal.new(experiment, name, **config, &block)
end

#goal=(name) ⇒ Object Also known as: funnel=



155
156
157
# File 'lib/trail_guide/experiments/config.rb', line 155

def goal=(name)
  goals << Metrics::Goal.new(experiment, name)
end

#goals(*names, **config, &block) ⇒ Object Also known as: funnels



165
166
167
168
169
170
171
# File 'lib/trail_guide/experiments/config.rb', line 165

def goals(*names, **config, &block)
  self[:goals] ||= []
  unless names.empty?
    self[:goals] = self[:goals].concat([names].flatten.map { |g| Metrics::Goal.new(experiment, g, **config, &block) })
  end
  self[:goals]
end

#goals=(*names) ⇒ Object Also known as: funnels=



160
161
162
# File 'lib/trail_guide/experiments/config.rb', line 160

def goals=(*names)
  self[:goals] = [names].flatten.map { |g| Metrics::Goal.new(experiment, g) }
end

#group(grp = nil) ⇒ Object



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

def group(grp=nil)
  unless grp.nil?
    groups << grp.to_s.underscore.to_sym
    return groups.last
  end
  groups.first
end

#group=(grp) ⇒ Object



113
114
115
116
# File 'lib/trail_guide/experiments/config.rb', line 113

def group=(grp)
  groups.unshift(grp.to_s.underscore.to_sym)
  groups.first
end

#groups(*grps) ⇒ Object



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

def groups(*grps)
  self[:groups] ||= []
  unless grps.empty?
    self[:groups] = self[:groups].concat([grps].flatten.map { |g| g.to_s.underscore.to_sym })
  end
  self[:groups]
end

#groups=(*grps) ⇒ Object



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

def groups=(*grps)
  self[:groups] = [grps].flatten.map { |g| g.to_s.underscore.to_sym }
end

#metric(name, **config, &block) ⇒ Object



174
175
176
177
# File 'lib/trail_guide/experiments/config.rb', line 174

def metric(name, **config, &block)
  group(name)
  goal(name, **config, &block)
end

#metric=(name) ⇒ Object



179
180
181
182
# File 'lib/trail_guide/experiments/config.rb', line 179

def metric=(name)
  self.group = name
  self.goal = name
end

#metrics(*names, **config, &block) ⇒ Object



184
185
186
187
# File 'lib/trail_guide/experiments/config.rb', line 184

def metrics(*names, **config, &block)
  groups(*names)
  goals(*names, **config, &block)
end

#metrics=(*names) ⇒ Object



189
190
191
192
# File 'lib/trail_guide/experiments/config.rb', line 189

def metrics=(*names)
  self.groups = *names
  self.goals = *names
end

#nameObject



89
90
91
# File 'lib/trail_guide/experiments/config.rb', line 89

def name
  @name ||= (self[:name] || experiment.name).try(:to_s).try(:underscore).try(:to_sym)
end

#on_choose(meth = nil, &block) ⇒ Object



206
207
208
209
# File 'lib/trail_guide/experiments/config.rb', line 206

def on_choose(meth=nil, &block)
  self[:on_choose] ||= []
  self[:on_choose] << (meth || block)
end

#on_convert(meth = nil, &block) ⇒ Object



216
217
218
219
# File 'lib/trail_guide/experiments/config.rb', line 216

def on_convert(meth=nil, &block)
  self[:on_convert] ||= []
  self[:on_convert] << (meth || block)
end

#on_delete(meth = nil, &block) ⇒ Object



256
257
258
259
# File 'lib/trail_guide/experiments/config.rb', line 256

def on_delete(meth=nil, &block)
  self[:on_delete] ||= []
  self[:on_delete] << (meth || block)
end

#on_pause(meth = nil, &block) ⇒ Object



236
237
238
239
# File 'lib/trail_guide/experiments/config.rb', line 236

def on_pause(meth=nil, &block)
  self[:on_pause] ||= []
  self[:on_pause] << (meth || block)
end

#on_redis_failover(meth = nil, &block) ⇒ Object



261
262
263
264
# File 'lib/trail_guide/experiments/config.rb', line 261

def on_redis_failover(meth=nil, &block)
  self[:on_redis_failover] ||= []
  self[:on_redis_failover] << (meth || block)
end

#on_reset(meth = nil, &block) ⇒ Object



251
252
253
254
# File 'lib/trail_guide/experiments/config.rb', line 251

def on_reset(meth=nil, &block)
  self[:on_reset] ||= []
  self[:on_reset] << (meth || block)
end

#on_resume(meth = nil, &block) ⇒ Object



241
242
243
244
# File 'lib/trail_guide/experiments/config.rb', line 241

def on_resume(meth=nil, &block)
  self[:on_resume] ||= []
  self[:on_resume] << (meth || block)
end

#on_schedule(meth = nil, &block) ⇒ Object



226
227
228
229
# File 'lib/trail_guide/experiments/config.rb', line 226

def on_schedule(meth=nil, &block)
  self[:on_schedule] ||= []
  self[:on_schedule] << (meth || block)
end

#on_start(meth = nil, &block) ⇒ Object



221
222
223
224
# File 'lib/trail_guide/experiments/config.rb', line 221

def on_start(meth=nil, &block)
  self[:on_start] ||= []
  self[:on_start] << (meth || block)
end

#on_stop(meth = nil, &block) ⇒ Object



231
232
233
234
# File 'lib/trail_guide/experiments/config.rb', line 231

def on_stop(meth=nil, &block)
  self[:on_stop] ||= []
  self[:on_stop] << (meth || block)
end

#on_use(meth = nil, &block) ⇒ Object



211
212
213
214
# File 'lib/trail_guide/experiments/config.rb', line 211

def on_use(meth=nil, &block)
  self[:on_use] ||= []
  self[:on_use] << (meth || block)
end

#on_winner(meth = nil, &block) ⇒ Object



246
247
248
249
# File 'lib/trail_guide/experiments/config.rb', line 246

def on_winner(meth=nil, &block)
  self[:on_winner] ||= []
  self[:on_winner] << (meth || block)
end

#preview_url?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/trail_guide/experiments/config.rb', line 198

def preview_url?
  !!preview_url
end

#reset_manually?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/trail_guide/experiments/config.rb', line 57

def reset_manually?
  !!reset_manually
end

#rollout_winner(meth = nil, &block) ⇒ Object



276
277
278
279
# File 'lib/trail_guide/experiments/config.rb', line 276

def rollout_winner(meth=nil, &block)
  self[:rollout_winner] ||= []
  self[:rollout_winner] << (meth || block)
end

#skip_request_filter?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/trail_guide/experiments/config.rb', line 77

def skip_request_filter?
  !!skip_request_filter
end

#start_manually?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/trail_guide/experiments/config.rb', line 53

def start_manually?
  !!start_manually
end

#store_participation?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/trail_guide/experiments/config.rb', line 61

def store_participation?
  !!store_participation
end

#track_winner_conversions?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/trail_guide/experiments/config.rb', line 73

def track_winner_conversions?
  !!track_winner_conversions
end

#variant(varname, metadata: {}, weight: 1, control: false) ⇒ Object

Raises:

  • (ArgumentError)


122
123
124
125
126
127
128
129
# File 'lib/trail_guide/experiments/config.rb', line 122

def variant(varname, metadata: {}, weight: 1, control: false)
  raise ArgumentError, "The variant `#{varname}` already exists in the experiment `#{name}`" if variants.any? { |var| var == varname }
  control = true if variants.empty?
  variants.each(&:variant!) if control
  variant = Variant.new(experiment, varname, metadata: , weight: weight, control: control)
  variants << variant
  variant
end