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.
Files changed (62) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +5 -4
  3. data/generators/mg/mg_generator.rb +2 -2
  4. data/generators/mg/templates/update_mountain_goat_tables_v2.rb +73 -0
  5. data/lib/mountain-goat/controllers/mg/choices_controller.rb +81 -0
  6. data/lib/mountain-goat/controllers/mg/goals_controller.rb +136 -0
  7. data/lib/mountain-goat/controllers/mg/records_controller.rb +46 -0
  8. data/lib/mountain-goat/controllers/mg/tests_controller.rb +139 -0
  9. data/lib/mountain-goat/mg_core.rb +403 -0
  10. data/lib/mountain-goat/models/mg/choice.rb +50 -0
  11. data/lib/mountain-goat/models/mg/gi_meta.rb +10 -0
  12. data/lib/mountain-goat/models/mg/goal.rb +183 -0
  13. data/lib/mountain-goat/models/mg/goal_meta_type.rb +20 -0
  14. data/lib/mountain-goat/models/mg/gs_meta.rb +10 -0
  15. data/lib/mountain-goat/models/mg/record.rb +59 -0
  16. data/lib/mountain-goat/models/mg/test.rb +31 -0
  17. data/lib/mountain-goat/public/g-funnel.js +168 -0
  18. data/lib/mountain-goat/switch_choice.rb +32 -0
  19. data/lib/mountain-goat/version.rb +1 -1
  20. data/lib/mountain-goat/views/mountain_goat/layouts/.tmp_mountain_goat.html.erb.50680~ +76 -0
  21. data/lib/mountain-goat/views/mountain_goat/mg/choices/_choice_form.html.erb +37 -0
  22. data/lib/mountain-goat/views/mountain_goat/mg/choices/edit.html.erb +13 -0
  23. data/lib/mountain-goat/views/mountain_goat/mg/choices/index.html.erb +34 -0
  24. data/lib/mountain-goat/views/mountain_goat/mg/choices/new.html.erb +15 -0
  25. data/lib/mountain-goat/views/mountain_goat/mg/choices/show.html.erb +30 -0
  26. data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_form.html.erb.5027~ +0 -0
  27. data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp__goal_meta_type_form.html.erb.39992~ +0 -0
  28. data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_edit.html.erb.55874~ +0 -0
  29. data/lib/mountain-goat/views/mountain_goat/mg/goals/.tmp_index.html.erb.97274~ +36 -0
  30. data/lib/mountain-goat/views/mountain_goat/mg/goals/_goal_form.html.erb +28 -0
  31. data/lib/mountain-goat/views/mountain_goat/mg/goals/_goal_meta_type_form.html.erb +34 -0
  32. data/lib/mountain-goat/views/mountain_goat/mg/goals/edit.html.erb +12 -0
  33. data/lib/mountain-goat/views/mountain_goat/mg/goals/index.html.erb +37 -0
  34. data/lib/mountain-goat/views/mountain_goat/mg/goals/new.html.erb +12 -0
  35. data/lib/mountain-goat/views/mountain_goat/mg/goals/show.html.erb +32 -0
  36. data/lib/mountain-goat/views/mountain_goat/mg/records/_record.html.erb +16 -0
  37. data/lib/mountain-goat/views/mountain_goat/mg/records/_records.html.erb +5 -0
  38. data/lib/mountain-goat/views/mountain_goat/mg/records/_records_form.html.erb +21 -0
  39. data/lib/mountain-goat/views/mountain_goat/mg/records/edit.html.erb +13 -0
  40. data/lib/mountain-goat/views/mountain_goat/mg/records/index.html.erb +17 -0
  41. data/lib/mountain-goat/views/mountain_goat/mg/records/new.html.erb +13 -0
  42. data/lib/mountain-goat/views/mountain_goat/mg/records/show.html.erb +14 -0
  43. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__chart.html.erb.13419~ +18 -0
  44. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__funnel.html.erb.60493~ +18 -0
  45. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_form.html.erb.87420~ +10 -0
  46. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp__report_item_pivot_form.html.erb.77056~ +14 -0
  47. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_edit.html.erb.31048~ +19 -0
  48. data/lib/mountain-goat/views/mountain_goat/mg/report_items/.tmp_new.html.erb.36371~ +17 -0
  49. data/lib/mountain-goat/views/mountain_goat/mg/report_items/_funnel.html.erb +13 -0
  50. data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_form.html.erb.76535~ +21 -0
  51. data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp__report_report_items.html.erb.26030~ +5 -0
  52. data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_edit.html.erb.78064~ +36 -0
  53. data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_index.html.erb.74591~ +23 -0
  54. data/lib/mountain-goat/views/mountain_goat/mg/reports/.tmp_show.html.erb.58427~ +21 -0
  55. data/lib/mountain-goat/views/mountain_goat/mg/tests/_test_form.html.erb +25 -0
  56. data/lib/mountain-goat/views/mountain_goat/mg/tests/edit.html.erb +12 -0
  57. data/lib/mountain-goat/views/mountain_goat/mg/tests/index.html.erb +48 -0
  58. data/lib/mountain-goat/views/mountain_goat/mg/tests/new.html.erb +12 -0
  59. data/lib/mountain-goat/views/mountain_goat/mg/tests/show.html.erb +63 -0
  60. metadata +69 -6
  61. data/lib/mountain-goat/metric_tracking.rb +0 -401
  62. 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
- prerelease: false
4
+ hash: 19
5
+ prerelease:
5
6
  segments:
6
7
  - 1
7
8
  - 0
8
- - 1
9
- version: 1.0.1
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/metric_tracking.rb
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/switch_variant.rb
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.3.6
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