mountain-goat 1.0.1 → 1.0.2
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.
- data/.gitignore +2 -0
- data/README.md +5 -4
- data/generators/mg/mg_generator.rb +2 -2
- data/generators/mg/templates/update_mountain_goat_tables_v2.rb +73 -0
- data/lib/mountain-goat/controllers/mg/choices_controller.rb +81 -0
- data/lib/mountain-goat/controllers/mg/goals_controller.rb +136 -0
- data/lib/mountain-goat/controllers/mg/records_controller.rb +46 -0
- data/lib/mountain-goat/controllers/mg/tests_controller.rb +139 -0
- data/lib/mountain-goat/mg_core.rb +403 -0
- data/lib/mountain-goat/models/mg/choice.rb +50 -0
- data/lib/mountain-goat/models/mg/gi_meta.rb +10 -0
- data/lib/mountain-goat/models/mg/goal.rb +183 -0
- data/lib/mountain-goat/models/mg/goal_meta_type.rb +20 -0
- data/lib/mountain-goat/models/mg/gs_meta.rb +10 -0
- data/lib/mountain-goat/models/mg/record.rb +59 -0
- data/lib/mountain-goat/models/mg/test.rb +31 -0
- data/lib/mountain-goat/public/g-funnel.js +168 -0
- data/lib/mountain-goat/switch_choice.rb +32 -0
- data/lib/mountain-goat/version.rb +1 -1
- data/lib/mountain-goat/views/mountain_goat/layouts/.tmp_mountain_goat.html.erb.50680~ +76 -0
- data/lib/mountain-goat/views/mountain_goat/mg/choices/_choice_form.html.erb +37 -0
- data/lib/mountain-goat/views/mountain_goat/mg/choices/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/choices/index.html.erb +34 -0
- data/lib/mountain-goat/views/mountain_goat/mg/choices/new.html.erb +15 -0
- data/lib/mountain-goat/views/mountain_goat/mg/choices/show.html.erb +30 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_form.html.erb.5027~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_meta_type_form.html.erb.39992~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_edit.html.erb.55874~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_index.html.erb.97274~ +36 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/_goal_form.html.erb +28 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/_goal_meta_type_form.html.erb +34 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/edit.html.erb +12 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/index.html.erb +37 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/new.html.erb +12 -0
- data/lib/mountain-goat/views/mountain_goat/mg/goals/show.html.erb +32 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/_record.html.erb +16 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/_records.html.erb +5 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/_records_form.html.erb +21 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/index.html.erb +17 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/new.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/records/show.html.erb +14 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__chart.html.erb.13419~ +18 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__funnel.html.erb.60493~ +18 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_form.html.erb.87420~ +10 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_pivot_form.html.erb.77056~ +14 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_edit.html.erb.31048~ +19 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_new.html.erb.36371~ +17 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_funnel.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_form.html.erb.76535~ +21 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_report_items.html.erb.26030~ +5 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_edit.html.erb.78064~ +36 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_index.html.erb.74591~ +23 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_show.html.erb.58427~ +21 -0
- data/lib/mountain-goat/views/mountain_goat/mg/tests/_test_form.html.erb +25 -0
- data/lib/mountain-goat/views/mountain_goat/mg/tests/edit.html.erb +12 -0
- data/lib/mountain-goat/views/mountain_goat/mg/tests/index.html.erb +48 -0
- data/lib/mountain-goat/views/mountain_goat/mg/tests/new.html.erb +12 -0
- data/lib/mountain-goat/views/mountain_goat/mg/tests/show.html.erb +63 -0
- metadata +69 -6
- data/lib/mountain-goat/metric_tracking.rb +0 -401
- data/lib/mountain-goat/switch_variant.rb +0 -32
@@ -0,0 +1,63 @@
|
|
1
|
+
<div id="container-main" class="mg-tests">
|
2
|
+
<h1><%=h @test.title.capitalize %> Test</h1>
|
3
|
+
<div class="container-content">
|
4
|
+
|
5
|
+
<div class="choices">
|
6
|
+
<% if @test.mg_choices.count == 0 %>
|
7
|
+
<span class="none">No choices</span>
|
8
|
+
<% else %>
|
9
|
+
<div class="statboard">
|
10
|
+
<div class="flyer-analysis statpad">
|
11
|
+
<div class="title">Records</div>
|
12
|
+
<div class="statbody">
|
13
|
+
<%= mg_chart("analysis_graph#{@test.id}",
|
14
|
+
{ "Served" => { :collection => @rates[:served], :x => :choice_type, :y => :value },
|
15
|
+
"Reward Count" => { :collection => @rates[:reward_count], :x => :choice_type, :y => :value } },
|
16
|
+
{ :xaxis => { :ticks => @rates[:titles].map { |i,t| [i + 0.5, t] }.sort }, :yaxis => { :min => 0, :minTickSize => 1 }, :bars => { :show => true }, :legend => { :show => false, :position => "ne" }, :grid => { :hoverable => true, :clickable => true }, :inline => false }, :placeholder_size => "420x260", :pie_hover => true, :line_hover_absolute => true, :item_title => "record(s)") %>
|
17
|
+
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<div class="flyer-analysis statpad">
|
21
|
+
<div class="title">Reward Rates</div>
|
22
|
+
<div class="statbody">
|
23
|
+
<%= mg_chart("analysis_conv_graph#{@test.id}",
|
24
|
+
{ "Reward Rate" => { :collection => @rates[:reward_rates], :x => :choice_type, :y => :value } },
|
25
|
+
{ :xaxis => { :ticks => @rates[:titles].map { |i,t| [i + 0.5, t] }.sort }, :yaxis => { :min => 0, :minTickSize => 1 }, :bars => { :show => true }, :legend => { :show => false, :position => "ne" }, :grid => { :hoverable => true, :clickable => true }, :inline => false }, :placeholder_size => "420x260", :pie_hover => true, :line_hover_absolute => true, :item_title => "") %>
|
26
|
+
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
|
32
|
+
<div class="choices">
|
33
|
+
<% if @test.mg_choices.count == 0 %>
|
34
|
+
<span class="none">No choices [<a href="<%= new_mg_test_choice_url :test_id => @test.id %>">Add one</a>]</span>
|
35
|
+
<% else %>
|
36
|
+
<div class="title-bar">
|
37
|
+
<span class="category choice">Choice</span>
|
38
|
+
<span class="category served">Served</span>
|
39
|
+
<span class="category hit-points">Points</span>
|
40
|
+
<span class="category hit-points-avg">Avg. Points</span>
|
41
|
+
<span class="category actions">Actions</span>
|
42
|
+
</div>
|
43
|
+
<ul class="listed">
|
44
|
+
<% @test.mg_choices.each do |choice| %>
|
45
|
+
<li class="choice">
|
46
|
+
<span class="category choice"><%=h choice.name %></span>
|
47
|
+
<span class="category served"><%= choice.served %></span>
|
48
|
+
<span class="category hit-points"><%= choice.reward || "None" %></span>
|
49
|
+
<span class="category hit-points-avg"><%=h choice.reward_rate || "N/A" %></span>
|
50
|
+
<span class="category actions"><a href="<%= mg_choice_url choice %>">Edit</a></span>
|
51
|
+
</li>
|
52
|
+
<% end %>
|
53
|
+
</ul>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
56
|
+
<% end %>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div class="action-bar">
|
60
|
+
<a class="button" href="<%= new_mg_test_choice_url :test_id => @test.id %>">New Choice</a>
|
61
|
+
</div>
|
62
|
+
</div>
|
63
|
+
</div>
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mountain-goat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 1
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
9
|
+
- 2
|
10
|
+
version: 1.0.2
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Geoffrey Hayes
|
@@ -22,9 +23,11 @@ dependencies:
|
|
22
23
|
name: rspec
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ~>
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
28
31
|
segments:
|
29
32
|
- 2
|
30
33
|
- 2
|
@@ -36,9 +39,11 @@ dependencies:
|
|
36
39
|
name: rack-test
|
37
40
|
prerelease: false
|
38
41
|
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
39
43
|
requirements:
|
40
44
|
- - ">="
|
41
45
|
- !ruby/object:Gem::Version
|
46
|
+
hash: 7
|
42
47
|
segments:
|
43
48
|
- 0
|
44
49
|
- 5
|
@@ -56,6 +61,7 @@ extensions: []
|
|
56
61
|
extra_rdoc_files: []
|
57
62
|
|
58
63
|
files:
|
64
|
+
- .gitignore
|
59
65
|
- Gemfile
|
60
66
|
- Gemfile.lock
|
61
67
|
- History.md
|
@@ -70,29 +76,42 @@ files:
|
|
70
76
|
- generators/mg/templates/mountain-goat.yml
|
71
77
|
- generators/mg/templates/mountain_goat_reports.rake
|
72
78
|
- generators/mg/templates/update_mountain_goat_tables.rb
|
79
|
+
- generators/mg/templates/update_mountain_goat_tables_v2.rb
|
73
80
|
- init.rb
|
74
81
|
- install.rb
|
75
82
|
- lib/mgflotilla.rb
|
76
83
|
- lib/mountain-goat.rb
|
77
84
|
- lib/mountain-goat/analytics.rb
|
85
|
+
- lib/mountain-goat/controllers/mg/choices_controller.rb
|
78
86
|
- lib/mountain-goat/controllers/mg/converts_controller.rb
|
87
|
+
- lib/mountain-goat/controllers/mg/goals_controller.rb
|
79
88
|
- lib/mountain-goat/controllers/mg/mg.rb
|
80
89
|
- lib/mountain-goat/controllers/mg/mountain_goat_controller.rb
|
81
90
|
- lib/mountain-goat/controllers/mg/playground_controller.rb
|
91
|
+
- lib/mountain-goat/controllers/mg/records_controller.rb
|
82
92
|
- lib/mountain-goat/controllers/mg/report_items_controller.rb
|
83
93
|
- lib/mountain-goat/controllers/mg/reports_controller.rb
|
94
|
+
- lib/mountain-goat/controllers/mg/tests_controller.rb
|
84
95
|
- lib/mountain-goat/m_g.rb
|
85
|
-
- lib/mountain-goat/
|
96
|
+
- lib/mountain-goat/mg_core.rb
|
97
|
+
- lib/mountain-goat/models/mg/choice.rb
|
98
|
+
- lib/mountain-goat/models/mg/gi_meta.rb
|
99
|
+
- lib/mountain-goat/models/mg/goal.rb
|
100
|
+
- lib/mountain-goat/models/mg/goal_meta_type.rb
|
101
|
+
- lib/mountain-goat/models/mg/gs_meta.rb
|
86
102
|
- lib/mountain-goat/models/mg/mountain_goat.rb
|
103
|
+
- lib/mountain-goat/models/mg/record.rb
|
87
104
|
- lib/mountain-goat/models/mg/report.rb
|
88
105
|
- lib/mountain-goat/models/mg/report_item.rb
|
89
106
|
- lib/mountain-goat/models/mg/report_mailer.rb
|
107
|
+
- lib/mountain-goat/models/mg/test.rb
|
90
108
|
- lib/mountain-goat/public/containerbg.png
|
91
109
|
- lib/mountain-goat/public/dirtyred.png
|
92
110
|
- lib/mountain-goat/public/dottedblack.png
|
93
111
|
- lib/mountain-goat/public/dottedblue.png
|
94
112
|
- lib/mountain-goat/public/g-bar-min.js
|
95
113
|
- lib/mountain-goat/public/g-dot-min.js
|
114
|
+
- lib/mountain-goat/public/g-funnel.js
|
96
115
|
- lib/mountain-goat/public/g-line-min.js
|
97
116
|
- lib/mountain-goat/public/g-pie-min.js
|
98
117
|
- lib/mountain-goat/public/g-raphael-min.js
|
@@ -107,14 +126,44 @@ files:
|
|
107
126
|
- lib/mountain-goat/public/raphael-min.js
|
108
127
|
- lib/mountain-goat/public/raster.png
|
109
128
|
- lib/mountain-goat/public/utils.js
|
110
|
-
- lib/mountain-goat/
|
129
|
+
- lib/mountain-goat/switch_choice.rb
|
111
130
|
- lib/mountain-goat/version.rb
|
131
|
+
- lib/mountain-goat/views/mountain_goat/layouts/.tmp_mountain_goat.html.erb.50680~
|
112
132
|
- lib/mountain-goat/views/mountain_goat/layouts/_pdf.html.erb
|
113
133
|
- lib/mountain-goat/views/mountain_goat/layouts/mountain_goat.html.erb
|
114
134
|
- lib/mountain-goat/views/mountain_goat/layouts/xhr.html.erb
|
135
|
+
- lib/mountain-goat/views/mountain_goat/mg/choices/_choice_form.html.erb
|
136
|
+
- lib/mountain-goat/views/mountain_goat/mg/choices/edit.html.erb
|
137
|
+
- lib/mountain-goat/views/mountain_goat/mg/choices/index.html.erb
|
138
|
+
- lib/mountain-goat/views/mountain_goat/mg/choices/new.html.erb
|
139
|
+
- lib/mountain-goat/views/mountain_goat/mg/choices/show.html.erb
|
140
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_form.html.erb.5027~
|
141
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_meta_type_form.html.erb.39992~
|
142
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_edit.html.erb.55874~
|
143
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_index.html.erb.97274~
|
144
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/_goal_form.html.erb
|
145
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/_goal_meta_type_form.html.erb
|
146
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/edit.html.erb
|
147
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/index.html.erb
|
148
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/new.html.erb
|
149
|
+
- lib/mountain-goat/views/mountain_goat/mg/goals/show.html.erb
|
115
150
|
- lib/mountain-goat/views/mountain_goat/mg/mountain_goat/login.html.erb
|
116
151
|
- lib/mountain-goat/views/mountain_goat/mg/playground/test.html.erb
|
152
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/_record.html.erb
|
153
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/_records.html.erb
|
154
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/_records_form.html.erb
|
155
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/edit.html.erb
|
156
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/index.html.erb
|
157
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/new.html.erb
|
158
|
+
- lib/mountain-goat/views/mountain_goat/mg/records/show.html.erb
|
159
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__chart.html.erb.13419~
|
160
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__funnel.html.erb.60493~
|
161
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_form.html.erb.87420~
|
162
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_pivot_form.html.erb.77056~
|
163
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_edit.html.erb.31048~
|
164
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_new.html.erb.36371~
|
117
165
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/_chart.html.erb
|
166
|
+
- lib/mountain-goat/views/mountain_goat/mg/report_items/_funnel.html.erb
|
118
167
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_form.html.erb
|
119
168
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_pivot_form.html.erb
|
120
169
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/_show.html.erb
|
@@ -122,6 +171,11 @@ files:
|
|
122
171
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/edit.html.erb
|
123
172
|
- lib/mountain-goat/views/mountain_goat/mg/report_items/new.html.erb
|
124
173
|
- lib/mountain-goat/views/mountain_goat/mg/report_mailer/report.html.erb
|
174
|
+
- lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_form.html.erb.76535~
|
175
|
+
- lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_report_items.html.erb.26030~
|
176
|
+
- lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_edit.html.erb.78064~
|
177
|
+
- lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_index.html.erb.74591~
|
178
|
+
- lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_show.html.erb.58427~
|
125
179
|
- lib/mountain-goat/views/mountain_goat/mg/reports/_report.html.erb
|
126
180
|
- lib/mountain-goat/views/mountain_goat/mg/reports/_report_form.html.erb
|
127
181
|
- lib/mountain-goat/views/mountain_goat/mg/reports/_report_report_items.html.erb
|
@@ -129,6 +183,11 @@ files:
|
|
129
183
|
- lib/mountain-goat/views/mountain_goat/mg/reports/index.html.erb
|
130
184
|
- lib/mountain-goat/views/mountain_goat/mg/reports/new.html.erb
|
131
185
|
- lib/mountain-goat/views/mountain_goat/mg/reports/show.html.erb
|
186
|
+
- lib/mountain-goat/views/mountain_goat/mg/tests/_test_form.html.erb
|
187
|
+
- lib/mountain-goat/views/mountain_goat/mg/tests/edit.html.erb
|
188
|
+
- lib/mountain-goat/views/mountain_goat/mg/tests/index.html.erb
|
189
|
+
- lib/mountain-goat/views/mountain_goat/mg/tests/new.html.erb
|
190
|
+
- lib/mountain-goat/views/mountain_goat/mg/tests/show.html.erb
|
132
191
|
- mountain-goat.gemspec
|
133
192
|
- test/fixtures/mg_ci_metas.yml
|
134
193
|
- test/fixtures/mg_convert_meta_types.yml
|
@@ -163,23 +222,27 @@ rdoc_options: []
|
|
163
222
|
require_paths:
|
164
223
|
- lib
|
165
224
|
required_ruby_version: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
166
226
|
requirements:
|
167
227
|
- - ">="
|
168
228
|
- !ruby/object:Gem::Version
|
229
|
+
hash: 3
|
169
230
|
segments:
|
170
231
|
- 0
|
171
232
|
version: "0"
|
172
233
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
234
|
+
none: false
|
173
235
|
requirements:
|
174
236
|
- - ">="
|
175
237
|
- !ruby/object:Gem::Version
|
238
|
+
hash: 3
|
176
239
|
segments:
|
177
240
|
- 0
|
178
241
|
version: "0"
|
179
242
|
requirements: []
|
180
243
|
|
181
244
|
rubyforge_project: mountain-goat
|
182
|
-
rubygems_version: 1.
|
245
|
+
rubygems_version: 1.4.2
|
183
246
|
signing_key:
|
184
247
|
specification_version: 3
|
185
248
|
summary: A/B Testing to the edge
|
@@ -1,401 +0,0 @@
|
|
1
|
-
require File.join([File.dirname(__FILE__), 'switch_variant'])
|
2
|
-
|
3
|
-
module MetricTracking
|
4
|
-
|
5
|
-
#Metric Tracking routes
|
6
|
-
class << ActionController::Routing::Routes;self;end.class_eval do
|
7
|
-
define_method :clear!, lambda {}
|
8
|
-
end
|
9
|
-
|
10
|
-
Mime::Type.register "application/xhtml+xml", :xhtml
|
11
|
-
|
12
|
-
ActionController::Routing::Routes.draw do |map|
|
13
|
-
map.namespace :mg do |mg|
|
14
|
-
mg.mg '/mg', :controller => :converts, :action => :index, :path_prefix => ""
|
15
|
-
mg.login '/login', :controller => :mountain_goat, :action => :login
|
16
|
-
mg.login_create '/login/create', :controller => :mountain_goat, :action => :login_create
|
17
|
-
mg.resources :metric_variants
|
18
|
-
mg.resources :converts, :has_many => [ :rallies ]
|
19
|
-
mg.resources :metrics, :has_many => :metric_variants
|
20
|
-
mg.resources :rallies, :collection => { :new_rallies => :get }
|
21
|
-
mg.resources :reports, :has_many => :report_items, :member => { :show_svg => :get }
|
22
|
-
mg.resources :report_items, :member => { :destroy => :get, :update => :post }, :collection => { :get_extra => :get }
|
23
|
-
mg.resources :playground, :collection => { :test => :get }
|
24
|
-
mg.new_rallies '/rallies/new', :controller => :rallies, :action => :new_rallies
|
25
|
-
mg.fresh_metrics '/fresh-metrics', :controller => :metrics, :action => :fresh_metrics
|
26
|
-
mg.connect '/public/:file', :controller => :mountain_goat, :action => :fetch
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module Controller
|
31
|
-
|
32
|
-
#This is just for testing
|
33
|
-
def mg_rand
|
34
|
-
return "(SELECT #{@mg_i.nil? ? 1 : @mg_i.to_f})" if defined?(MOUNTAIN_GOAT_TEST) && MOUNTAIN_GOAT_TEST
|
35
|
-
"RAND()"
|
36
|
-
end
|
37
|
-
|
38
|
-
def mg_epsilon
|
39
|
-
if @mg_epsilon.nil?
|
40
|
-
@mg_epsilon = 0.1 #default
|
41
|
-
mg_yml = nil
|
42
|
-
begin
|
43
|
-
mg_yml = YAML::load(File.open("#{RAILS_ROOT}/config/mountain-goat.yml"))
|
44
|
-
rescue
|
45
|
-
end
|
46
|
-
if mg_yml
|
47
|
-
if mg_yml.has_key?(RAILS_ENV) && mg_yml[RAILS_ENV].has_key?('epsilon')
|
48
|
-
@mg_epsilon = mg_yml[RAILS_ENV]['epsilon'].to_f
|
49
|
-
elsif mg_yml.has_key?('settings') && mg_yml['settings'].has_key?('epsilon')
|
50
|
-
@mg_epsilon = mg_yml['settings']['epsilon'].to_f
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
return @mg_epsilon
|
55
|
-
end
|
56
|
-
|
57
|
-
def mg_strategy
|
58
|
-
if @mg_strategy.nil?
|
59
|
-
@mg_strategy = 'e-greedy' #default
|
60
|
-
mg_yml = nil
|
61
|
-
begin
|
62
|
-
mg_yml = YAML::load(File.open("#{RAILS_ROOT}/config/mountain-goat.yml"))
|
63
|
-
rescue
|
64
|
-
end
|
65
|
-
if mg_yml
|
66
|
-
if mg_yml.has_key?(RAILS_ENV) && mg_yml[RAILS_ENV].has_key?('strategy')
|
67
|
-
@mg_strategy = mg_yml[RAILS_ENV]['strategy']
|
68
|
-
elsif mg_yml.has_key?('settings') && mg_yml['settings'].has_key?('strategy')
|
69
|
-
@mg_strategy = mg_yml['settings']['strategy']
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
return @mg_strategy
|
74
|
-
end
|
75
|
-
|
76
|
-
def mg_apply_strategy(metric)
|
77
|
-
case mg_strategy.downcase
|
78
|
-
when 'e-greedy'
|
79
|
-
logger.warn Mg::MetricVariant.all(:order => "CASE WHEN served = 0 THEN 1 ELSE 0 END DESC, CASE WHEN #{rand.to_f} < #{mg_epsilon.to_f} THEN #{mg_rand} ELSE CASE WHEN served = 0 THEN -1 ELSE reward / served END END DESC, #{mg_rand} DESC", :conditions => { :metric_id => metric.id } )
|
80
|
-
return Mg::MetricVariant.first(:order => "CASE WHEN served = 0 THEN 1 ELSE 0 END DESC, CASE WHEN #{rand.to_f} < #{mg_epsilon.to_f} THEN #{mg_rand} ELSE CASE WHEN served = 0 THEN -1 ELSE reward / served END END DESC, #{mg_rand} DESC", :conditions => { :metric_id => metric.id } )
|
81
|
-
when 'e-greedy-decreasing'
|
82
|
-
return Mg::MetricVariant.first(:order => "CASE WHEN served = 0 THEN 1 ELSE 0 END DESC,
|
83
|
-
CASE WHEN #{rand.to_f} < #{mg_epsilon.to_f} / ( select sum(served) from mg_metric_variants where metric_id = #{ metric.id.to_i } ) THEN #{mg_rand} ELSE CASE WHEN served = 0 THEN -1 ELSE reward / served END END DESC,
|
84
|
-
#{mg_rand} DESC", :conditions => { :metric_id => metric.id } ) # * log( ( select sum(served) from mg_metric_variants where metric_id = #{ metric.id.to_i } ) )
|
85
|
-
when 'a/b'
|
86
|
-
return Mg::MetricVariant.first(:order => "#{rand.to_f} DESC", :conditions => { :metric_id => metric.id } )
|
87
|
-
else
|
88
|
-
raise "Invalid strategy #{mg_strategy}"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def mg_storage
|
93
|
-
if @mg_storage.nil?
|
94
|
-
@mg_storage = defined?(cookies) ? cookies : nil
|
95
|
-
|
96
|
-
mg_yml = nil
|
97
|
-
begin
|
98
|
-
mg_yml = YAML::load(File.open("#{RAILS_ROOT}/config/mountain-goat.yml"))
|
99
|
-
rescue
|
100
|
-
end
|
101
|
-
if mg_yml
|
102
|
-
if mg_yml.has_key?(RAILS_ENV) && mg_yml[RAILS_ENV].has_key?('storage')
|
103
|
-
uc = mg_yml[RAILS_ENV]['storage'].strip
|
104
|
-
@mg_storage = ( uc == "cookies" && defined?(cookies) ) ? cookies : ( uc == "session" && defined?(session) ) ? session : nil
|
105
|
-
elsif mg_yml.has_key?('settings') && mg_yml['settings'].has_key?('storage')
|
106
|
-
uc = mg_yml['settings']['storage'].strip
|
107
|
-
@mg_storage = ( uc == "cookies" && defined?(cookies) ) ? cookies : ( uc == "session" && defined?(session) ) ? session : nil
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
@mg_storage = {} if @mg_storage.nil? #'none'
|
112
|
-
return @mg_storage
|
113
|
-
end
|
114
|
-
|
115
|
-
######################
|
116
|
-
# Metric Tracking #
|
117
|
-
######################
|
118
|
-
|
119
|
-
def bds(metric_type, &block)
|
120
|
-
raise ArgumentError, "Switch variant needs block" if !block_given?
|
121
|
-
metric = get_metric( metric_type, true )
|
122
|
-
block.call(SwitchVariant.new( logger, metric, nil ) )
|
123
|
-
|
124
|
-
var = get_switch_metric_variant( metric_type )
|
125
|
-
block.call(SwitchVariant.new( logger, metric, var ) )
|
126
|
-
end
|
127
|
-
|
128
|
-
def bd(metric_type, default, opts = {}, opt = nil)
|
129
|
-
return get_metric_variant(metric_type, default, opts, opt)[:value]
|
130
|
-
end
|
131
|
-
|
132
|
-
def bdd(metric_type, default, opts = {}, opt = nil)
|
133
|
-
return get_metric_variant(metric_type, default, opts, opt)
|
134
|
-
end
|
135
|
-
|
136
|
-
#Legacy
|
137
|
-
def sv(metric_type, convert_type, &block)
|
138
|
-
bds(metric_type, &block)
|
139
|
-
end
|
140
|
-
|
141
|
-
def mv(metric_type, convert_type, default, opts = {}, opt = nil)
|
142
|
-
bd(metric_type, default, opts, opt)
|
143
|
-
end
|
144
|
-
|
145
|
-
def mv_detailed(metric_type, convert_type, default, opts = {}, opt = nil)
|
146
|
-
bdd(metric_type, default, opts, opt)
|
147
|
-
end
|
148
|
-
|
149
|
-
#shorthand
|
150
|
-
def rw(convert_type, reward, options = {})
|
151
|
-
self.bandit_reward(convert_type, reward, options)
|
152
|
-
end
|
153
|
-
|
154
|
-
def rc(convert_type, options = {})
|
155
|
-
self.bandit_reward(convert_type, 1, options)
|
156
|
-
end
|
157
|
-
|
158
|
-
def record_conversion(convert_type, options = {})
|
159
|
-
self.bandit_reward(convert_type, 1, options)
|
160
|
-
end
|
161
|
-
|
162
|
-
#allows bandit_reward(convert, options)
|
163
|
-
def bandit_reward(convert_type, reward, options = {})
|
164
|
-
|
165
|
-
if reward.is_a?(Hash) #allow arguments bandit_reward(convert, options)
|
166
|
-
options = reward
|
167
|
-
reward = 0
|
168
|
-
end
|
169
|
-
|
170
|
-
metrics = {} #for user-defined metrics
|
171
|
-
options = options.with_indifferent_access
|
172
|
-
|
173
|
-
MountainGoat.get_meta_options.each do |k, v|
|
174
|
-
if options.include?(k) && options[k]
|
175
|
-
options.delete(k)
|
176
|
-
res = v.call(self)
|
177
|
-
options.merge!( res ) if !res.nil? && res.instance_of?(Hash)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
options.each do |k, v|
|
182
|
-
if k.to_s =~ /^metric_(\w+)$/i
|
183
|
-
options.delete k
|
184
|
-
metrics.merge!({ $1, v })
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
logger.warn "Recording conversion #{convert_type.to_s} with options #{options.inspect}"
|
189
|
-
|
190
|
-
convert = Mg::Convert.first( :conditions => { :convert_type => convert_type.to_s } )
|
191
|
-
|
192
|
-
#now, we just create the convert if we don't have one
|
193
|
-
convert = Mg::Convert.create!( :convert_type => convert_type.to_s, :name => convert_type.to_s, :reward => reward ) if convert.nil?
|
194
|
-
|
195
|
-
#first, let's tally for the conversion itself
|
196
|
-
#we need to see what meta information we should fill based on the conversion type
|
197
|
-
Mg::Rally.create!( { :convert_id => convert.id } ).set_meta_data(options)
|
198
|
-
|
199
|
-
#User-defined metric tallies
|
200
|
-
metrics.each do |metric_type, variant_id|
|
201
|
-
m = Mg::Metric.find_by_metric_type(metric_type)
|
202
|
-
if m.nil?
|
203
|
-
logger.warn "Missing user-defined metric #{metric_type}"
|
204
|
-
next
|
205
|
-
end
|
206
|
-
|
207
|
-
v = m.metric_variants.first( :conditions => { :id => variant_id } ) #make sure everything matches up
|
208
|
-
|
209
|
-
if v.nil?
|
210
|
-
logger.warn "Variant #{variant_id} not in metric variants for #{m.title}"
|
211
|
-
next
|
212
|
-
end
|
213
|
-
|
214
|
-
logger.warn "Tallying conversion #{convert.name} for #{m.title} - #{v.name} (#{v.value} - #{v.id})"
|
215
|
-
v.tally_convert(convert, reward)
|
216
|
-
end
|
217
|
-
|
218
|
-
if !mg_storage.nil?
|
219
|
-
#we just converted, let's tally each of our metrics (from cookies or session)
|
220
|
-
Mg::Metric.all.each do |metric|
|
221
|
-
metric_sym = "metric_#{metric.metric_type}".to_sym
|
222
|
-
metric_variant_sym = "metric_#{metric.metric_type}_variant".to_sym
|
223
|
-
|
224
|
-
value = mg_storage[metric_sym]
|
225
|
-
variant_id = mg_storage[metric_variant_sym]
|
226
|
-
|
227
|
-
#logger.warn "Value: #{metric_sym} - #{value}"
|
228
|
-
#logger.warn "Value: #{metric_variant_sym} - #{variant_id}"
|
229
|
-
|
230
|
-
if variant_id.blank? #the user just doesn't have this set
|
231
|
-
#This is now common-case
|
232
|
-
next
|
233
|
-
end
|
234
|
-
|
235
|
-
variant = Mg::MetricVariant.first(:conditions => { :id => variant_id.to_i } )
|
236
|
-
|
237
|
-
if variant.nil?
|
238
|
-
logger.error "Variant #{variant_id} not in metric variants for #{metric.title}"
|
239
|
-
next
|
240
|
-
end
|
241
|
-
|
242
|
-
if variant.value != value
|
243
|
-
logger.warn "Variant #{variant.name} values differ for metric #{metric.title}. '#{variant.value}' != '#{value}'!"
|
244
|
-
end
|
245
|
-
|
246
|
-
logger.warn "Tallying conversion #{convert.name} for #{metric.title} - #{variant.name} (#{variant.value} - #{variant.id})"
|
247
|
-
variant.tally_convert(convert, reward)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
private
|
253
|
-
|
254
|
-
#returns a map { :value => value, :variant_id => id }
|
255
|
-
def get_metric_variant(metric_type, default, opts = {}, opt = nil)
|
256
|
-
metric_sym = "metric_#{metric_type}#{ opt.nil? ? "" : '_' + opt.to_s }".to_sym
|
257
|
-
metric_variant_sym = "metric_#{metric_type}_variant".to_sym
|
258
|
-
|
259
|
-
#first, we'll check for a cookie value
|
260
|
-
if !mg_storage.nil? && mg_storage[metric_sym] && !mg_storage[metric_sym].blank?
|
261
|
-
#we have the cookie
|
262
|
-
variant_id = mg_storage[metric_variant_sym]
|
263
|
-
variant = Mg::MetricVariant.first(:conditions => { :id => variant_id.to_i } )
|
264
|
-
if !variant.nil?
|
265
|
-
if variant.metric.tally_each_serve
|
266
|
-
variant.tally_serve
|
267
|
-
end
|
268
|
-
else
|
269
|
-
logger.warn "Serving metric #{metric_type} #{ opt.nil? ? "" : opt.to_s } without finding / tallying variant."
|
270
|
-
end
|
271
|
-
|
272
|
-
return { :value => mg_storage[metric_sym], :variant_id => mg_storage[metric_variant_sym] } #it's the best we can do
|
273
|
-
else
|
274
|
-
#we don't have the cookie, let's find a value to set
|
275
|
-
metric = get_metric( metric_type, false )
|
276
|
-
|
277
|
-
metric_variant = mg_apply_strategy(metric)
|
278
|
-
|
279
|
-
if metric_variant.nil?
|
280
|
-
logger.warn "Missing metric variants for #{metric_type}"
|
281
|
-
metric_variant = Mg::MetricVariant.create!( { :metric_id => metric.id, :value => default, :name => default }.merge(opts) )
|
282
|
-
end
|
283
|
-
|
284
|
-
if metric_variant.metric.tally_each_serve
|
285
|
-
metric_variant.tally_serve #donate we served this to a user
|
286
|
-
end
|
287
|
-
|
288
|
-
value = metric_variant.read_attribute( opt.nil? ? :value : opt )
|
289
|
-
logger.debug "Serving #{metric_variant.name} (#{value}) for #{metric_sym}"
|
290
|
-
#good, we have a variant, let's store it in session
|
291
|
-
|
292
|
-
if !mg_storage.nil?
|
293
|
-
mg_storage[metric_sym] = value #, :domain => WILD_DOMAIN
|
294
|
-
mg_storage[metric_variant_sym] = metric_variant.id #, :domain => WILD_DOMAIN
|
295
|
-
end
|
296
|
-
|
297
|
-
return { :value => value, :variant_id => metric_variant.id }
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
def get_switch_metric_variant(metric_type)
|
302
|
-
metric_variant_sym = "metric_#{metric_type}_variant".to_sym
|
303
|
-
|
304
|
-
#first, we'll check for a cookie selection
|
305
|
-
if !mg_storage.nil? && mg_storage[metric_variant_sym] && !mg_storage[metric_variant_sym].blank?
|
306
|
-
#we have the cookie
|
307
|
-
|
308
|
-
variant_id = mg_storage[metric_variant_sym]
|
309
|
-
variant = Mg::MetricVariant.first(:conditions => { :id => variant_id.to_i } )
|
310
|
-
if !variant.nil?
|
311
|
-
|
312
|
-
if variant.metric.tally_each_serve
|
313
|
-
variant.tally_serve
|
314
|
-
end
|
315
|
-
|
316
|
-
return variant
|
317
|
-
|
318
|
-
end
|
319
|
-
|
320
|
-
#otherwise, it's a big wtf? let's just move on
|
321
|
-
logger.warn "Missing metric variant for #{metric_type} (switch-type), reassigning..."
|
322
|
-
end
|
323
|
-
|
324
|
-
#we don't have the cookie, let's find a value to set
|
325
|
-
metric = get_metric( metric_type, true )
|
326
|
-
|
327
|
-
metric_variant = mg_apply_strategy(metric)
|
328
|
-
|
329
|
-
if metric_variant.nil?
|
330
|
-
logger.warn "Missing metric variants for #{metric_type}"
|
331
|
-
raise ArgumentError, "Missing variants for switch-type #{metric_type}"
|
332
|
-
end
|
333
|
-
|
334
|
-
if metric_variant.metric.tally_each_serve
|
335
|
-
metric_variant.tally_serve #donate we served this to a user
|
336
|
-
end
|
337
|
-
|
338
|
-
logger.debug "Serving #{metric_variant.name} (#{metric_variant.switch_type}) for #{metric.title} (switch-type)"
|
339
|
-
#good, we have a variant, let's store it in session (not the value, just the selection)
|
340
|
-
if !mg_storage.nil?
|
341
|
-
mg_storage[metric_variant_sym] = metric_variant.id #, :domain => WILD_DOMAIN
|
342
|
-
end
|
343
|
-
|
344
|
-
return metric_variant
|
345
|
-
end
|
346
|
-
|
347
|
-
def get_metric(metric_type, is_switch = false)
|
348
|
-
|
349
|
-
metric = Mg::Metric.first(:conditions => { :metric_type => metric_type.to_s } )
|
350
|
-
|
351
|
-
if metric.nil? #we don't have a metric of this type
|
352
|
-
logger.warn "Missing metric type #{metric_type.to_s} -- creating"
|
353
|
-
metric = Mg::Metric.create( :metric_type => metric_type.to_s, :title => metric_type.to_s, :is_switch => is_switch )
|
354
|
-
end
|
355
|
-
|
356
|
-
return metric
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
module View
|
361
|
-
def mv(*args, &block)
|
362
|
-
@controller.send(:mv, *args, &block)
|
363
|
-
end
|
364
|
-
|
365
|
-
def mv_detailed(*args, &block)
|
366
|
-
@controller.send(:mv_detailed, *args, &block)
|
367
|
-
end
|
368
|
-
|
369
|
-
def sv(*args, &block)
|
370
|
-
@controller.send(:sv, *args, &block)
|
371
|
-
end
|
372
|
-
|
373
|
-
def bd(*args, &block)
|
374
|
-
@controller.send(:bd, *args, &block)
|
375
|
-
end
|
376
|
-
|
377
|
-
def bdd(*args, &block)
|
378
|
-
@controller.send(:bdd, *args, &block)
|
379
|
-
end
|
380
|
-
|
381
|
-
def bds(*args, &block)
|
382
|
-
@controller.send(:bds, *args, &block)
|
383
|
-
end
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
class ActionController::Base
|
388
|
-
include MetricTracking::Controller
|
389
|
-
end
|
390
|
-
|
391
|
-
class ActionView::Base
|
392
|
-
include MetricTracking::View
|
393
|
-
end
|
394
|
-
|
395
|
-
class ActionMailer::Base
|
396
|
-
include MetricTracking::Controller
|
397
|
-
end
|
398
|
-
|
399
|
-
class ActiveRecord::Base
|
400
|
-
include MetricTracking::Controller
|
401
|
-
end
|