trailguide 0.1.20 → 0.1.21
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 +32 -0
- data/app/views/trail_guide/admin/experiments/_combined_experiment.html.erb +18 -6
- data/app/views/trail_guide/admin/experiments/_experiment.html.erb +18 -6
- data/config/initializers/trailguide.rb +26 -0
- data/lib/trail_guide/config.rb +1 -1
- data/lib/trail_guide/engine.rb +4 -2
- data/lib/trail_guide/experiments/base.rb +18 -0
- data/lib/trail_guide/experiments/config.rb +1 -1
- data/lib/trail_guide/experiments/participant.rb +3 -3
- data/lib/trail_guide/participant.rb +8 -3
- data/lib/trail_guide/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec80228061f031c01c0eb4d6dbef24d52ca205ccf2e6137ebe3a18886b095258
|
4
|
+
data.tar.gz: 6fa47e1dd04511cbb35d39a737589a34658fcc9d1f0dde64298908a9ca539f27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca6d1439e8632ec16946dcfc50e5201f24714a568a2b99c234d0ed666db6a6149724aeea7190be7669fe06dc232619cce6ead9211f175c1a518d2b6458dff7d4
|
7
|
+
data.tar.gz: 7ab9d62c95a8c3b0a63fd1b09f0b51bea8d021b98a6797acff0658fbe8a718d5b8665458df8b92b31e89c2e606788d1aa677e96a1ce21dfeb2516e780e8bfacb
|
@@ -15,6 +15,38 @@ module TrailGuide
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
helper_method :preview_url
|
18
|
+
|
19
|
+
def experiment_peekable?(experiment)
|
20
|
+
return false unless TrailGuide::Admin.configuration.peek_parameter
|
21
|
+
return false unless experiment.running?
|
22
|
+
return false if experiment.target_sample_size_reached?
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
helper_method :experiment_peekable?
|
26
|
+
|
27
|
+
def experiment_peeking?(experiment)
|
28
|
+
params[TrailGuide::Admin.configuration.peek_parameter] == experiment.experiment_name.to_s
|
29
|
+
end
|
30
|
+
helper_method :experiment_peeking?
|
31
|
+
|
32
|
+
def experiment_metrics_visible?(experiment)
|
33
|
+
return true unless experiment.running?
|
34
|
+
return true if params[TrailGuide::Admin.configuration.peek_parameter] == experiment.experiment_name.to_s
|
35
|
+
return true if experiment.target_sample_size_reached?
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
helper_method :experiment_metrics_visible?
|
39
|
+
|
40
|
+
def experiment_metric(experiment, metric)
|
41
|
+
return metric if experiment_metrics_visible?(experiment)
|
42
|
+
return '?'
|
43
|
+
end
|
44
|
+
helper_method :experiment_metric
|
45
|
+
|
46
|
+
def peek_url(experiment, *args, **opts)
|
47
|
+
trail_guide_admin.experiments_url(*args, opts.merge({TrailGuide::Admin.configuration.peek_parameter => experiment.experiment_name, anchor: experiment.experiment_name}))
|
48
|
+
end
|
49
|
+
helper_method :peek_url
|
18
50
|
end
|
19
51
|
end
|
20
52
|
end
|
@@ -8,6 +8,13 @@
|
|
8
8
|
</div>
|
9
9
|
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
10
10
|
<% if combined_experiment.running? %>
|
11
|
+
<% if experiment_peekable?(combined_experiment) %>
|
12
|
+
<% if experiment_peeking?(combined_experiment) %>
|
13
|
+
<%= link_to "hide", trail_guide_admin.experiments_path(anchor: combined_experiment.experiment_name), class: 'btn btn-sm btn-outline-primary', method: :put %>
|
14
|
+
<% else %>
|
15
|
+
<%= link_to "peek", peek_url(combined_experiment), class: 'btn btn-sm btn-primary', method: :put %>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
11
18
|
<%= link_to "stop", trail_guide_admin.stop_experiment_path(combined_experiment.experiment_name), class: 'btn btn-sm btn-warning', method: :put %>
|
12
19
|
<%= link_to "restart", trail_guide_admin.restart_experiment_path(combined_experiment.experiment_name), class: 'btn btn-sm btn-danger',method: :put %>
|
13
20
|
<% elsif combined_experiment.started? %>
|
@@ -84,13 +91,13 @@
|
|
84
91
|
<% end %>
|
85
92
|
</th>
|
86
93
|
|
87
|
-
<td><%= variant.participants %></td>
|
94
|
+
<td><%= experiment_metric combined_experiment, variant.participants %></td>
|
88
95
|
|
89
96
|
<% if experiment.goals.empty? %>
|
90
|
-
<td><%= variant.converted %></td>
|
97
|
+
<td><%= experiment_metric combined_experiment, variant.converted %></td>
|
91
98
|
<% else %>
|
92
99
|
<% experiment.goals.each do |goal| %>
|
93
|
-
<td><%= variant.converted(goal) %></td>
|
100
|
+
<td><%= experiment_metric combined_experiment, variant.converted(goal) %></td>
|
94
101
|
<% end %>
|
95
102
|
<% end %>
|
96
103
|
|
@@ -117,12 +124,17 @@
|
|
117
124
|
<tfoot class="thead-light">
|
118
125
|
<tr>
|
119
126
|
<th scope="row"> </th>
|
120
|
-
<th
|
127
|
+
<th>
|
128
|
+
<span><%= combined_experiment.participants %></span>
|
129
|
+
<% if combined_experiment.configuration.target_sample_size %>
|
130
|
+
<span class="text-muted">/ <%= combined_experiment.configuration.target_sample_size %></span>
|
131
|
+
<% end %>
|
132
|
+
</th>
|
121
133
|
<% if combined_experiment.goals.empty? %>
|
122
|
-
<th><%=
|
134
|
+
<th><%= experiment_metric combined_experiment, combined_experiments.sum(&:converted) %></th>
|
123
135
|
<% else %>
|
124
136
|
<% combined_experiment.goals.each do |goal| %>
|
125
|
-
<th><%= combined_experiments.sum { |e| e.
|
137
|
+
<th><%= experiment_metric combined_experiment, combined_experiments.sum { |e| e.converted(goal) } %></th>
|
126
138
|
<% end %>
|
127
139
|
<% end %>
|
128
140
|
<th> </th>
|
@@ -8,6 +8,13 @@
|
|
8
8
|
</div>
|
9
9
|
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
10
10
|
<% if experiment.running? %>
|
11
|
+
<% if experiment_peekable?(experiment) %>
|
12
|
+
<% if experiment_peeking?(experiment) %>
|
13
|
+
<%= link_to "hide", trail_guide_admin.experiments_path(anchor: experiment.experiment_name), class: 'btn btn-sm btn-outline-primary', method: :put %>
|
14
|
+
<% else %>
|
15
|
+
<%= link_to "peek", peek_url(experiment), class: 'btn btn-sm btn-primary', method: :put %>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
11
18
|
<%= link_to "stop", trail_guide_admin.stop_experiment_path(experiment.experiment_name), class: 'btn btn-sm btn-warning', method: :put %>
|
12
19
|
<%= link_to "restart", trail_guide_admin.restart_experiment_path(experiment.experiment_name), class: 'btn btn-sm btn-danger',method: :put %>
|
13
20
|
<% elsif experiment.started? %>
|
@@ -78,13 +85,13 @@
|
|
78
85
|
<% end %>
|
79
86
|
</th>
|
80
87
|
|
81
|
-
<td><%= variant.participants %></td>
|
88
|
+
<td><%= experiment_metric experiment, variant.participants %></td>
|
82
89
|
|
83
90
|
<% if experiment.goals.empty? %>
|
84
|
-
<td><%= variant.converted %></td>
|
91
|
+
<td><%= experiment_metric experiment, variant.converted %></td>
|
85
92
|
<% else %>
|
86
93
|
<% experiment.goals.each do |goal| %>
|
87
|
-
<td><%= variant.converted(goal) %></td>
|
94
|
+
<td><%= experiment_metric experiment, variant.converted(goal) %></td>
|
88
95
|
<% end %>
|
89
96
|
<% end %>
|
90
97
|
|
@@ -110,12 +117,17 @@
|
|
110
117
|
<tfoot class="thead-light">
|
111
118
|
<tr>
|
112
119
|
<th scope="row"> </th>
|
113
|
-
<th
|
120
|
+
<th>
|
121
|
+
<span><%= experiment.participants %></span>
|
122
|
+
<% if experiment.configuration.target_sample_size %>
|
123
|
+
<span class="text-muted">/ <%= experiment.configuration.target_sample_size %></span>
|
124
|
+
<% end %>
|
125
|
+
</th>
|
114
126
|
<% if experiment.goals.empty? %>
|
115
|
-
<th><%= experiment.
|
127
|
+
<th><%= experiment_metric experiment, experiment.converted %></th>
|
116
128
|
<% else %>
|
117
129
|
<% experiment.goals.each do |goal| %>
|
118
|
-
<th><%= experiment
|
130
|
+
<th><%= experiment_metric experiment, experiment.converted(goal) %></th>
|
119
131
|
<% end %>
|
120
132
|
<% end %>
|
121
133
|
<th> </th>
|
@@ -7,6 +7,13 @@ TrailGuide.configure do |config|
|
|
7
7
|
# globally disable trailguide - returns control everywhere
|
8
8
|
config.disabled = false
|
9
9
|
|
10
|
+
# whether or not to include TrailGuide::Helper into controller and view
|
11
|
+
# contexts
|
12
|
+
#
|
13
|
+
# true the helper module is automatically mixed-in to controllers/views
|
14
|
+
# false you'll need to include the helper module manually where you want
|
15
|
+
config.include_helpers = true
|
16
|
+
|
10
17
|
# request param for overriding/previewing variants - allows previewing
|
11
18
|
# variants with request params
|
12
19
|
# i.e. example.com/somepage/?experiment[my_experiment]=option_b
|
@@ -146,6 +153,12 @@ TrailGuide::Experiment.configure do |config|
|
|
146
153
|
# false default behavior, requests will be filtered based on your config
|
147
154
|
config.skip_request_filter = false
|
148
155
|
|
156
|
+
# set a default target sample size for all experiments - this will prevent
|
157
|
+
# metrics and stats from being displayed in the admin UI until the sample size
|
158
|
+
# is reached or the experiment is stopped
|
159
|
+
#
|
160
|
+
# config.target_sample_size = nil
|
161
|
+
|
149
162
|
# callback when connecting to redis fails and trailguide falls back to always
|
150
163
|
# returning control variants
|
151
164
|
config.on_redis_failover = -> (experiment, error) do
|
@@ -227,6 +240,19 @@ end
|
|
227
240
|
# admin ui configuration
|
228
241
|
#
|
229
242
|
TrailGuide::Admin.configure do |config|
|
243
|
+
# display title for admin UI
|
244
|
+
#
|
230
245
|
config.title = 'TrailGuide'
|
246
|
+
|
247
|
+
# display subtitle for admin UI
|
248
|
+
#
|
231
249
|
config.subtitle = 'Experiments and A/B Tests'
|
250
|
+
|
251
|
+
# request parameter can be used to "peek" at results even before an
|
252
|
+
# experiment's target_sample_size has been hit if one is configured
|
253
|
+
#
|
254
|
+
# if you set this to nil, admins will not be able to peek at experiment
|
255
|
+
# results until the target sample size is hit or the experiment is stopped
|
256
|
+
#
|
257
|
+
config.peek_parameter = nil
|
232
258
|
end
|
data/lib/trail_guide/config.rb
CHANGED
@@ -3,7 +3,7 @@ module TrailGuide
|
|
3
3
|
DEFAULT_KEYS = [
|
4
4
|
:redis, :disabled, :override_parameter, :allow_multiple_experiments,
|
5
5
|
:adapter, :on_adapter_failover, :filtered_ip_addresses,
|
6
|
-
:filtered_user_agents, :request_filter
|
6
|
+
:filtered_user_agents, :request_filter, :include_helpers
|
7
7
|
].freeze
|
8
8
|
|
9
9
|
def initialize(*args, **opts, &block)
|
data/lib/trail_guide/engine.rb
CHANGED
@@ -8,8 +8,10 @@ module TrailGuide
|
|
8
8
|
|
9
9
|
initializer "trailguide" do |app|
|
10
10
|
TrailGuide::Catalog.load_experiments!
|
11
|
-
|
12
|
-
|
11
|
+
if TrailGuide.configuration.include_helpers
|
12
|
+
ActionController::Base.send :include, TrailGuide::Helper
|
13
|
+
ActionController::Base.helper TrailGuide::Helper
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -131,6 +131,24 @@ module TrailGuide
|
|
131
131
|
reset
|
132
132
|
end
|
133
133
|
|
134
|
+
def participants
|
135
|
+
variants.sum(&:participants)
|
136
|
+
end
|
137
|
+
|
138
|
+
def converted(checkpoint=nil)
|
139
|
+
variants.sum { |var| var.converted(checkpoint) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def unconverted
|
143
|
+
participants - converted
|
144
|
+
end
|
145
|
+
|
146
|
+
def target_sample_size_reached?
|
147
|
+
return true unless configuration.target_sample_size
|
148
|
+
return true if participants >= configuration.target_sample_size
|
149
|
+
return false
|
150
|
+
end
|
151
|
+
|
134
152
|
def as_json(opts={})
|
135
153
|
{ experiment_name => {
|
136
154
|
configuration: {
|
@@ -5,7 +5,7 @@ module TrailGuide
|
|
5
5
|
:name, :summary, :preview_url, :algorithm, :metric, :variants, :goals,
|
6
6
|
:start_manually, :reset_manually, :store_override, :track_override,
|
7
7
|
:combined, :allow_multiple_conversions, :allow_multiple_goals,
|
8
|
-
:track_winner_conversions, :skip_request_filter
|
8
|
+
:track_winner_conversions, :skip_request_filter, :target_sample_size
|
9
9
|
].freeze
|
10
10
|
|
11
11
|
CALLBACK_KEYS = [
|
@@ -9,15 +9,15 @@ module TrailGuide
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def participating?
|
12
|
-
participant.participating?(experiment)
|
12
|
+
@participating ||= variant.present?#participant.participating?(experiment)
|
13
13
|
end
|
14
14
|
|
15
15
|
def converted?(checkpoint=nil)
|
16
|
-
participant.converted?(experiment, checkpoint)
|
16
|
+
@converted ||= participant.converted?(experiment, checkpoint)
|
17
17
|
end
|
18
18
|
|
19
19
|
def variant
|
20
|
-
participant.variant(experiment)
|
20
|
+
@variant ||= participant.variant(experiment)
|
21
21
|
end
|
22
22
|
|
23
23
|
def method_missing(meth, *args, &block)
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module TrailGuide
|
2
2
|
class Participant
|
3
|
-
attr_reader :context
|
3
|
+
attr_reader :context, :variants
|
4
4
|
delegate :key?, :keys, :[], :[]=, :delete, :destroy!, :to_h, to: :adapter
|
5
5
|
|
6
6
|
def initialize(context, adapter: nil)
|
7
7
|
@context = context
|
8
8
|
@adapter = adapter.new(context) unless adapter.nil?
|
9
|
-
|
9
|
+
@variants = {}
|
10
|
+
#cleanup_inactive_experiments!
|
10
11
|
end
|
11
12
|
|
12
13
|
def adapter
|
@@ -36,6 +37,7 @@ module TrailGuide
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def participating?(experiment, include_control=true)
|
40
|
+
return variants[experiment.storage_key] if variants.key?(experiment.storage_key)
|
39
41
|
return false unless experiment.started?
|
40
42
|
return false unless adapter.key?(experiment.storage_key)
|
41
43
|
varname = adapter[experiment.storage_key]
|
@@ -44,7 +46,10 @@ module TrailGuide
|
|
44
46
|
return false unless variant && adapter.key?(variant.storage_key)
|
45
47
|
|
46
48
|
chosen_at = Time.at(adapter[variant.storage_key].to_i)
|
47
|
-
|
49
|
+
if chosen_at >= experiment.started_at
|
50
|
+
variants[experiment.storage_key] = variant
|
51
|
+
return variant
|
52
|
+
end
|
48
53
|
end
|
49
54
|
|
50
55
|
def converted?(experiment, checkpoint=nil)
|
data/lib/trail_guide/version.rb
CHANGED
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.21
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|