trailguide 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/trail_guide/admin/application_controller.rb +1 -1
- data/app/controllers/trail_guide/admin/experiments_controller.rb +29 -6
- data/app/views/trail_guide/admin/experiments/_combined_experiment.html.erb +129 -0
- data/app/views/trail_guide/admin/experiments/_experiment.html.erb +35 -10
- data/app/views/trail_guide/admin/experiments/index.html.erb +6 -2
- data/config/routes.rb +5 -0
- data/lib/trail_guide/catalog.rb +42 -2
- data/lib/trail_guide/combined_experiment.rb +48 -0
- data/lib/trail_guide/engine.rb +1 -0
- data/lib/trail_guide/experiment.rb +4 -237
- data/lib/trail_guide/experiments/base.rb +268 -0
- data/lib/trail_guide/experiments/combined_config.rb +12 -0
- data/lib/trail_guide/experiments/config.rb +158 -0
- data/lib/trail_guide/helper.rb +1 -1
- data/lib/trail_guide/participant.rb +19 -4
- data/lib/trail_guide/version.rb +1 -1
- data/lib/trailguide.rb +5 -0
- metadata +7 -3
- data/lib/trail_guide/experiment_config.rb +0 -137
@@ -38,7 +38,7 @@ module TrailGuide
|
|
38
38
|
return false unless variant && adapter.key?(variant.storage_key)
|
39
39
|
|
40
40
|
chosen_at = Time.at(adapter[variant.storage_key].to_i)
|
41
|
-
chosen_at >= experiment.started_at
|
41
|
+
return variant if chosen_at >= experiment.started_at
|
42
42
|
end
|
43
43
|
|
44
44
|
def converted?(experiment, checkpoint=nil)
|
@@ -68,6 +68,10 @@ module TrailGuide
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
def variant(experiment)
|
72
|
+
participating?(experiment) || nil
|
73
|
+
end
|
74
|
+
|
71
75
|
def participating!(variant)
|
72
76
|
adapter[variant.experiment.storage_key] = variant.name
|
73
77
|
adapter[variant.storage_key] = Time.now.to_i
|
@@ -81,7 +85,7 @@ module TrailGuide
|
|
81
85
|
adapter.delete(variant.experiment.storage_key)
|
82
86
|
adapter.delete(variant.storage_key)
|
83
87
|
adapter.delete(storage_key)
|
84
|
-
experiment.funnels.each do |funnel|
|
88
|
+
variant.experiment.funnels.each do |funnel|
|
85
89
|
funnel_key = "#{variant.experiment.storage_key}:#{funnel.to_s}"
|
86
90
|
adapter.delete(funnel_key)
|
87
91
|
end
|
@@ -90,11 +94,22 @@ module TrailGuide
|
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
97
|
+
def exit!(experiment)
|
98
|
+
chosen = variant(experiment)
|
99
|
+
return true if chosen.nil?
|
100
|
+
adapter.delete(experiment.storage_key)
|
101
|
+
adapter.delete(chosen.storage_key)
|
102
|
+
experiment.goals.each do |goal|
|
103
|
+
adapter.delete("#{experiment.storage_key}:#{goal.to_s}")
|
104
|
+
end
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
93
108
|
def active_experiments(include_control=true)
|
94
109
|
return false if adapter.keys.empty?
|
95
110
|
adapter.keys.map { |key| key.to_s.split(":").first.to_sym }.uniq.map do |key|
|
96
111
|
experiment = TrailGuide.catalog.find(key)
|
97
|
-
next unless experiment && experiment.running? && participating?(experiment, include_control)
|
112
|
+
next unless experiment && !experiment.combined? && experiment.running? && participating?(experiment, include_control)
|
98
113
|
[ experiment.experiment_name, adapter[experiment.storage_key] ]
|
99
114
|
end.compact.to_h
|
100
115
|
end
|
@@ -105,7 +120,7 @@ module TrailGuide
|
|
105
120
|
adapter.keys.any? do |key|
|
106
121
|
experiment_name = key.to_s.split(":").first.to_sym
|
107
122
|
experiment = TrailGuide.catalog.find(experiment_name)
|
108
|
-
experiment && experiment.running? && participating?(experiment, include_control)
|
123
|
+
experiment && !experiment.combined? && experiment.running? && participating?(experiment, include_control)
|
109
124
|
end
|
110
125
|
end
|
111
126
|
end
|
data/lib/trail_guide/version.rb
CHANGED
data/lib/trailguide.rb
CHANGED
@@ -6,6 +6,7 @@ require "trail_guide/algorithms"
|
|
6
6
|
require "trail_guide/participant"
|
7
7
|
require "trail_guide/variant"
|
8
8
|
require "trail_guide/experiment"
|
9
|
+
require "trail_guide/combined_experiment"
|
9
10
|
require "trail_guide/catalog"
|
10
11
|
require "trail_guide/helper"
|
11
12
|
require "trail_guide/engine"
|
@@ -24,6 +25,7 @@ module TrailGuide
|
|
24
25
|
config.algorithm = :weighted
|
25
26
|
config.adapter = :multi
|
26
27
|
config.allow_multiple_experiments = true # false / :control
|
28
|
+
config.track_winner_conversions = false
|
27
29
|
config.allow_multiple_conversions = false
|
28
30
|
config.allow_multiple_goals = false
|
29
31
|
|
@@ -34,9 +36,12 @@ module TrailGuide
|
|
34
36
|
config.on_experiment_start = nil # -> (experiment) { ... }
|
35
37
|
config.on_experiment_stop = nil # -> (experiment) { ... }
|
36
38
|
config.on_experiment_resume = nil # -> (experiment) { ... }
|
39
|
+
config.on_experiment_winner = nil # -> (experiment, winner) { ... }
|
37
40
|
config.on_experiment_reset = nil # -> (experiment) { ... }
|
38
41
|
config.on_experiment_delete = nil # -> (experiment) { ... }
|
39
42
|
|
43
|
+
config.return_experiment_winner = nil # -> (experiment, winner) { ... return variant }
|
44
|
+
|
40
45
|
config.filtered_user_agents = []
|
41
46
|
config.filtered_ip_addresses = []
|
42
47
|
config.request_filter = -> (context) do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailguide
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rebec
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- app/controllers/trail_guide/admin/experiments_controller.rb
|
112
112
|
- app/controllers/trail_guide/experiments_controller.rb
|
113
113
|
- app/views/layouts/trail_guide/admin/application.html.erb
|
114
|
+
- app/views/trail_guide/admin/experiments/_combined_experiment.html.erb
|
114
115
|
- app/views/trail_guide/admin/experiments/_experiment.html.erb
|
115
116
|
- app/views/trail_guide/admin/experiments/index.html.erb
|
116
117
|
- config/routes.rb
|
@@ -130,10 +131,13 @@ files:
|
|
130
131
|
- lib/trail_guide/algorithms/random.rb
|
131
132
|
- lib/trail_guide/algorithms/weighted.rb
|
132
133
|
- lib/trail_guide/catalog.rb
|
134
|
+
- lib/trail_guide/combined_experiment.rb
|
133
135
|
- lib/trail_guide/engine.rb
|
134
136
|
- lib/trail_guide/errors.rb
|
135
137
|
- lib/trail_guide/experiment.rb
|
136
|
-
- lib/trail_guide/
|
138
|
+
- lib/trail_guide/experiments/base.rb
|
139
|
+
- lib/trail_guide/experiments/combined_config.rb
|
140
|
+
- lib/trail_guide/experiments/config.rb
|
137
141
|
- lib/trail_guide/helper.rb
|
138
142
|
- lib/trail_guide/participant.rb
|
139
143
|
- lib/trail_guide/unity.rb
|
@@ -1,137 +0,0 @@
|
|
1
|
-
module TrailGuide
|
2
|
-
class ExperimentConfig < Canfig::Config
|
3
|
-
ENGINE_CONFIG_KEYS = [
|
4
|
-
:start_manually, :reset_manually, :store_override, :track_override,
|
5
|
-
:algorithm, :allow_multiple_conversions, :allow_multiple_goals
|
6
|
-
].freeze
|
7
|
-
|
8
|
-
def self.engine_config
|
9
|
-
ENGINE_CONFIG_KEYS.map do |key|
|
10
|
-
[key, TrailGuide.configuration.send(key.to_sym)]
|
11
|
-
end.to_h
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.default_config
|
15
|
-
{ name: nil, metric: nil, variants: [], goals: [] }
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.callbacks_config
|
19
|
-
{
|
20
|
-
callbacks: {
|
21
|
-
on_choose: [TrailGuide.configuration.on_experiment_choose].flatten.compact,
|
22
|
-
on_use: [TrailGuide.configuration.on_experiment_use].flatten.compact,
|
23
|
-
on_convert: [TrailGuide.configuration.on_experiment_convert].flatten.compact,
|
24
|
-
on_start: [TrailGuide.configuration.on_experiment_start].flatten.compact,
|
25
|
-
on_stop: [TrailGuide.configuration.on_experiment_stop].flatten.compact,
|
26
|
-
on_resume: [TrailGuide.configuration.on_experiment_resume].flatten.compact,
|
27
|
-
on_reset: [TrailGuide.configuration.on_experiment_reset].flatten.compact,
|
28
|
-
on_delete: [TrailGuide.configuration.on_experiment_delete].flatten.compact,
|
29
|
-
}
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_reader :experiment
|
34
|
-
|
35
|
-
def initialize(experiment, *args, **opts, &block)
|
36
|
-
@experiment = experiment
|
37
|
-
opts = opts.merge(self.class.engine_config)
|
38
|
-
opts = opts.merge(self.class.default_config)
|
39
|
-
opts = opts.merge(self.class.callbacks_config)
|
40
|
-
super(*args, **opts, &block)
|
41
|
-
end
|
42
|
-
|
43
|
-
def resettable?
|
44
|
-
!reset_manually
|
45
|
-
end
|
46
|
-
|
47
|
-
def allow_multiple_conversions?
|
48
|
-
allow_multiple_conversions
|
49
|
-
end
|
50
|
-
|
51
|
-
def allow_multiple_goals?
|
52
|
-
allow_multiple_goals
|
53
|
-
end
|
54
|
-
|
55
|
-
def name
|
56
|
-
@name ||= (self[:name] || experiment.name).try(:to_s).try(:underscore).try(:to_sym)
|
57
|
-
end
|
58
|
-
|
59
|
-
def metric
|
60
|
-
@metric ||= (self[:metric] || name).try(:to_s).try(:underscore).try(:to_sym)
|
61
|
-
end
|
62
|
-
|
63
|
-
def algorithm
|
64
|
-
@algorithm ||= TrailGuide::Algorithms.algorithm(self[:algorithm])
|
65
|
-
end
|
66
|
-
|
67
|
-
def variant(varname, metadata: {}, weight: 1, control: false)
|
68
|
-
raise ArgumentError, "The variant `#{varname}` already exists in the experiment `#{name}`" if variants.any? { |var| var == varname }
|
69
|
-
control = true if variants.empty?
|
70
|
-
variants.each(&:variant!) if control
|
71
|
-
variant = Variant.new(experiment, varname, metadata: metadata, weight: weight, control: control)
|
72
|
-
variants << variant
|
73
|
-
variant
|
74
|
-
end
|
75
|
-
|
76
|
-
def control
|
77
|
-
return variants.find { |var| var.control? } || variants.first
|
78
|
-
end
|
79
|
-
|
80
|
-
def control=(name)
|
81
|
-
variants.each(&:variant!)
|
82
|
-
var_idx = variants.index { |var| var == name }
|
83
|
-
|
84
|
-
if var_idx.nil?
|
85
|
-
variant = Variant.new(experiment, name, control: true)
|
86
|
-
else
|
87
|
-
variant = variants.slice!(var_idx, 1)[0]
|
88
|
-
variant.control!
|
89
|
-
end
|
90
|
-
|
91
|
-
variants.unshift(variant)
|
92
|
-
variant
|
93
|
-
end
|
94
|
-
|
95
|
-
def goal(name)
|
96
|
-
goals << name.to_s.underscore.to_sym
|
97
|
-
end
|
98
|
-
alias_method :funnel, :goal
|
99
|
-
|
100
|
-
def goals
|
101
|
-
self[:goals]
|
102
|
-
end
|
103
|
-
alias_method :funnels, :goals
|
104
|
-
|
105
|
-
def on_choose(meth=nil, &block)
|
106
|
-
callbacks[:on_choose] << (meth || block)
|
107
|
-
end
|
108
|
-
|
109
|
-
def on_use(meth=nil, &block)
|
110
|
-
callbacks[:on_use] << (meth || block)
|
111
|
-
end
|
112
|
-
|
113
|
-
def on_convert(meth=nil, &block)
|
114
|
-
callbacks[:on_convert] << (meth || block)
|
115
|
-
end
|
116
|
-
|
117
|
-
def on_start(meth=nil, &block)
|
118
|
-
callbacks[:on_start] << (meth || block)
|
119
|
-
end
|
120
|
-
|
121
|
-
def on_stop(meth=nil, &block)
|
122
|
-
callbacks[:on_stop] << (meth || block)
|
123
|
-
end
|
124
|
-
|
125
|
-
def on_resume(meth=nil, &block)
|
126
|
-
callbacks[:on_resume] << (meth || block)
|
127
|
-
end
|
128
|
-
|
129
|
-
def on_reset(meth=nil, &block)
|
130
|
-
callbacks[:on_reset] << (meth || block)
|
131
|
-
end
|
132
|
-
|
133
|
-
def on_delete(meth=nil, &block)
|
134
|
-
callbacks[:on_delete] << (meth || block)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|