trailguide 0.1.15 → 0.1.16
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/assets/images/trail_guide/trailguide.png +0 -0
- data/app/assets/stylesheets/trail_guide/admin/application.css +13 -0
- data/app/assets/stylesheets/trail_guide/admin/experiments.css +13 -0
- data/app/controllers/trail_guide/admin/application_controller.rb +13 -0
- data/app/views/layouts/trail_guide/admin/_footer.erb +5 -0
- data/app/views/layouts/trail_guide/admin/_header.erb +8 -0
- data/app/views/layouts/trail_guide/admin/application.html.erb +8 -1
- data/app/views/trail_guide/admin/experiments/_combined_experiment.html.erb +17 -13
- data/app/views/trail_guide/admin/experiments/_experiment.html.erb +16 -12
- data/app/views/trail_guide/admin/experiments/index.html.erb +1 -9
- data/config/initializers/assets.rb +1 -0
- data/lib/trail_guide/admin.rb +6 -0
- data/lib/trail_guide/catalog.rb +52 -7
- data/lib/trail_guide/engine.rb +1 -55
- data/lib/trail_guide/experiments/base.rb +3 -3
- data/lib/trail_guide/experiments/config.rb +12 -1
- data/lib/trail_guide/participant.rb +7 -7
- data/lib/trail_guide/variant.rb +3 -3
- data/lib/trail_guide/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bee61db15d9f3cfea6ecfe502be220772f82c34a256a8bdcde1f093ab637cd3
|
4
|
+
data.tar.gz: b2f3a245b0643fb631c38f8dffb245a28a8456391fd06ac4d9c12b6f44e1aac3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0357c34c58aa5ff3d8d5fab56a5b08f137cf686b44f563d3cb72eea422fb2f60d04a01732ff0f57926a3a36811e8dc82f5fdf3b643705219e75b9d91c0efad15
|
7
|
+
data.tar.gz: 8179d27ac67509e1b4ed26c67995d446994fdfec8fe21893c1b2042203de2128fb35714f6683b85bec0ce9f1beb67eca41c3fad3051194d232a8489fd0f882a2
|
Binary file
|
@@ -2,6 +2,19 @@ module TrailGuide
|
|
2
2
|
module Admin
|
3
3
|
class ApplicationController < ::ApplicationController
|
4
4
|
protect_from_forgery with: :exception
|
5
|
+
|
6
|
+
def preview_url(variant, *args, **opts)
|
7
|
+
config_url = variant.experiment.configuration.preview_url
|
8
|
+
opts = opts.merge({experiment: {variant.experiment.experiment_name => variant.name}})
|
9
|
+
if config_url.respond_to?(:call)
|
10
|
+
main_app.instance_exec *args, **opts, &config_url
|
11
|
+
elsif config_url.is_a?(Symbol)
|
12
|
+
main_app.send(config_url, *args, **opts)
|
13
|
+
else
|
14
|
+
config_url.to_s + "?experiment[#{variant.experiment.experiment_name}]=#{variant.name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
helper_method :preview_url
|
5
18
|
end
|
6
19
|
end
|
7
20
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<nav class="navbar navbar-expanded-sm navbar-light bg-light">
|
2
|
+
<%= link_to trail_guide_admin.experiments_path, class: "navbar-brand" do %>
|
3
|
+
<%= image_tag "trail_guide/trailguide.png" %>
|
4
|
+
<%= TrailGuide::Admin.configuration.title %>
|
5
|
+
<% end %>
|
6
|
+
<span class="navbar-brand"><small class="text-muted"><%= TrailGuide::Admin.configuration.subtitle %></small></span>
|
7
|
+
</nav>
|
8
|
+
|
@@ -11,8 +11,15 @@
|
|
11
11
|
<%= stylesheet_link_tag "trail_guide/admin/application", media: "all" %>
|
12
12
|
</head>
|
13
13
|
<body>
|
14
|
+
<%= render 'layouts/trail_guide/admin/header' %>
|
14
15
|
|
15
|
-
|
16
|
+
<main>
|
17
|
+
<div class="container-fluid">
|
18
|
+
<%= yield %>
|
19
|
+
</div>
|
20
|
+
</main>
|
21
|
+
|
22
|
+
<%= render 'layouts/trail_guide/admin/footer' %>
|
16
23
|
|
17
24
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
18
25
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<div class="row justify-content-center">
|
1
|
+
<div class="experiment row justify-content-center">
|
2
2
|
<div class="col-sm-12 col-md-10 col-lg-8">
|
3
3
|
<div class="row">
|
4
4
|
<div class="col-sm-12 col-md-6 col-lg-8">
|
5
|
-
<
|
5
|
+
<h4 id="<%= combined_experiment.experiment_name %>">
|
6
6
|
<%= link_to combined_experiment.experiment_name.to_s.humanize.titleize, trail_guide_admin.experiments_path(anchor: combined_experiment.experiment_name), class: "text-dark" %>
|
7
|
-
</
|
7
|
+
</h4>
|
8
8
|
</div>
|
9
9
|
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
10
10
|
<% if combined_experiment.running? %>
|
@@ -20,7 +20,10 @@
|
|
20
20
|
</div>
|
21
21
|
|
22
22
|
<div class="row">
|
23
|
-
<div class="col-sm-12
|
23
|
+
<div class="col-sm-12 col-md-6 col-lg-8">
|
24
|
+
<p><%= combined_experiment.configuration.summary %></p>
|
25
|
+
</div>
|
26
|
+
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
24
27
|
<% if combined_experiment.started? %>
|
25
28
|
<small class="text-muted"><%= combined_experiment.started_at.strftime('%b %e %Y @ %l:%M %p') %></small>
|
26
29
|
<span class="text-muted">—</span>
|
@@ -41,9 +44,9 @@
|
|
41
44
|
<thead class="thead-light">
|
42
45
|
<tr>
|
43
46
|
<th scope="col">
|
44
|
-
<
|
47
|
+
<h5 id="<%= experiment.experiment_name %>">
|
45
48
|
<%= link_to experiment.experiment_name.to_s.humanize.titleize, trail_guide_admin.experiments_path(anchor: experiment.experiment_name), class: "text-dark" %>
|
46
|
-
</
|
49
|
+
</h5>
|
47
50
|
</th>
|
48
51
|
|
49
52
|
<th scope="col">Participants</th>
|
@@ -62,9 +65,14 @@
|
|
62
65
|
|
63
66
|
<tbody>
|
64
67
|
<% experiment.variants.each do |variant| %>
|
65
|
-
<tr
|
68
|
+
<tr>
|
66
69
|
<th scope="row">
|
67
|
-
|
70
|
+
<% if experiment.configuration.preview_url? %>
|
71
|
+
<%= link_to variant.name.to_s.humanize.titleize, preview_url(variant), target: :blank, class: "text-dark" %>
|
72
|
+
<% else %>
|
73
|
+
<%= variant.name.to_s.humanize.titleize %>
|
74
|
+
<% end %>
|
75
|
+
|
68
76
|
<% if experiment.running? && !experiment.winner? && participant.variant(experiment) == variant %>
|
69
77
|
<span class="badge badge-secondary">joined</span>
|
70
78
|
<% end %>
|
@@ -108,7 +116,7 @@
|
|
108
116
|
|
109
117
|
<tfoot class="thead-light">
|
110
118
|
<tr>
|
111
|
-
<th scope="row"
|
119
|
+
<th scope="row"> </th>
|
112
120
|
<th><%= combined_experiment.variants.sum(&:participants) %></th>
|
113
121
|
<% if combined_experiment.goals.empty? %>
|
114
122
|
<th><%= combined_experiments.sum { |e| e.variants.sum(&:converted) } %></th>
|
@@ -123,7 +131,3 @@
|
|
123
131
|
</table>
|
124
132
|
</div>
|
125
133
|
</div>
|
126
|
-
|
127
|
-
<br />
|
128
|
-
<br />
|
129
|
-
<br />
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<div class="row justify-content-center">
|
1
|
+
<div class="experiment row justify-content-center">
|
2
2
|
<div class="col-sm-12 col-md-10 col-lg-8">
|
3
3
|
<div class="row">
|
4
4
|
<div class="col-sm-12 col-md-6 col-lg-8">
|
5
|
-
<
|
5
|
+
<h4 id="<%= experiment.experiment_name %>">
|
6
6
|
<%= link_to experiment.experiment_name.to_s.humanize.titleize, trail_guide_admin.experiments_path(anchor: experiment.experiment_name), class: "text-dark" %>
|
7
|
-
</
|
7
|
+
</h4>
|
8
8
|
</div>
|
9
9
|
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
10
10
|
<% if experiment.running? %>
|
@@ -20,7 +20,10 @@
|
|
20
20
|
</div>
|
21
21
|
|
22
22
|
<div class="row">
|
23
|
-
<div class="col-sm-12
|
23
|
+
<div class="col-sm-12 col-md-6 col-lg-8">
|
24
|
+
<p><%= experiment.configuration.summary %></p>
|
25
|
+
</div>
|
26
|
+
<div class="col-sm-12 col-md-6 col-lg-4 text-right">
|
24
27
|
<% if experiment.started? %>
|
25
28
|
<small class="text-muted"><%= experiment.started_at.strftime('%b %e %Y @ %l:%M %p') %></small>
|
26
29
|
<span class="text-muted">—</span>
|
@@ -38,7 +41,7 @@
|
|
38
41
|
<table class="table table-hover">
|
39
42
|
<thead class="thead-light">
|
40
43
|
<tr>
|
41
|
-
<th scope="col"
|
44
|
+
<th scope="col"> </th>
|
42
45
|
|
43
46
|
<th scope="col">Participants</th>
|
44
47
|
|
@@ -56,9 +59,14 @@
|
|
56
59
|
|
57
60
|
<tbody>
|
58
61
|
<% experiment.variants.each do |variant| %>
|
59
|
-
<tr
|
62
|
+
<tr>
|
60
63
|
<th scope="row">
|
61
|
-
|
64
|
+
<% if experiment.configuration.preview_url? %>
|
65
|
+
<%= link_to variant.name.to_s.humanize.titleize, preview_url(variant), target: :blank, class: "text-dark" %>
|
66
|
+
<% else %>
|
67
|
+
<%= variant.name.to_s.humanize.titleize %>
|
68
|
+
<% end %>
|
69
|
+
|
62
70
|
<% if experiment.running? && !experiment.winner? && participant.variant(experiment) == variant %>
|
63
71
|
<span class="badge badge-secondary">joined</span>
|
64
72
|
<% end %>
|
@@ -101,7 +109,7 @@
|
|
101
109
|
|
102
110
|
<tfoot class="thead-light">
|
103
111
|
<tr>
|
104
|
-
<th scope="row"
|
112
|
+
<th scope="row"> </th>
|
105
113
|
<th><%= experiment.variants.sum(&:participants) %></th>
|
106
114
|
<% if experiment.goals.empty? %>
|
107
115
|
<th><%= experiment.variants.sum(&:converted) %></th>
|
@@ -116,7 +124,3 @@
|
|
116
124
|
</table>
|
117
125
|
</div>
|
118
126
|
</div>
|
119
|
-
|
120
|
-
<br />
|
121
|
-
<br />
|
122
|
-
<br />
|
@@ -1,12 +1,4 @@
|
|
1
|
-
<div class="
|
2
|
-
<div class="row justify-content-center">
|
3
|
-
<div class="col-sm-12 col-md-10 col-lg-8">
|
4
|
-
<h1><%= link_to "Experiments", trail_guide_admin.experiments_path, class: "text-dark" %></h1>
|
5
|
-
</div>
|
6
|
-
</div>
|
7
|
-
|
8
|
-
<hr />
|
9
|
-
|
1
|
+
<div class="experiments">
|
10
2
|
<% TrailGuide.catalog.each do |experiment| %>
|
11
3
|
<% if experiment.combined? %>
|
12
4
|
<%= render 'combined_experiment', combined_experiment: experiment %>
|
@@ -0,0 +1 @@
|
|
1
|
+
Rails.application.config.assets.precompile += %w( trail_guide/trailguide.png )
|
data/lib/trail_guide/admin.rb
CHANGED
data/lib/trail_guide/catalog.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
module TrailGuide
|
2
2
|
class Catalog
|
3
|
+
class DSL
|
4
|
+
def self.experiment(name, &block)
|
5
|
+
Class.new(TrailGuide::Experiment) do
|
6
|
+
configure name: name
|
7
|
+
configure &block
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
include Enumerable
|
4
13
|
|
5
14
|
class << self
|
@@ -7,16 +16,52 @@ module TrailGuide
|
|
7
16
|
@catalog ||= new
|
8
17
|
end
|
9
18
|
|
10
|
-
def
|
11
|
-
catalog
|
12
|
-
|
19
|
+
def load_experiments!
|
20
|
+
@catalog = nil
|
21
|
+
|
22
|
+
# Load experiments from YAML configs if any exists
|
23
|
+
load_yaml_experiments(Rails.root.join("config/experiments.yml"))
|
24
|
+
Dir[Rails.root.join("config/experiments/**/*.yml")].each { |f| load_yaml_experiments(f) }
|
13
25
|
|
14
|
-
|
15
|
-
|
26
|
+
# Load experiments from ruby configs if any exist
|
27
|
+
DSL.instance_eval(File.read(Rails.root.join("config/experiments.rb"))) if File.exists?(Rails.root.join("config/experiments.rb"))
|
28
|
+
Dir[Rails.root.join("config/experiments/**/*.rb")].each { |f| DSL.instance_eval(File.read(f)) }
|
29
|
+
|
30
|
+
# Load any experiment classes defined in the app
|
31
|
+
Dir[Rails.root.join("app/experiments/**/*.rb")].each { |f| load f }
|
16
32
|
end
|
17
33
|
|
18
|
-
def
|
19
|
-
|
34
|
+
def load_yaml_experiments(file)
|
35
|
+
experiments = (YAML.load_file(file) || {} rescue {})
|
36
|
+
.symbolize_keys.map { |k,v| [k, v.symbolize_keys] }.to_h
|
37
|
+
|
38
|
+
experiments.each do |name, options|
|
39
|
+
expvars = options[:variants].map do |var|
|
40
|
+
if var.is_a?(Array)
|
41
|
+
[var[0], var[1].symbolize_keys]
|
42
|
+
else
|
43
|
+
[var]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
DSL.experiment(name) do |config|
|
48
|
+
expvars.each do |expvar|
|
49
|
+
variant *expvar
|
50
|
+
end
|
51
|
+
# TODO also map goals once they're real classes
|
52
|
+
config.control = options[:control] if options[:control]
|
53
|
+
config.metric = options[:metric] if options[:metric]
|
54
|
+
config.algorithm = options[:algorithm] if options[:algorithm]
|
55
|
+
config.goals = options[:goals] if options[:goals]
|
56
|
+
config.combined = options[:combined] if options[:combined]
|
57
|
+
config.reset_manually = options[:reset_manually] if options.key?(:reset_manually)
|
58
|
+
config.start_manually = options[:start_manually] if options.key?(:start_manually)
|
59
|
+
config.store_override = options[:store_override] if options.key?(:store_override)
|
60
|
+
config.track_override = options[:track_override] if options.key?(:track_override)
|
61
|
+
config.allow_multiple_conversions = options[:allow_multiple_conversions] if options.key?(:allow_multiple_conversions)
|
62
|
+
config.allow_multiple_goals = options[:allow_multiple_goals] if options.key?(:allow_multiple_goals)
|
63
|
+
end
|
64
|
+
end
|
20
65
|
end
|
21
66
|
|
22
67
|
def combined_experiment(combined, name)
|
data/lib/trail_guide/engine.rb
CHANGED
@@ -7,63 +7,9 @@ module TrailGuide
|
|
7
7
|
end
|
8
8
|
|
9
9
|
initializer "trailguide" do |app|
|
10
|
-
TrailGuide::
|
10
|
+
TrailGuide::Catalog.load_experiments!
|
11
11
|
ActionController::Base.send :include, TrailGuide::Helper
|
12
12
|
ActionController::Base.helper TrailGuide::Helper
|
13
13
|
end
|
14
|
-
|
15
|
-
def self.load_experiments
|
16
|
-
# Load experiments from YAML configs if any exists
|
17
|
-
load_yaml_experiments(Rails.root.join("config/experiments.yml"))
|
18
|
-
Dir[Rails.root.join("config/experiments/**/*.yml")].each { |f| load_yaml_experiments(f) }
|
19
|
-
|
20
|
-
# Load experiments from ruby configs if any exist
|
21
|
-
DSL.instance_eval(File.read(Rails.root.join("config/experiments.rb"))) if File.exists?(Rails.root.join("config/experiments.rb"))
|
22
|
-
Dir[Rails.root.join("config/experiments/**/*.rb")].each { |f| DSL.instance_eval(File.read(f)) }
|
23
|
-
|
24
|
-
# Load any experiment classes defined in the app
|
25
|
-
Dir[Rails.root.join("app/experiments/**/*.rb")].each { |f| load f }
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.load_yaml_experiments(file)
|
29
|
-
experiments = (YAML.load_file(file) || {} rescue {})
|
30
|
-
.symbolize_keys.map { |k,v| [k, v.symbolize_keys] }.to_h
|
31
|
-
|
32
|
-
experiments.each do |name, options|
|
33
|
-
expvars = options[:variants].map do |var|
|
34
|
-
if var.is_a?(Array)
|
35
|
-
[var[0], var[1].symbolize_keys]
|
36
|
-
else
|
37
|
-
[var]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
DSL.experiment(name) do |config|
|
42
|
-
expvars.each do |expvar|
|
43
|
-
variant *expvar
|
44
|
-
end
|
45
|
-
config.control = options[:control] if options[:control]
|
46
|
-
config.metric = options[:metric] if options[:metric]
|
47
|
-
config.algorithm = options[:algorithm] if options[:algorithm]
|
48
|
-
config.goals = options[:goals] if options[:goals]
|
49
|
-
config.combined = options[:combined] if options[:combined]
|
50
|
-
config.reset_manually = options[:reset_manually] if options.key?(:reset_manually)
|
51
|
-
config.start_manually = options[:start_manually] if options.key?(:start_manually)
|
52
|
-
config.store_override = options[:store_override] if options.key?(:store_override)
|
53
|
-
config.track_override = options[:track_override] if options.key?(:track_override)
|
54
|
-
config.allow_multiple_conversions = options[:allow_multiple_conversions] if options.key?(:allow_multiple_conversions)
|
55
|
-
config.allow_multiple_goals = options[:allow_multiple_goals] if options.key?(:allow_multiple_goals)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class DSL
|
61
|
-
def self.experiment(name, &block)
|
62
|
-
Class.new(TrailGuide::Experiment) do
|
63
|
-
configure name: name
|
64
|
-
configure &block
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
14
|
end
|
69
15
|
end
|
@@ -158,7 +158,7 @@ module TrailGuide
|
|
158
158
|
end
|
159
159
|
|
160
160
|
attr_reader :participant
|
161
|
-
delegate :configuration, :experiment_name, :variants, :control, :
|
161
|
+
delegate :configuration, :experiment_name, :variants, :control, :goals,
|
162
162
|
:storage_key, :running?, :started?, :started_at, :start!, :resettable?,
|
163
163
|
:winner?, :allow_multiple_conversions?, :allow_multiple_goals?,
|
164
164
|
:track_winner_conversions?, :callbacks, to: :class
|
@@ -221,8 +221,8 @@ module TrailGuide
|
|
221
221
|
def convert!(checkpoint=nil, metadata: nil)
|
222
222
|
return false if !running? || (winner? && !track_winner_conversions?)
|
223
223
|
return false unless participating?
|
224
|
-
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for `#{experiment_name}`." unless checkpoint.present? ||
|
225
|
-
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for `#{experiment_name}`." unless checkpoint.nil? ||
|
224
|
+
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for `#{experiment_name}`." unless checkpoint.present? || goals.empty?
|
225
|
+
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for `#{experiment_name}`." unless checkpoint.nil? || goals.any? { |goal| goal == checkpoint.to_s.underscore.to_sym }
|
226
226
|
# TODO eventually allow progressing through funnel checkpoints towards goals
|
227
227
|
if converted?(checkpoint)
|
228
228
|
return false unless allow_multiple_conversions?
|
@@ -14,7 +14,14 @@ module TrailGuide
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.default_config
|
17
|
-
{ name: nil,
|
17
|
+
{ name: nil,
|
18
|
+
metric: nil,
|
19
|
+
variants: [],
|
20
|
+
goals: [],
|
21
|
+
combined: [],
|
22
|
+
summary: nil,
|
23
|
+
preview_url: nil,
|
24
|
+
}
|
18
25
|
end
|
19
26
|
|
20
27
|
def self.callbacks_config
|
@@ -115,6 +122,10 @@ module TrailGuide
|
|
115
122
|
!combined.empty?
|
116
123
|
end
|
117
124
|
|
125
|
+
def preview_url?
|
126
|
+
!!preview_url
|
127
|
+
end
|
128
|
+
|
118
129
|
def on_choose(meth=nil, &block)
|
119
130
|
callbacks[:on_choose] << (meth || block)
|
120
131
|
end
|
@@ -48,7 +48,7 @@ module TrailGuide
|
|
48
48
|
|
49
49
|
def converted?(experiment, checkpoint=nil)
|
50
50
|
return false unless experiment.started?
|
51
|
-
if experiment.
|
51
|
+
if experiment.goals.empty?
|
52
52
|
raise InvalidGoalError, "You provided the checkpoint `#{checkpoint}` but the experiment `#{experiment.experiment_name}` does not have any goals defined." unless checkpoint.nil?
|
53
53
|
storage_key = "#{experiment.storage_key}:converted"
|
54
54
|
return false unless adapter.key?(storage_key)
|
@@ -56,15 +56,15 @@ module TrailGuide
|
|
56
56
|
converted_at = Time.at(adapter[storage_key].to_i)
|
57
57
|
converted_at >= experiment.started_at
|
58
58
|
elsif !checkpoint.nil?
|
59
|
-
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for experiment `#{experiment.experiment_name}`." unless experiment.
|
59
|
+
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for experiment `#{experiment.experiment_name}`." unless experiment.goals.any? { |goal| goal == checkpoint.to_s.underscore.to_sym }
|
60
60
|
storage_key = "#{experiment.storage_key}:#{checkpoint.to_s.underscore}"
|
61
61
|
return false unless adapter.key?(storage_key)
|
62
62
|
|
63
63
|
converted_at = Time.at(adapter[storage_key].to_i)
|
64
64
|
converted_at >= experiment.started_at
|
65
65
|
else
|
66
|
-
experiment.
|
67
|
-
storage_key = "#{experiment.storage_key}:#{
|
66
|
+
experiment.goals.each do |goal|
|
67
|
+
storage_key = "#{experiment.storage_key}:#{goal.to_s}"
|
68
68
|
next unless adapter.key?(storage_key)
|
69
69
|
converted_at = Time.at(adapter[storage_key].to_i)
|
70
70
|
return true if converted_at >= experiment.started_at
|
@@ -90,9 +90,9 @@ module TrailGuide
|
|
90
90
|
adapter.delete(variant.experiment.storage_key)
|
91
91
|
adapter.delete(variant.storage_key)
|
92
92
|
adapter.delete(storage_key)
|
93
|
-
variant.experiment.
|
94
|
-
|
95
|
-
adapter.delete(
|
93
|
+
variant.experiment.goals.each do |goal|
|
94
|
+
goal_key = "#{variant.experiment.storage_key}:#{goal.to_s}"
|
95
|
+
adapter.delete(goal_key)
|
96
96
|
end
|
97
97
|
else
|
98
98
|
adapter[storage_key] = Time.now.to_i
|
data/lib/trail_guide/variant.rb
CHANGED
@@ -57,14 +57,14 @@ module TrailGuide
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def converted(checkpoint=nil)
|
60
|
-
if experiment.
|
60
|
+
if experiment.goals.empty?
|
61
61
|
raise InvalidGoalError, "You provided the checkpoint `#{checkpoint}` but the experiment `#{experiment.experiment_name}` does not have any goals defined." unless checkpoint.nil?
|
62
62
|
(TrailGuide.redis.hget(storage_key, 'converted') || 0).to_i
|
63
63
|
elsif !checkpoint.nil?
|
64
|
-
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for experiment `#{experiment.experiment_name}`." unless experiment.
|
64
|
+
raise InvalidGoalError, "Invalid goal checkpoint `#{checkpoint}` for experiment `#{experiment.experiment_name}`." unless experiment.goals.any? { |goal| goal == checkpoint.to_s.underscore.to_sym }
|
65
65
|
(TrailGuide.redis.hget(storage_key, checkpoint.to_s.underscore) || 0).to_i
|
66
66
|
else
|
67
|
-
experiment.
|
67
|
+
experiment.goals.sum do |checkpoint|
|
68
68
|
(TrailGuide.redis.hget(storage_key, checkpoint.to_s.underscore) || 0).to_i
|
69
69
|
end
|
70
70
|
end
|
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.16
|
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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -104,16 +104,21 @@ files:
|
|
104
104
|
- MIT-LICENSE
|
105
105
|
- README.md
|
106
106
|
- app/assets/config/trail_guide/admin_manifest.js
|
107
|
+
- app/assets/images/trail_guide/trailguide.png
|
107
108
|
- app/assets/javascripts/trail_guide/admin/application.js
|
108
109
|
- app/assets/javascripts/trailguide.js
|
109
110
|
- app/assets/stylesheets/trail_guide/admin/application.css
|
111
|
+
- app/assets/stylesheets/trail_guide/admin/experiments.css
|
110
112
|
- app/controllers/trail_guide/admin/application_controller.rb
|
111
113
|
- app/controllers/trail_guide/admin/experiments_controller.rb
|
112
114
|
- app/controllers/trail_guide/experiments_controller.rb
|
115
|
+
- app/views/layouts/trail_guide/admin/_footer.erb
|
116
|
+
- app/views/layouts/trail_guide/admin/_header.erb
|
113
117
|
- app/views/layouts/trail_guide/admin/application.html.erb
|
114
118
|
- app/views/trail_guide/admin/experiments/_combined_experiment.html.erb
|
115
119
|
- app/views/trail_guide/admin/experiments/_experiment.html.erb
|
116
120
|
- app/views/trail_guide/admin/experiments/index.html.erb
|
121
|
+
- config/initializers/assets.rb
|
117
122
|
- config/routes.rb
|
118
123
|
- lib/trail_guide/adapters.rb
|
119
124
|
- lib/trail_guide/adapters/participants.rb
|