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.
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