trailguide 0.1.20 → 0.1.21

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c45506f04eec39f23d00fce31f84d1d1e24f4289e2870af413f8928d54cceef8
4
- data.tar.gz: 1b6cb001e0fa544f6390e4a82e265922f9ab75b602f4406a2cddf337704fd076
3
+ metadata.gz: ec80228061f031c01c0eb4d6dbef24d52ca205ccf2e6137ebe3a18886b095258
4
+ data.tar.gz: 6fa47e1dd04511cbb35d39a737589a34658fcc9d1f0dde64298908a9ca539f27
5
5
  SHA512:
6
- metadata.gz: 757e5c2ad76a1143db739638fc60ccaf263d047c2ea3f41172c74368c8c3a2744ba760b0255c6393623ba2266df5e6ec4bff3f3c4e6898370eeefb536b7b5e0b
7
- data.tar.gz: 820c4664c60c161dfad8e81dcab8cb61e4137d9961c97e5b0da3f9dfdb5e496692d3bc1bd7f9784645095dfb4be38c75bb73604b80e6e855d42ec2bdac187de0
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">&nbsp;</th>
120
- <th><%= combined_experiment.variants.sum(&:participants) %></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><%= combined_experiments.sum { |e| e.variants.sum(&:converted) } %></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.variants.sum { |v| v.converted(goal) } } %></th>
137
+ <th><%= experiment_metric combined_experiment, combined_experiments.sum { |e| e.converted(goal) } %></th>
126
138
  <% end %>
127
139
  <% end %>
128
140
  <th>&nbsp;</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">&nbsp;</th>
113
- <th><%= experiment.variants.sum(&:participants) %></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.variants.sum(&:converted) %></th>
127
+ <th><%= experiment_metric experiment, experiment.converted %></th>
116
128
  <% else %>
117
129
  <% experiment.goals.each do |goal| %>
118
- <th><%= experiment.variants.sum { |var| var.converted(goal) } %></th>
130
+ <th><%= experiment_metric experiment, experiment.converted(goal) %></th>
119
131
  <% end %>
120
132
  <% end %>
121
133
  <th>&nbsp;</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
@@ -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)
@@ -8,8 +8,10 @@ module TrailGuide
8
8
 
9
9
  initializer "trailguide" do |app|
10
10
  TrailGuide::Catalog.load_experiments!
11
- ActionController::Base.send :include, TrailGuide::Helper
12
- ActionController::Base.helper TrailGuide::Helper
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
- cleanup_inactive_experiments!
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
- return variant if chosen_at >= experiment.started_at
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)
@@ -2,7 +2,7 @@ module TrailGuide
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- PATCH = 20
5
+ PATCH = 21
6
6
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
 
8
8
  class << self
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.20
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-12 00:00:00.000000000 Z
11
+ date: 2019-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails