split-counters 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3f6a298b34fbb6e817f9f200bcd4789a180a1d2
4
+ data.tar.gz: 4dead8b7639298a0795c6f3863065c205abe7876
5
+ SHA512:
6
+ metadata.gz: 5e43a0776886cc1b5ef7a1e9182becd3a5dc4999eacc8a7b02ec957e3a58629961cec0657b7b71caedfe7393718f63078d286b59f9249844ce4d4e9f7c598188
7
+ data.tar.gz: 69f8f55e94dfc7d4ac8c6d0cf030cf33638c92de65d0f1b95d31bcab9555417c4ea08dad8a691547fce644d813b552100f31860f579266ddc2d9e162771675bf
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Bernard Kroes
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task :default => :spec
data/Readme.mdown ADDED
@@ -0,0 +1,97 @@
1
+ # Split::Counters
2
+
3
+ An extension to [Split](http://github.com/andrew/split) to add counters per experiment and alternative.
4
+
5
+ ## Requirements
6
+
7
+ The split gem and its dependencies.
8
+
9
+ ## Setup
10
+
11
+ If you are using bundler add split to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'split-counters', :require => 'split/counters'
15
+ ```
16
+
17
+ and if you want to use the separate 'counters' dashboard:
18
+
19
+ ```ruby
20
+ gem 'split-counters', :require => ['split/counters', 'split/countersdashboard']
21
+ ```
22
+
23
+ Then run:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ Otherwise install the gem:
30
+
31
+ ```bash
32
+ gem install split-counters
33
+ ```
34
+
35
+ and require it in your project:
36
+
37
+ ```ruby
38
+ require 'split/counters'
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ A simple example in a Rails app could be:
44
+ ```ruby
45
+ def index
46
+ the_ab_test = ab_test("exp", "alt1", "alt2")
47
+
48
+ ab_counter_inc("IndexViews", "exp", the_ab_test)
49
+ ab_counter_inc("PageViews", "exp", ab_test("exp", "alt1", "alt2"))
50
+ end
51
+
52
+ def other
53
+ the_ab_test = ab_test("exp", "alt1", "alt2")
54
+
55
+ ab_counter_inc("OtherViews", "exp", the_ab_test)
56
+ ab_counter_inc("PageViews", "exp", ab_test("exp", "alt1", "alt2"))
57
+ end
58
+ ```
59
+
60
+ ## Counter Dashboard
61
+
62
+ ![Counters Dashboard](/screenshots/screenshot1.png?raw=true "Counters Dashboard")
63
+
64
+ To enable the 'counters' dashboard (and the Split dashboard), add this to your routes:
65
+
66
+ ```ruby
67
+ class Whitelist
68
+ def matches?(request)
69
+ (request.remote_ip == '127.0.0.1') || (Rails.env == 'development')
70
+ end
71
+ end
72
+ mount Split::Dashboard, :at => 'split', :constraints => Whitelist.new
73
+ mount Split::Countersdashboard, :at => 'splitcounters', :constraints => Whitelist.new
74
+ ```
75
+
76
+ This will make the counters dashboard available at /splitcounters (in development mode or from 127.0.0.1).
77
+
78
+ ## Development
79
+
80
+ Source hosted at [GitHub](http://github.com/bernardkroes/split-counters).
81
+ Report Issues/Feature requests on [GitHub Issues](http://github.com/bernardkroes/split-counters/issues).
82
+
83
+ Tests can be run with `rake spec`
84
+
85
+ ### Note on Patches/Pull Requests
86
+
87
+ * Fork the project.
88
+ * Make your feature addition or bug fix.
89
+ * Add tests for it. This is important so I don't break it in a
90
+ future version unintentionally.
91
+ * Commit, do not mess with rakefile, version, or history.
92
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
93
+ * Send me a pull request. Bonus points for topic branches.
94
+
95
+ ## Copyright
96
+
97
+ Copyright (c) 2014 Bernard Kroes. See LICENSE for details.
@@ -0,0 +1,5 @@
1
+ module Split
2
+ module Counters
3
+ VERSION = "0.3.0"
4
+ end
5
+ end
@@ -0,0 +1,69 @@
1
+ require "split/helper"
2
+
3
+ module Split
4
+ module CounterHelper
5
+ def ab_counter_inc(counter_name, experiment, alternative)
6
+ begin
7
+ Split::Counters.inc(counter_name, experiment, alternative)
8
+ rescue => e
9
+ raise(e) unless Split.configuration.db_failover
10
+ end
11
+ end
12
+ end
13
+
14
+ module Counters
15
+ def self.hash_name_for_name(in_name)
16
+ "co:#{in_name}"
17
+ end
18
+
19
+ def self.keyname_for_experiment_and_alternative(experiment, alternative)
20
+ [experiment.gsub(":", ""), alternative.gsub(":", "")].join(':')
21
+ end
22
+
23
+ def self.inc(name, experiment, alternative)
24
+ Split.redis.hincrby(Split::Counters.hash_name_for_name(name), Split::Counters.keyname_for_experiment_and_alternative(experiment, alternative), 1)
25
+ end
26
+
27
+ def self.current_value(name, experiment, alternative)
28
+ Split.redis.hget(Split::Counters.hash_name_for_name(name), Split::Counters.keyname_for_experiment_and_alternative(experiment, alternative))
29
+ end
30
+
31
+ def self.exists?(name)
32
+ Split.redis.exists(Split::Counters.hash_name_for_name(name))
33
+ end
34
+
35
+ def self.delete(name)
36
+ Split.redis.del(Split::Counters.hash_name_for_name(name))
37
+ end
38
+
39
+ def self.reset(name, experiment, alternative)
40
+ Split.redis.hdel(Split::Counters.hash_name_for_name(name), Split::Counters.keyname_for_experiment_and_alternative(experiment, alternative))
41
+ end
42
+
43
+ def self.all_values_hash(name)
44
+ return_hash = {}
45
+ result_hash = Split.redis.hgetall(self.hash_name_for_name(name)) # {"exp1:alt1"=>"1", "exp1:alt2"=>"2", "exp2:alt1"=>"1", "exp2:alt2"=>"2"}
46
+ result_hash.each do |key, value|
47
+ experiment, alternative = key.split(":")
48
+ return_hash[experiment] ||= Hash.new
49
+ return_hash[experiment].merge!({ alternative => value.to_i })
50
+ end
51
+ return_hash
52
+ end
53
+
54
+ def self.all_counter_names
55
+ Split.redis.keys("co:*").collect { |k| k.gsub(/^.*:/,"") }
56
+ end
57
+ end
58
+ end
59
+
60
+ module Split::Helper
61
+ include Split::CounterHelper
62
+ end
63
+
64
+ if defined?(Rails)
65
+ class ActionController::Base
66
+ ActionController::Base.send :include, Split::CounterHelper
67
+ ActionController::Base.helper Split::CounterHelper
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ module Split
2
+ module CountersdashboardHelpers
3
+ def url(*path_parts)
4
+ [ path_prefix, path_parts ].join("/").squeeze('/')
5
+ end
6
+
7
+ def path_prefix
8
+ request.env['SCRIPT_NAME']
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ function confirmResetCounter() {
2
+ var agree = confirm("This will reset the value for this counter, experiment and alternative?");
3
+ return agree ? true : false;
4
+ }
5
+
6
+ function confirmDeleteCounter() {
7
+ var agree = confirm("Are you sure you want to delete this counter and all its data?");
8
+ return agree ? true : false;
9
+ }
@@ -0,0 +1,48 @@
1
+ html, body, div, span, applet, object, iframe,
2
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3
+ a, abbr, acronym, address, big, cite, code,
4
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
5
+ small, strike, strong, sub, sup, tt, var,
6
+ dl, dt, dd, ul, li,
7
+ form, label, legend,
8
+ table, caption, tbody, tfoot, thead, tr, th, td {
9
+ margin: 0;
10
+ padding: 0;
11
+ border: 0;
12
+ outline: 0;
13
+ font-weight: inherit;
14
+ font-style: normal;
15
+ font-size: 100%;
16
+ font-family: inherit;
17
+ }
18
+
19
+ :focus {
20
+ outline: 0;
21
+ }
22
+
23
+ body {
24
+ line-height: 1;
25
+ }
26
+
27
+ ul {
28
+ list-style: none;
29
+ }
30
+
31
+ table {
32
+ border-collapse: collapse;
33
+ border-spacing: 0;
34
+ }
35
+
36
+ caption, th, td {
37
+ text-align: left;
38
+ font-weight: normal;
39
+ }
40
+
41
+ blockquote:before, blockquote:after,
42
+ q:before, q:after {
43
+ content: "";
44
+ }
45
+
46
+ blockquote, q {
47
+ quotes: "" "";
48
+ }
@@ -0,0 +1,316 @@
1
+ html {
2
+ background: #efefef;
3
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4
+ font-size: 13px;
5
+ }
6
+
7
+ body {
8
+ padding: 0 10px;
9
+ margin: 10px auto 0;
10
+ max-width:800px;
11
+ }
12
+
13
+ .header {
14
+ background: #ededed;
15
+ background: -webkit-gradient(linear, left top, left bottom,
16
+ color-stop(0%,#576a76),
17
+ color-stop(100%,#4d5256));
18
+ background: -moz-linear-gradient(top, #576076 0%, #414e58 100%);
19
+ background: -webkit-linear-gradient(top, #576a76 0%, #414e58 100%);
20
+ background: -o-linear-gradient(top, #576a76 0%, #414e58 100%);
21
+ background: -ms-linear-gradient(top, #576a76 0%, #414e58 100%);
22
+ background: linear-gradient(top, #576a76 0%, #414e58 100%);
23
+ border-bottom: 1px solid #fff;
24
+ -moz-border-radius-topleft: 5px;
25
+ -webkit-border-top-left-radius: 5px;
26
+ border-top-left-radius: 5px;
27
+ -moz-border-radius-topright: 5px;
28
+ -webkit-border-top-right-radius:5px;
29
+ border-top-right-radius: 5px;
30
+
31
+ overflow:hidden;
32
+ padding: 10px 5%;
33
+ text-shadow:0 1px 0 #000;
34
+ }
35
+
36
+ .header h1 {
37
+ color: #eee;
38
+ float:left;
39
+ font-size:1.2em;
40
+ font-weight:normal;
41
+ margin:2px 30px 0 0;
42
+ }
43
+
44
+ .header ul li {
45
+ display: inline;
46
+ }
47
+
48
+ .header ul li a {
49
+ color: #eee;
50
+ text-decoration: none;
51
+ margin-right: 10px;
52
+ display: inline-block;
53
+ padding: 4px 8px;
54
+ -moz-border-radius: 10px;
55
+ -webkit-border-radius:10px;
56
+ border-radius: 10px;
57
+
58
+ }
59
+
60
+ .header ul li a:hover {
61
+ background: rgba(255,255,255,0.1);
62
+ }
63
+
64
+ .header ul li a:active {
65
+ -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);
66
+ -webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,0.2);
67
+ box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);
68
+ }
69
+
70
+ .header ul li.current a {
71
+ background: rgba(255,255,255,0.1);
72
+ -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);
73
+ -webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,0.2);
74
+ box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);
75
+ color: #fff;
76
+ }
77
+
78
+ .header p.environment {
79
+ clear: both;
80
+ padding: 10px 0 0 0;
81
+ color: #BBB;
82
+ font-style: italic;
83
+ float: right;
84
+ }
85
+
86
+ #main {
87
+ padding: 10px 5%;
88
+ background: #f9f9f9;
89
+ border:1px solid #ccc;
90
+ border-top:none;
91
+ -moz-box-shadow: 0 3px 10px rgba(0,0,0,0.2);
92
+ -webkit-box-shadow:0 3px 10px rgba(0,0,0,0.2);
93
+ box-shadow: 0 3px 10px rgba(0,0,0,0.2);
94
+ overflow: hidden;
95
+ }
96
+
97
+ #main .logo {
98
+ float: right;
99
+ margin: 10px;
100
+ }
101
+
102
+ #main span.hl {
103
+ background: #efefef;
104
+ padding: 2px;
105
+ }
106
+
107
+ #main h1 {
108
+ margin: 10px 0;
109
+ font-size: 190%;
110
+ font-weight: bold;
111
+ color: #0080FF;
112
+ }
113
+
114
+ #main table {
115
+ width: 100%;
116
+ margin:0 0 10px;
117
+ }
118
+
119
+ #main table tr td, #main table tr th {
120
+ border-bottom: 1px solid #ccc;
121
+ padding: 6px;
122
+ }
123
+
124
+ #main table tr th {
125
+ background: #efefef;
126
+ color: #888;
127
+ font-size: 80%;
128
+ text-transform:uppercase;
129
+ }
130
+
131
+ #main table tr td.no-data {
132
+ text-align: center;
133
+ padding: 40px 0;
134
+ color: #999;
135
+ font-style: italic;
136
+ font-size: 130%;
137
+ }
138
+
139
+ #main a {
140
+ color: #111;
141
+ }
142
+
143
+ #main p {
144
+ margin: 5px 0;
145
+ }
146
+
147
+ #main p.intro {
148
+ margin-bottom: 15px;
149
+ font-size: 85%;
150
+ color: #999;
151
+ margin-top: 0;
152
+ line-height: 1.3;
153
+ }
154
+
155
+ #main h1.wi {
156
+ margin-bottom: 5px;
157
+ }
158
+
159
+ #main p.sub {
160
+ font-size: 95%;
161
+ color: #999;
162
+ }
163
+
164
+ .experiment {
165
+ background:#fff;
166
+ border: 1px solid #eee;
167
+ border-bottom:none;
168
+ margin:30px 0;
169
+ }
170
+
171
+ .experiment_with_goal {
172
+ margin: -32px 0 30px 0;
173
+ }
174
+
175
+ .experiment .experiment-header {
176
+ background: #f4f4f4;
177
+ background: -webkit-gradient(linear, left top, left bottom,
178
+ color-stop(0%,#f4f4f4),
179
+ color-stop(100%,#e0e0e0));
180
+ background: -moz-linear-gradient (top, #f4f4f4 0%, #e0e0e0 100%);
181
+ background: -webkit-linear-gradient(top, #f4f4f4 0%, #e0e0e0 100%);
182
+ background: -o-linear-gradient (top, #f4f4f4 0%, #e0e0e0 100%);
183
+ background: -ms-linear-gradient (top, #f4f4f4 0%, #e0e0e0 100%);
184
+ background: linear-gradient (top, #f4f4f4 0%, #e0e0e0 100%);
185
+ border-top:1px solid #fff;
186
+ overflow:hidden;
187
+ padding:0 10px;
188
+ }
189
+
190
+ .experiment h2 {
191
+ color:#888;
192
+ margin: 12px 0 12px 0;
193
+ font-size: 1em;
194
+ font-weight:bold;
195
+ float:left;
196
+ text-shadow:0 1px 0 rgba(255,255,255,0.8);
197
+ }
198
+
199
+ .experiment h2 .goal {
200
+ font-style: italic;
201
+ }
202
+
203
+ .experiment h2 .version {
204
+ font-style:italic;
205
+ font-size:0.8em;
206
+ color:#bbb;
207
+ font-weight:normal;
208
+ }
209
+
210
+ .experiment table em{
211
+ font-style:italic;
212
+ font-size:0.9em;
213
+ color:#bbb;
214
+ }
215
+
216
+ .experiment table .totals td {
217
+ background: #eee;
218
+ font-weight: bold;
219
+ }
220
+
221
+ #footer {
222
+ padding: 10px 5%;
223
+ color: #999;
224
+ font-size: 85%;
225
+ line-height: 1.5;
226
+ padding-top: 10px;
227
+ }
228
+
229
+ #footer p a {
230
+ color: #999;
231
+ }
232
+
233
+ .inline-controls {
234
+ float:right;
235
+ }
236
+
237
+ .inline-controls small {
238
+ color: #888;
239
+ font-size: 11px;
240
+ }
241
+
242
+ .inline-controls form {
243
+ display: inline-block;
244
+ font-size: 10px;
245
+ line-height: 38px;
246
+ }
247
+
248
+ .inline-controls input {
249
+ margin-left: 10px;
250
+ }
251
+
252
+ .worse, .better {
253
+ color: #773F3F;
254
+ font-size: 10px;
255
+ font-weight:bold;
256
+ }
257
+
258
+ .better {
259
+ color: #408C48;
260
+ }
261
+
262
+ a.button, button, input[type="submit"] {
263
+ padding: 4px 10px;
264
+ overflow: hidden;
265
+ background: #d8dae0;
266
+ -moz-box-shadow: 0 1px 0 rgba(0,0,0,0.5);
267
+ -webkit-box-shadow:0 1px 0 rgba(0,0,0,0.5);
268
+ box-shadow: 0 1px 0 rgba(0,0,0,0.5);
269
+ border:none;
270
+ -moz-border-radius: 30px;
271
+ -webkit-border-radius:30px;
272
+ border-radius: 30px;
273
+ color:#2e3035;
274
+ cursor: pointer;
275
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
276
+ text-decoration: none;
277
+ text-shadow:0 1px 0 rgba(255,255,255,0.8);
278
+ -moz-user-select: none;
279
+ -webkit-user-select:none;
280
+ user-select: none;
281
+ white-space: nowrap;
282
+ }
283
+ a.button:hover, button:hover, input[type="submit"]:hover,
284
+ a.button:focus, button:focus, input[type="submit"]:focus{
285
+ background:#bbbfc7;
286
+ }
287
+ a.button:active, button:active, input[type="submit"]:active{
288
+ -moz-box-shadow: inset 0 0 4px #484d57;
289
+ -webkit-box-shadow:inset 0 0 4px #484d57;
290
+ box-shadow: inset 0 0 4px #484d57;
291
+ position:relative;
292
+ top:1px;
293
+ }
294
+
295
+ a.button.red, button.red, input[type="submit"].red,
296
+ a.button.green, button.green, input[type="submit"].green {
297
+ color:#fff;
298
+ text-shadow:0 1px 0 rgba(0,0,0,0.4);
299
+ }
300
+
301
+ a.button.red, button.red, input[type="submit"].red {
302
+ background:#a56d6d;
303
+ }
304
+ a.button.red:hover, button.red:hover, input[type="submit"].red:hover,
305
+ a.button.red:focus, button.red:focus, input[type="submit"].red:focus {
306
+ background:#895C5C;
307
+ }
308
+ a.button.green, button.green, input[type="submit"].green {
309
+ background:#8daa92;
310
+ }
311
+ a.button.green:hover, button.green:hover, input[type="submit"].green:hover,
312
+ a.button.green:focus, button.green:focus, input[type="submit"].green:focus {
313
+ background:#768E7A;
314
+ }
315
+
316
+
@@ -0,0 +1,43 @@
1
+ <div class="experiment">
2
+ <div class="experiment-header">
3
+ <h2>
4
+ Counter: <%= counter_name %>
5
+ </h2>
6
+ <div class='inline-controls'>
7
+ <form action="<%= url "/counter/#{counter_name}" %>" method='post' onclick="return confirmDeleteCounter()">
8
+ <input type="hidden" name="_method" value="delete"/>
9
+ <input type="submit" value="Delete" class="red">
10
+ </form>
11
+ </div>
12
+ </div>
13
+ <table>
14
+ <tr>
15
+ <th>Experiment Name</th>
16
+ <th>Alternative</th>
17
+ <th>Counter</th>
18
+ <th>Reset</th>
19
+ </tr>
20
+ <% Split::Counters.all_values_hash(counter_name).each do |experiment, alternative_hash| %>
21
+ <% total_value = 0 %>
22
+ <% alternative_hash.each do |alternative, value| %>
23
+ <tr>
24
+ <td><%= experiment %></td>
25
+ <td><%= alternative %></td>
26
+ <td><%= value %></td>
27
+ <td>
28
+ <form action="<%= url "/counter/reset/#{counter_name}/#{experiment}/#{alternative}" %>" method='post' onclick="return confirmResetCounter()">
29
+ <input type="submit" value="Reset Data">
30
+ </form>
31
+ </td>
32
+ <tr>
33
+ <% total_value += value %>
34
+ <% end %>
35
+ <tr class="totals">
36
+ <td>Totals</td>
37
+ <td></td>
38
+ <td><%= total_value %></td>
39
+ <td></td>
40
+ <tr>
41
+ <% end %>
42
+ </table>
43
+ </div>
@@ -0,0 +1,9 @@
1
+ <% if @counter_names.any? %>
2
+ <p class="intro">The list below contains all the registered counters.</p>
3
+ <% @counter_names.each do |counter_name| %>
4
+ <%= erb :_counter, :locals => {:counter_name => counter_name} %>
5
+ <% end %>
6
+ <% else %>
7
+ <p class="intro">No counters have been registered.</p>
8
+ <% end %>
9
+
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta content='text/html; charset=utf-8' http-equiv='Content-Type'>
5
+ <link href="<%= url 'reset.css' %>" media="screen" rel="stylesheet" type="text/css">
6
+ <link href="<%= url 'style.css' %>" media="screen" rel="stylesheet" type="text/css">
7
+ <script type="text/javascript" src='<%= url 'countersdashboard.js' %>'></script>
8
+ <title>Split Counters</title>
9
+
10
+ </head>
11
+ <body>
12
+ <div class="header">
13
+ <h1>Split Counters Dashboard</h1>
14
+ <p class="environment"><%= @current_env %></p>
15
+ </div>
16
+
17
+ <div id="main">
18
+ <%= yield %>
19
+ </div>
20
+
21
+ <div id="footer">
22
+ <p>
23
+ <a href="http://github.com/bernardkroes/split-counters">Split Counters</a>.
24
+ Powered by <a href="http://github.com/andrew/split">Split</a> v<%=Split::VERSION %>
25
+ </p>
26
+ </div>
27
+ </body>
28
+ </html>
@@ -0,0 +1,43 @@
1
+ require 'sinatra/base'
2
+ require 'split'
3
+ require 'bigdecimal'
4
+ require 'split/countersdashboard/helpers'
5
+
6
+ module Split
7
+ class Countersdashboard < Sinatra::Base
8
+ dir = File.dirname(File.expand_path(__FILE__))
9
+
10
+ set :views, "#{dir}/countersdashboard/views"
11
+ set :public_folder, "#{dir}/countersdashboard/public"
12
+ set :static, true
13
+ set :method_override, true
14
+
15
+ helpers Split::CountersdashboardHelpers
16
+
17
+ get '/' do
18
+ @counter_names = Split::Counters.all_counter_names
19
+ # Display Rails Environment mode (or Rack version if not using Rails)
20
+ if Object.const_defined?('Rails')
21
+ @current_env = Rails.env.titlecase
22
+ else
23
+ @current_env = "Rack: #{Rack.version}"
24
+ end
25
+ erb :index
26
+ end
27
+
28
+ delete '/counter/:counter' do
29
+ if Split::Counters.exists?(params[:counter])
30
+ Split::Counters.delete(params[:counter])
31
+ end
32
+ redirect url('/')
33
+ end
34
+
35
+ post '/counter/reset/:counter/:experiment/:alternative' do
36
+ if Split::Counters.exists?(params[:counter])
37
+ Split::Counters.reset(params[:counter], params[:experiment], params[:alternative])
38
+ end
39
+ redirect url('/')
40
+ end
41
+ end
42
+ end
43
+
Binary file
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Split::Counters do
4
+ include Split::Helper
5
+
6
+ describe 'basic use' do
7
+ before(:each) do
8
+ Split::Counters.delete('co')
9
+ Split::Counters.delete('co2')
10
+ end
11
+
12
+ after(:each) do
13
+ Split::Counters.delete('co')
14
+ Split::Counters.delete('co2')
15
+ end
16
+
17
+ it "should create a counter upon using it, if it does not exist" do
18
+ Split::Counters.inc('co', 'exp1', 'alt1')
19
+ Split::Counters.current_value('co', 'exp1', 'alt1').should eq("1")
20
+ end
21
+
22
+ it "should create a counter directly upon using it, if it does not exist" do
23
+ Split::Counters.delete('co')
24
+ Split::Counters.inc('co','exp1', 'alt1')
25
+ Split::Counters.current_value('co', 'exp1', 'alt1').should eq("1")
26
+ Split::Counters.exists?('co').should eq(true)
27
+ end
28
+
29
+ it "should be possible to delete a counter" do
30
+ Split::Counters.inc('co','exp1', 'alt1')
31
+ Split::Counters.exists?('co').should eq(true)
32
+ Split::Counters.delete('co')
33
+ Split::Counters.exists?('co').should eq(false)
34
+ end
35
+
36
+ it "should be possible to reset a counter" do
37
+ Split::Counters.delete('co')
38
+ Split::Counters.inc('co','exp1', 'alt1')
39
+ Split::Counters.current_value('co', 'exp1', 'alt1').should eq("1")
40
+ Split::Counters.reset('co', 'exp1', 'alt1')
41
+ Split::Counters.inc('co','exp1', 'alt1')
42
+ Split::Counters.current_value('co', 'exp1', 'alt1').should eq("1")
43
+ end
44
+
45
+ it "should be possible to get all experiments and hashs counter values" do
46
+ Split::Counters.delete('co')
47
+ Split::Counters.inc('co', 'exp1', 'alt1')
48
+ Split::Counters.inc('co', 'exp1', 'alt2')
49
+ Split::Counters.inc('co', 'exp1', 'alt2')
50
+ Split::Counters.inc('co', 'exp2', 'alt1')
51
+ # {"exp1"=>{"alt1"=>1, "alt2"=>2}, "exp2"=>{"alt1"=>1}}
52
+ Split::Counters.all_values_hash('co').length.should eq(2)
53
+ Split::Counters.all_values_hash('co')['exp1'].length.should eq(2)
54
+ Split::Counters.all_values_hash('co')['exp2'].length.should eq(1)
55
+ end
56
+
57
+ it "should be possible to get a list of all counters" do
58
+ Split::Counters.delete('co')
59
+ Split::Counters.inc('co', 'exp1', 'alt1')
60
+ Split::Counters.inc('co2','exp2', 'alt1')
61
+ Split::Counters.all_counter_names.length.should eq(2)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'split'
4
+ require 'split/helper'
5
+ require 'split/counters'
6
+ require 'ostruct'
7
+
8
+ def session
9
+ @session ||= {}
10
+ end
11
+
12
+ def params
13
+ @params ||= {}
14
+ end
15
+
16
+ def request(ua = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27')
17
+ r = OpenStruct.new
18
+ r.user_agent = ua
19
+ r.ip = '192.168.1.1'
20
+ @request ||= r
21
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "split/counters/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Bernard Kroes"]
7
+ gem.email = ["bernardkroes@gmail.com"]
8
+ gem.summary = %q{Split extension for counting things per experiment and alternative}
9
+ gem.description = %q{This gem adds counters to Split}
10
+ gem.homepage = "https://github.com/bernardkroes/split-counters"
11
+ gem.license = 'MIT'
12
+
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.name = "split-counters"
17
+ gem.require_paths = ['lib']
18
+ gem.version = Split::Counters::VERSION
19
+
20
+ gem.add_dependency(%q<split>, [">= 0.7.0"])
21
+
22
+ # Development Dependencies
23
+ gem.add_development_dependency(%q<rspec>, ["~> 2.14"])
24
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: split-counters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Bernard Kroes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: split
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '2.14'
41
+ description: This gem adds counters to Split
42
+ email:
43
+ - bernardkroes@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - Gemfile
49
+ - LICENSE
50
+ - Rakefile
51
+ - Readme.mdown
52
+ - lib/split/counters.rb
53
+ - lib/split/counters/version.rb
54
+ - lib/split/countersdashboard.rb
55
+ - lib/split/countersdashboard/helpers.rb
56
+ - lib/split/countersdashboard/public/countersdashboard.js
57
+ - lib/split/countersdashboard/public/reset.css
58
+ - lib/split/countersdashboard/public/style.css
59
+ - lib/split/countersdashboard/views/_counter.erb
60
+ - lib/split/countersdashboard/views/index.erb
61
+ - lib/split/countersdashboard/views/layout.erb
62
+ - screenshots/screenshot1.png
63
+ - spec/counters_spec.rb
64
+ - spec/spec_helper.rb
65
+ - split-counters.gemspec
66
+ homepage: https://github.com/bernardkroes/split-counters
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.0.6
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Split extension for counting things per experiment and alternative
90
+ test_files:
91
+ - spec/counters_spec.rb
92
+ - spec/spec_helper.rb