mountain-goat 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|