vanity 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/Appraisals +6 -6
- data/CHANGELOG +9 -3
- data/Gemfile.lock +1 -1
- data/README.md +299 -0
- data/doc/configuring.textile +8 -1
- data/doc/identity.textile +2 -0
- data/doc/metrics.textile +10 -0
- data/gemfiles/rails32.gemfile.lock +1 -1
- data/gemfiles/rails41.gemfile.lock +1 -1
- data/gemfiles/rails42.gemfile.lock +1 -1
- data/gemfiles/{rails4.gemfile → rails42_protected_attributes.gemfile} +2 -2
- data/gemfiles/rails42_protected_attributes.gemfile.lock +209 -0
- data/lib/generators/templates/vanity_migration.rb +1 -0
- data/lib/vanity/adapters/abstract_adapter.rb +11 -0
- data/lib/vanity/adapters/active_record_adapter.rb +15 -1
- data/lib/vanity/adapters/mock_adapter.rb +14 -0
- data/lib/vanity/adapters/mongodb_adapter.rb +14 -0
- data/lib/vanity/adapters/redis_adapter.rb +15 -0
- data/lib/vanity/configuration.rb +43 -11
- data/lib/vanity/experiment/ab_test.rb +145 -15
- data/lib/vanity/experiment/alternative.rb +4 -0
- data/lib/vanity/frameworks/rails.rb +69 -31
- data/lib/vanity/locales/vanity.en.yml +9 -0
- data/lib/vanity/locales/vanity.pt-BR.yml +4 -0
- data/lib/vanity/metric/active_record.rb +9 -1
- data/lib/vanity/templates/_ab_test.erb +9 -2
- data/lib/vanity/templates/_experiment.erb +21 -1
- data/lib/vanity/templates/vanity.css +11 -3
- data/lib/vanity/templates/vanity.js +35 -6
- data/lib/vanity/version.rb +1 -1
- data/test/commands/report_test.rb +1 -0
- data/test/dummy/config/application.rb +1 -0
- data/test/experiment/ab_test.rb +414 -0
- data/test/experiment/base_test.rb +16 -10
- data/test/frameworks/rails/action_controller_test.rb +14 -6
- data/test/frameworks/rails/action_mailer_test.rb +8 -6
- data/test/frameworks/rails/action_view_test.rb +1 -0
- data/test/helper_test.rb +2 -0
- data/test/metric/active_record_test.rb +56 -0
- data/test/playground_test.rb +3 -0
- data/test/test_helper.rb +28 -2
- data/test/web/rails/dashboard_test.rb +2 -0
- data/vanity.gemspec +2 -2
- metadata +8 -8
- data/README.rdoc +0 -231
- data/gemfiles/rails4.gemfile.lock +0 -179
@@ -49,6 +49,8 @@ module Vanity
|
|
49
49
|
@ar_timestamp, @ar_timestamp_table = @ar_timestamp.to_s.split('.').reverse
|
50
50
|
@ar_timestamp_table ||= @ar_scoped.table_name
|
51
51
|
|
52
|
+
@ar_identity_block = options.delete(:identity)
|
53
|
+
|
52
54
|
fail "Unrecognized options: #{options.keys * ", "}" unless options.empty?
|
53
55
|
|
54
56
|
@ar_scoped.after_create(self)
|
@@ -99,7 +101,13 @@ module Vanity
|
|
99
101
|
def after_create(record)
|
100
102
|
return unless @playground.collecting?
|
101
103
|
count = @ar_column ? (record.send(@ar_column) || 0) : 1
|
102
|
-
|
104
|
+
|
105
|
+
identity = Vanity.context.vanity_identity rescue nil
|
106
|
+
identity ||= if @ar_identity_block
|
107
|
+
@ar_identity_block.call(record)
|
108
|
+
end
|
109
|
+
|
110
|
+
call_hooks record.send(@ar_timestamp), identity, [count] if count > 0 && @ar_scoped.exists?(record.id)
|
103
111
|
end
|
104
112
|
end
|
105
113
|
end
|
@@ -2,10 +2,17 @@
|
|
2
2
|
<table>
|
3
3
|
<caption>
|
4
4
|
<%= experiment.conclusion(score).join(" ") %>
|
5
|
+
<% if experiment.active? && !experiment.enabled? %>
|
6
|
+
<div class='disabled_info'><%=I18n.t('vanity.experiment_has_been_disabled', name: experiment.default.name)%></div>
|
7
|
+
<% end %>
|
5
8
|
</caption>
|
6
9
|
<% score.alts.each do |alt| %>
|
7
10
|
<tr class="<%= "choice" if score.choice == alt %>">
|
8
|
-
<td class="option"><%= alt.name.gsub(/^o/, "O")
|
11
|
+
<td class="option"><%= alt.name.gsub(/^o/, "O") %>:
|
12
|
+
<% if alt.default? %>
|
13
|
+
<div class='default'><%=I18n.t('vanity.default')%></div>
|
14
|
+
<% end %>
|
15
|
+
</td>
|
9
16
|
<td class="value"><code><%=vanity_h alt.value.to_s %></code></td>
|
10
17
|
<td class="value"><%= I18n.t 'vanity.participants', :count=>alt.participants %></td>
|
11
18
|
<td class="value"><%= I18n.t 'vanity.converted', :count=>alt.converted %></td>
|
@@ -14,7 +21,7 @@
|
|
14
21
|
<%= I18n.t('vanity.best_alternative', :probabilty=>alt.probability.to_i) if score.method == :bayes_score %>
|
15
22
|
<%= I18n.t('vanity.better_alternative_than', :probability=>alt.difference.to_i, :alternative=>score.least.name) if alt.difference && alt.difference >= 1 %>
|
16
23
|
</td>
|
17
|
-
<% if experiment.active? && respond_to?(:url_for) %>
|
24
|
+
<% if experiment.enabled? && experiment.active? && respond_to?(:url_for) %>
|
18
25
|
<td class="action">
|
19
26
|
<small>
|
20
27
|
<% if experiment.showing?(alt) %>
|
@@ -1,4 +1,24 @@
|
|
1
|
-
|
1
|
+
<% if experiment.active? %>
|
2
|
+
<% status = experiment.enabled? ? 'status_enabled' : 'status_disabled' %>
|
3
|
+
<% else %>
|
4
|
+
<% status = 'status_completed' %>
|
5
|
+
<% end %>
|
6
|
+
<div class="inner <%= status %>">
|
7
|
+
<h3>
|
8
|
+
<%=vanity_h experiment.name %><span class="type">(<%= experiment.class.friendly_name %>)</span>
|
9
|
+
<% if experiment.type == 'ab_test' && experiment.active? && experiment.playground.collecting? %>
|
10
|
+
<span class='enabled-links'>
|
11
|
+
<% action = experiment.enabled? ? :disable : :enable %>
|
12
|
+
|
13
|
+
<% if experiment.enabled? %> <%= I18n.t( 'vanity.enabled' ) %> | <% end %>
|
14
|
+
<a title='<%=I18n.t( action, scope: 'vanity.act_on_this_experiment' )%>' href='#'
|
15
|
+
data-id='<%= experiment.id %>' data-url='<%= url_for(:action=>action, :e => experiment.id) %>'>
|
16
|
+
<%= action %></a>
|
17
|
+
<% if !experiment.enabled? %> | <%= I18n.t( 'vanity.disabled' ) %> <% end %>
|
18
|
+
|
19
|
+
</span>
|
20
|
+
<% end %>
|
21
|
+
</h3>
|
2
22
|
<%= experiment.description.to_s.split(/\n\s*\n/).map { |para| vanity_html_safe(%{<p class="description">#{vanity_h para}</p>}) }.join.html_safe %>
|
3
23
|
<% if flash.notice %>
|
4
24
|
<p>
|
@@ -7,14 +7,22 @@
|
|
7
7
|
.vanity .experiments { list-style: none; margin: 0; padding: 0 }
|
8
8
|
.vanity .experiment { margin: 1em 0 2em 0; border-bottom: 1px solid #ddd }
|
9
9
|
.vanity .experiment .type { margin-left: .3em; color: #bbb; font-size: .8em; font-weight: normal }
|
10
|
+
.vanity .experiment .inner {padding: 1em}
|
11
|
+
.vanity .experiment .status_completed { background: #EEF }
|
12
|
+
.vanity .experiment .status_enabled { background: #EFE }
|
13
|
+
.vanity .experiment .status_disabled { background: #FEE }
|
14
|
+
.vanity .experiment .enabled-links { float: right; font-weight: normal }
|
10
15
|
|
11
16
|
.vanity .ab_test table { border-collapse: collapse; table-layout: fixed; width: 100%; border-bottom: 1px solid #ccc; margin: 1em 0 0 0 }
|
12
17
|
.vanity .ab_test td { padding: .5em; border-top: 1px solid #ccc; width: 2em; overflow: hidden }
|
13
18
|
.vanity .ab_test .choice td { font-weight: bold; background: #f0f0f8 }
|
14
19
|
.vanity .ab_test caption { caption-side: bottom; padding: .5em; background: transparent; text-align: left }
|
15
|
-
.vanity .ab_test
|
16
|
-
.vanity .ab_test td.
|
17
|
-
.vanity .ab_test td.
|
20
|
+
.vanity .ab_test caption .disabled_info { padding-top: .5em }
|
21
|
+
.vanity .ab_test td.option { width: 5em; white-space: nowrap; overflow: hidden }
|
22
|
+
.vanity .ab_test td.option .default { font-size: 75% }
|
23
|
+
.vanity .ab_test td.value { width: 8em; white-space: nowrap; overflow: hidden }
|
24
|
+
.vanity .ab_test td.action { width: 6em; overflow: hidden; text-align: right }
|
25
|
+
.vanity .ab_test button.reset { float: right }
|
18
26
|
|
19
27
|
.vanity .metrics { list-style: none; margin: 0; padding: 0; border-bottom: 1px solid #ddd }
|
20
28
|
.vanity .metric { margin: 2em 0 }
|
@@ -70,13 +70,42 @@ $(function() {
|
|
70
70
|
});
|
71
71
|
});
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
function post_on_click(sel) {
|
74
|
+
$(sel).live("click", function() {
|
75
|
+
var link = $(this);
|
76
|
+
$.ajax({
|
77
|
+
data: 'authenticity_token=' + encodeURIComponent(document.auth_token),
|
78
|
+
success: function(request){ $('#experiment_' + link.attr("data-id")).html(request) },
|
79
|
+
url: link.attr("data-url"), type: 'post'
|
80
|
+
});
|
81
|
+
return false;
|
79
82
|
});
|
83
|
+
}
|
84
|
+
|
85
|
+
post_on_click(".experiment.ab_test a.button.chooses");
|
86
|
+
post_on_click(".experiment.ab_test .enabled-links a");
|
87
|
+
|
88
|
+
$(".experiment button.reset").live("click", function() {
|
89
|
+
if (confirm('Are you sure you want to reset the experiment? This will clear all collected data so far and restart the experiment from scratch. This cannot be undone.')){
|
90
|
+
var link = $(this);
|
91
|
+
$.ajax({
|
92
|
+
data: 'authenticity_token=' + encodeURIComponent(document.auth_token),
|
93
|
+
success: function(request){ $('#experiment_' + link.attr("data-id")).html(request) },
|
94
|
+
url: link.attr("data-url"), type: 'post'
|
95
|
+
});
|
96
|
+
}
|
97
|
+
return false;
|
98
|
+
});
|
99
|
+
|
100
|
+
$(".experiment button.finish").live("click", function() {
|
101
|
+
var link = $(this);
|
102
|
+
if (confirm('Are you sure you want to complete the experiment and set ' + link.attr("alt-name") + ' as the outcome?')){
|
103
|
+
$.ajax({
|
104
|
+
data: 'authenticity_token=' + encodeURIComponent(document.auth_token),
|
105
|
+
success: function(request){ $('#experiment_' + link.attr("data-id")).html(request) },
|
106
|
+
url: link.attr("data-url"), type: 'post'
|
107
|
+
});
|
108
|
+
}
|
80
109
|
return false;
|
81
110
|
});
|
82
111
|
|
data/lib/vanity/version.rb
CHANGED
@@ -40,6 +40,7 @@ module Dummy
|
|
40
40
|
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
|
41
41
|
# parameters by using an attr_accessible or attr_protected declaration.
|
42
42
|
config.active_record.whitelist_attributes = true if ActiveRecord::Base.respond_to?(:whitelist_attributes=)
|
43
|
+
config.active_record.mass_assignment_sanitizer = :strict if defined?(ProtectedAttributes)
|
43
44
|
|
44
45
|
# Configure the default encoding used in templates for Ruby 1.9.
|
45
46
|
config.encoding = "utf-8"
|