split 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +76 -70
- data/lib/split.rb +1 -1
- data/lib/split/configuration.rb +4 -0
- data/lib/split/dashboard/views/_experiment.erb +1 -3
- data/lib/split/experiment.rb +28 -14
- data/lib/split/helper.rb +4 -3
- data/lib/split/persistence/cookie_adapter.rb +6 -3
- data/lib/split/version.rb +2 -2
- data/spec/configuration_spec.rb +30 -0
- data/spec/experiment_spec.rb +7 -0
- data/spec/helper_spec.rb +8 -0
- data/split.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fa1bd87e254e6e0bc2f244f3fd6e479d40e096f
|
4
|
+
data.tar.gz: c50789065bf80c9b850bdc0851d1b6dbcd330183
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1ce3cc70e83f014d69136754658030b310aaff9450d6ad8750011202d071f44a0157c824e79a0a4dd7938e2534090949e6f138c913c799caec02ba01b2b277c
|
7
|
+
data.tar.gz: db2e2a14bdd4bfb3e8620c211e74cf649dbfe9c67a0555e1304f79ab2e8c9da707c5798816d90887c4f2d1f7ab2ad87d99157d00125801eb29afb8026033983b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## 1.3.0 (October 20th, 2015)
|
2
|
+
|
3
|
+
Features
|
4
|
+
|
5
|
+
- allow for custom redis_url different from ENV variable (@davidgrieser, #323)
|
6
|
+
- add ability to change the length of the persistence cookie (@peterylai, #335)
|
7
|
+
|
8
|
+
Bugfixes:
|
9
|
+
|
10
|
+
- Rescue from Redis::BaseError instead of Redis::CannotConnectError (@nfm, #342)
|
11
|
+
- Fix active experiments when experiment is on a later version (@ndrisso, #331)
|
12
|
+
- Fix caching of winning alternative (@nfm, #329)
|
13
|
+
|
14
|
+
Misc:
|
15
|
+
|
16
|
+
- Remove duplication from Experiment#save (@pakallis, #333)
|
17
|
+
- Remove unnecessary argument from Experiment#write_to_alternative (@t4deu, #332)
|
18
|
+
|
1
19
|
## 1.2.1 (May 17th, 2015)
|
2
20
|
|
3
21
|
Features
|
data/README.md
CHANGED
@@ -23,47 +23,29 @@ Split only supports redis 2.0 or greater.
|
|
23
23
|
If you're on OS X, Homebrew is the simplest way to install Redis:
|
24
24
|
|
25
25
|
```bash
|
26
|
-
|
27
|
-
|
26
|
+
brew install redis
|
27
|
+
redis-server /usr/local/etc/redis.conf
|
28
28
|
```
|
29
29
|
|
30
30
|
You now have a Redis daemon running on 6379.
|
31
31
|
|
32
32
|
## Setup
|
33
33
|
|
34
|
-
If you are using bundler add split to your Gemfile:
|
35
|
-
|
36
|
-
``` ruby
|
37
|
-
gem 'split'
|
38
|
-
```
|
39
|
-
|
40
|
-
Then run:
|
41
|
-
|
42
|
-
```bash
|
43
|
-
$ bundle install
|
44
|
-
```
|
45
|
-
|
46
|
-
Otherwise install the gem:
|
47
|
-
|
48
34
|
```bash
|
49
|
-
|
50
|
-
```
|
51
|
-
|
52
|
-
and require it in your project:
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
require 'split'
|
35
|
+
gem install split
|
56
36
|
```
|
57
37
|
|
58
|
-
### Rails
|
38
|
+
### Rails
|
59
39
|
|
60
|
-
|
40
|
+
Adding `gem 'split'` to your Gemfile will autoloaded it when rails starts up, as long as you've configured redis it will 'just work'.
|
61
41
|
|
62
42
|
### Sinatra
|
63
43
|
|
64
44
|
To configure sinatra with Split you need to enable sessions and mix in the helper methods. Add the following lines at the top of your sinatra app:
|
65
45
|
|
66
46
|
```ruby
|
47
|
+
require 'split'
|
48
|
+
|
67
49
|
class MySinatraApp < Sinatra::Base
|
68
50
|
enable :sessions
|
69
51
|
helpers Split::Helper
|
@@ -86,8 +68,8 @@ It can be used to render different templates, show different text or any other c
|
|
86
68
|
Example: View
|
87
69
|
|
88
70
|
```erb
|
89
|
-
<% ab_test(
|
90
|
-
<%= image_tag(button_file, :
|
71
|
+
<% ab_test(:login_button, "/images/button1.jpg", "/images/button2.jpg") do |button_file| %>
|
72
|
+
<%= image_tag(button_file, alt: "Login!") %>
|
91
73
|
<% end %>
|
92
74
|
```
|
93
75
|
|
@@ -96,7 +78,7 @@ Example: Controller
|
|
96
78
|
```ruby
|
97
79
|
def register_new_user
|
98
80
|
# See what level of free points maximizes users' decision to buy replacement points.
|
99
|
-
@starter_points = ab_test(
|
81
|
+
@starter_points = ab_test(:new_user_free_points, '100', '200', '300')
|
100
82
|
end
|
101
83
|
```
|
102
84
|
|
@@ -105,14 +87,14 @@ Example: Conversion tracking (in a controller!)
|
|
105
87
|
```ruby
|
106
88
|
def buy_new_points
|
107
89
|
# some business logic
|
108
|
-
finished(
|
90
|
+
finished(:new_user_free_points)
|
109
91
|
end
|
110
92
|
```
|
111
93
|
|
112
94
|
Example: Conversion tracking (in a view)
|
113
95
|
|
114
96
|
```erb
|
115
|
-
Thanks for signing up, dude! <% finished(
|
97
|
+
Thanks for signing up, dude! <% finished(:signup_page_redesign) %>
|
116
98
|
```
|
117
99
|
|
118
100
|
You can find more examples, tutorials and guides on the [wiki](https://github.com/splitrb/split/wiki).
|
@@ -139,11 +121,11 @@ Perhaps you only want to show an alternative to 10% of your visitors because it
|
|
139
121
|
To do this you can pass a weight with each alternative in the following ways:
|
140
122
|
|
141
123
|
```ruby
|
142
|
-
ab_test(
|
124
|
+
ab_test(:homepage_design, {'Old' => 18}, {'New' => 2})
|
143
125
|
|
144
|
-
ab_test(
|
126
|
+
ab_test(:homepage_design, 'Old', {'New' => 1.0/9})
|
145
127
|
|
146
|
-
ab_test('
|
128
|
+
ab_test(:homepage_design', {'Old' => 9}, 'New')
|
147
129
|
```
|
148
130
|
|
149
131
|
This will only show the new alternative to visitors 1 in 10 times, the default weight for an alternative is 1.
|
@@ -161,7 +143,7 @@ will always have red buttons. This won't be stored in your session or count towa
|
|
161
143
|
|
162
144
|
In the event you want to disable all tests without having to know the individual experiment names, add a `SPLIT_DISABLE` query parameter.
|
163
145
|
|
164
|
-
http://myawesomesite.com?SPLIT_DISABLE=
|
146
|
+
http://myawesomesite.com?SPLIT_DISABLE=true
|
165
147
|
|
166
148
|
It is not required to send `SPLIT_DISABLE=false` to activate Split.
|
167
149
|
|
@@ -179,7 +161,7 @@ When a user completes a test their session is reset so that they may start the t
|
|
179
161
|
To stop this behaviour you can pass the following option to the `finished` method:
|
180
162
|
|
181
163
|
```ruby
|
182
|
-
finished(
|
164
|
+
finished(:experiment_name, reset: false)
|
183
165
|
```
|
184
166
|
|
185
167
|
The user will then always see the alternative they started with.
|
@@ -212,6 +194,15 @@ Split.configure do |config|
|
|
212
194
|
end
|
213
195
|
```
|
214
196
|
|
197
|
+
By default, cookies will expire in 1 year. To change that, set the `persistence_cookie_length` in the configuration (unit of time in seconds).
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
Split.configure do |config|
|
201
|
+
config.persistence = :cookie
|
202
|
+
config.persistence_cookie_length = 2592000 # 30 days
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
215
206
|
__Note:__ Using cookies depends on `ActionDispatch::Cookies` or any identical API
|
216
207
|
|
217
208
|
#### Redis
|
@@ -220,9 +211,9 @@ Using Redis will allow ab_users to persist across sessions or machines.
|
|
220
211
|
|
221
212
|
```ruby
|
222
213
|
Split.configure do |config|
|
223
|
-
config.persistence = Split::Persistence::RedisAdapter.with_config(:
|
214
|
+
config.persistence = Split::Persistence::RedisAdapter.with_config(lookup_by: -> (context) { context.current_user_id })
|
224
215
|
# Equivalent
|
225
|
-
# config.persistence = Split::Persistence::RedisAdapter.with_config(:
|
216
|
+
# config.persistence = Split::Persistence::RedisAdapter.with_config(lookup_by: :current_user_id)
|
226
217
|
end
|
227
218
|
```
|
228
219
|
|
@@ -233,7 +224,7 @@ Options:
|
|
233
224
|
#### Custom Adapter
|
234
225
|
|
235
226
|
Your custom adapter needs to implement the same API as existing adapters.
|
236
|
-
See `Split::
|
227
|
+
See `Split::Persistence::CookieAdapter` or `Split::Persistence::SessionAdapter` for a starting point.
|
237
228
|
|
238
229
|
```ruby
|
239
230
|
Split.configure do |config|
|
@@ -294,8 +285,8 @@ For example:
|
|
294
285
|
|
295
286
|
``` ruby
|
296
287
|
Split.configure do |config|
|
297
|
-
config.on_experiment_reset =
|
298
|
-
config.on_experiment_delete =
|
288
|
+
config.on_experiment_reset = -> (example) { # Do something on reset }
|
289
|
+
config.on_experiment_delete = -> (experiment) { # Do something else on delete }
|
299
290
|
end
|
300
291
|
```
|
301
292
|
|
@@ -316,13 +307,13 @@ run Rack::URLMap.new \
|
|
316
307
|
However, if you are using Rails 3: You can mount this inside your app routes by first adding this to the Gemfile:
|
317
308
|
|
318
309
|
```ruby
|
319
|
-
gem 'split', :
|
310
|
+
gem 'split', require: 'split/dashboard'
|
320
311
|
```
|
321
312
|
|
322
313
|
Then adding this to config/routes.rb
|
323
314
|
|
324
315
|
```ruby
|
325
|
-
mount Split::Dashboard, :
|
316
|
+
mount Split::Dashboard, at: 'split'
|
326
317
|
```
|
327
318
|
|
328
319
|
You may want to password protect that page, you can do so with `Rack::Auth::Basic` (in your split initializer file)
|
@@ -335,11 +326,11 @@ end
|
|
335
326
|
|
336
327
|
You can even use Devise or any other Warden-based authentication method to authorize users. Just replace `mount Split::Dashboard, :at => 'split'` in `config/routes.rb` with the following:
|
337
328
|
```ruby
|
338
|
-
match "/split" => Split::Dashboard, :
|
329
|
+
match "/split" => Split::Dashboard, anchor: false, via: [:get, :post, :delete], constraints: -> (request) do
|
339
330
|
request.env['warden'].authenticated? # are we authenticated?
|
340
331
|
request.env['warden'].authenticate! # authenticate if not already
|
341
332
|
# or even check any other condition such as request.env['warden'].user.is_admin?
|
342
|
-
|
333
|
+
end
|
343
334
|
```
|
344
335
|
|
345
336
|
More information on this [here](http://steve.dynedge.co.uk/2011/12/09/controlling-access-to-routes-and-rack-apps-in-rails-3-with-devise-and-warden/)
|
@@ -355,16 +346,17 @@ You can override the default configuration options of Split like so:
|
|
355
346
|
```ruby
|
356
347
|
Split.configure do |config|
|
357
348
|
config.db_failover = true # handle redis errors gracefully
|
358
|
-
config.db_failover_on_db_error =
|
349
|
+
config.db_failover_on_db_error = -> (error) { Rails.logger.error(error.message) }
|
359
350
|
config.allow_multiple_experiments = true
|
360
351
|
config.enabled = true
|
361
352
|
config.persistence = Split::Persistence::SessionAdapter
|
362
353
|
#config.start_manually = false ## new test will have to be started manually from the admin panel. default false
|
363
354
|
config.include_rails_helper = true
|
355
|
+
config.redis_url = "custom.redis.url:6380"
|
364
356
|
end
|
365
357
|
```
|
366
358
|
|
367
|
-
|
359
|
+
Split looks for the Redis host in the environment variable ```REDIS_URL``` then defaults to ```localhost:6379``` if not specified by configure block.
|
368
360
|
|
369
361
|
### Filtering
|
370
362
|
|
@@ -381,7 +373,7 @@ Split.configure do |config|
|
|
381
373
|
config.ignore_ip_addresses << '81.19.48.130' # or regex: /81\.19\.48\.[0-9]+/
|
382
374
|
|
383
375
|
# or provide your own filter functionality, the default is proc{ |request| is_robot? || is_ignored_ip_address? }
|
384
|
-
config.ignore_filter =
|
376
|
+
config.ignore_filter = -> (request) { CustomExcludeLogic.excludes?(request) }
|
385
377
|
end
|
386
378
|
```
|
387
379
|
|
@@ -394,15 +386,15 @@ algorithm and if the experiment resets once finished:
|
|
394
386
|
```ruby
|
395
387
|
Split.configure do |config|
|
396
388
|
config.experiments = {
|
397
|
-
|
398
|
-
:
|
399
|
-
:
|
389
|
+
my_first_experiment: {
|
390
|
+
alternatives: ["a", "b"],
|
391
|
+
resettable: false
|
400
392
|
},
|
401
|
-
|
402
|
-
:
|
403
|
-
:
|
404
|
-
{ :
|
405
|
-
{ :
|
393
|
+
:my_second_experiment => {
|
394
|
+
algorithm: 'Split::Algorithms::Whiplash',
|
395
|
+
alternatives: [
|
396
|
+
{ name: "a", percent: 67 },
|
397
|
+
{ name: "b", percent: 33 }
|
406
398
|
]
|
407
399
|
}
|
408
400
|
}
|
@@ -436,27 +428,41 @@ my_second_experiment:
|
|
436
428
|
This simplifies the calls from your code:
|
437
429
|
|
438
430
|
```ruby
|
439
|
-
ab_test(
|
431
|
+
ab_test(:my_first_experiment)
|
440
432
|
```
|
441
433
|
|
442
434
|
and:
|
443
435
|
|
444
436
|
```ruby
|
445
|
-
finished(
|
437
|
+
finished(:my_first_experiment)
|
446
438
|
```
|
447
439
|
|
448
440
|
You can also add meta data for each experiment, very useful when you need more than an alternative name to change behaviour:
|
449
441
|
|
442
|
+
```ruby
|
443
|
+
Split.configure do |config|
|
444
|
+
config.experiments = {
|
445
|
+
my_first_experiment: {
|
446
|
+
alternatives: ["a", "b"],
|
447
|
+
metadata: {
|
448
|
+
"a" => {"text" => "Have a fantastic day"},
|
449
|
+
"b" => {"text" => "Don't get hit by a bus"},
|
450
|
+
}
|
451
|
+
},
|
452
|
+
}
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
450
456
|
```yaml
|
451
457
|
my_first_experiment:
|
452
458
|
alternatives:
|
453
459
|
- a
|
454
460
|
- b
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
461
|
+
metadata:
|
462
|
+
a:
|
463
|
+
text: "Have a fantastic day"
|
464
|
+
b:
|
465
|
+
text: "Don't get hit by a bus"
|
460
466
|
```
|
461
467
|
|
462
468
|
This allows for some advanced experiment configuration using methods like:
|
@@ -486,9 +492,9 @@ the `:metric` option.
|
|
486
492
|
```ruby
|
487
493
|
Split.configure do |config|
|
488
494
|
config.experiments = {
|
489
|
-
|
490
|
-
:
|
491
|
-
:
|
495
|
+
my_first_experiment: {
|
496
|
+
alternatives: ["a", "b"],
|
497
|
+
metric: :my_metric,
|
492
498
|
}
|
493
499
|
}
|
494
500
|
end
|
@@ -514,7 +520,7 @@ You might wish to allow an experiment to have multiple, distinguishable goals.
|
|
514
520
|
The API to define goals for an experiment is this:
|
515
521
|
|
516
522
|
```ruby
|
517
|
-
ab_test({
|
523
|
+
ab_test({link_color: ["purchase", "refund"]}, "red", "blue")
|
518
524
|
```
|
519
525
|
|
520
526
|
or you can you can define them in a configuration file:
|
@@ -522,9 +528,9 @@ or you can you can define them in a configuration file:
|
|
522
528
|
```ruby
|
523
529
|
Split.configure do |config|
|
524
530
|
config.experiments = {
|
525
|
-
|
526
|
-
:
|
527
|
-
:
|
531
|
+
link_color: {
|
532
|
+
alternatives: ["red", "blue"],
|
533
|
+
goals: ["purchase", "refund"]
|
528
534
|
}
|
529
535
|
}
|
530
536
|
end
|
@@ -533,7 +539,7 @@ end
|
|
533
539
|
To complete a goal conversion, you do it like:
|
534
540
|
|
535
541
|
```ruby
|
536
|
-
finished(
|
542
|
+
finished(link_color: "purchase")
|
537
543
|
```
|
538
544
|
|
539
545
|
**NOTE:** This does not mean that a single experiment can have/complete progressive goals.
|
data/lib/split.rb
CHANGED
data/lib/split/configuration.rb
CHANGED
@@ -10,6 +10,7 @@ module Split
|
|
10
10
|
attr_accessor :allow_multiple_experiments
|
11
11
|
attr_accessor :enabled
|
12
12
|
attr_accessor :persistence
|
13
|
+
attr_accessor :persistence_cookie_length
|
13
14
|
attr_accessor :algorithm
|
14
15
|
attr_accessor :store_override
|
15
16
|
attr_accessor :start_manually
|
@@ -19,6 +20,7 @@ module Split
|
|
19
20
|
attr_accessor :on_experiment_delete
|
20
21
|
attr_accessor :include_rails_helper
|
21
22
|
attr_accessor :beta_probability_simulations
|
23
|
+
attr_accessor :redis_url
|
22
24
|
|
23
25
|
attr_reader :experiments
|
24
26
|
|
@@ -201,9 +203,11 @@ module Split
|
|
201
203
|
@enabled = true
|
202
204
|
@experiments = {}
|
203
205
|
@persistence = Split::Persistence::SessionAdapter
|
206
|
+
@persistence_cookie_length = 31536000 # One year from now
|
204
207
|
@algorithm = Split::Algorithms::WeightedSample
|
205
208
|
@include_rails_helper = true
|
206
209
|
@beta_probability_simulations = 10000
|
210
|
+
@redis_url = ENV.fetch('REDIS_URL', 'localhost:6379')
|
207
211
|
end
|
208
212
|
|
209
213
|
private
|
@@ -4,9 +4,7 @@
|
|
4
4
|
<% experiment_class = "experiment" %>
|
5
5
|
<% end %>
|
6
6
|
|
7
|
-
<%
|
8
|
-
<% experiment.estimate_winning_alternative %>
|
9
|
-
<% end %>
|
7
|
+
<% experiment.calc_winning_alternatives %>
|
10
8
|
|
11
9
|
<div class="<%= experiment_class %>">
|
12
10
|
<div class="experiment-header">
|
data/lib/split/experiment.rb
CHANGED
@@ -83,7 +83,8 @@ module Split
|
|
83
83
|
Split.redis.sadd(:experiments, name)
|
84
84
|
start unless Split.configuration.start_manually
|
85
85
|
@alternatives.reverse.each {|a| Split.redis.lpush(name, a.name)}
|
86
|
-
|
86
|
+
save_goals
|
87
|
+
save_metadata
|
87
88
|
Split.redis.set(metadata_key, @metadata.to_json) unless @metadata.nil?
|
88
89
|
else
|
89
90
|
existing_alternatives = load_alternatives_from_redis
|
@@ -96,8 +97,8 @@ module Split
|
|
96
97
|
delete_metadata
|
97
98
|
Split.redis.del(@name)
|
98
99
|
@alternatives.reverse.each {|a| Split.redis.lpush(name, a.name)}
|
99
|
-
|
100
|
-
|
100
|
+
save_goals
|
101
|
+
save_metadata
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
@@ -274,17 +275,22 @@ module Split
|
|
274
275
|
end
|
275
276
|
|
276
277
|
def calc_winning_alternatives
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
278
|
+
# Super simple cache so that we only recalculate winning alternatives once per day
|
279
|
+
days_since_epoch = Time.now.utc.to_i / 86400
|
280
|
+
|
281
|
+
if self.calc_time != days_since_epoch
|
282
|
+
if goals.empty?
|
283
|
+
self.estimate_winning_alternative
|
284
|
+
else
|
285
|
+
goals.each do |goal|
|
286
|
+
self.estimate_winning_alternative(goal)
|
287
|
+
end
|
282
288
|
end
|
283
|
-
end
|
284
289
|
|
285
|
-
|
290
|
+
self.calc_time = days_since_epoch
|
286
291
|
|
287
|
-
|
292
|
+
self.save
|
293
|
+
end
|
288
294
|
end
|
289
295
|
|
290
296
|
def estimate_winning_alternative(goal = nil)
|
@@ -309,12 +315,12 @@ module Split
|
|
309
315
|
|
310
316
|
@alternative_probabilities = calc_alternative_probabilities(winning_counts, Split.configuration.beta_probability_simulations)
|
311
317
|
|
312
|
-
write_to_alternatives(
|
318
|
+
write_to_alternatives(goal)
|
313
319
|
|
314
320
|
self.save
|
315
321
|
end
|
316
322
|
|
317
|
-
def write_to_alternatives(
|
323
|
+
def write_to_alternatives(goal = nil)
|
318
324
|
alternatives.each do |alternative|
|
319
325
|
alternative.set_p_winner(@alternative_probabilities[alternative], goal)
|
320
326
|
end
|
@@ -390,7 +396,7 @@ module Split
|
|
390
396
|
end
|
391
397
|
|
392
398
|
def calc_time
|
393
|
-
Split.redis.hget(experiment_config_key, :calc_time)
|
399
|
+
Split.redis.hget(experiment_config_key, :calc_time).to_i
|
394
400
|
end
|
395
401
|
|
396
402
|
def jstring(goal = nil)
|
@@ -451,5 +457,13 @@ module Split
|
|
451
457
|
end
|
452
458
|
end
|
453
459
|
|
460
|
+
def save_goals
|
461
|
+
@goals.reverse.each {|a| Split.redis.lpush(goals_key, a)} unless @goals.nil?
|
462
|
+
end
|
463
|
+
|
464
|
+
def save_metadata
|
465
|
+
Split.redis.set(metadata_key, @metadata.to_json) unless @metadata.nil?
|
466
|
+
end
|
467
|
+
|
454
468
|
end
|
455
469
|
end
|
data/lib/split/helper.rb
CHANGED
@@ -16,7 +16,7 @@ module Split
|
|
16
16
|
else
|
17
17
|
control_variable(experiment.control)
|
18
18
|
end
|
19
|
-
rescue Errno::ECONNREFUSED, Redis::
|
19
|
+
rescue Errno::ECONNREFUSED, Redis::BaseError, SocketError => e
|
20
20
|
raise(e) unless Split.configuration.db_failover
|
21
21
|
Split.configuration.db_failover_on_db_error.call(e)
|
22
22
|
|
@@ -116,9 +116,10 @@ module Split
|
|
116
116
|
def active_experiments
|
117
117
|
experiment_pairs = {}
|
118
118
|
ab_user.keys.each do |key|
|
119
|
-
|
119
|
+
key_without_version = key.split(/\:\d(?!\:)/)[0]
|
120
|
+
Metric.possible_experiments(key_without_version).each do |experiment|
|
120
121
|
if !experiment.has_winner?
|
121
|
-
experiment_pairs[
|
122
|
+
experiment_pairs[key_without_version] = ab_user[key]
|
122
123
|
end
|
123
124
|
end
|
124
125
|
end
|
@@ -4,10 +4,9 @@ module Split
|
|
4
4
|
module Persistence
|
5
5
|
class CookieAdapter
|
6
6
|
|
7
|
-
EXPIRES = Time.now + 31536000 # One year from now
|
8
|
-
|
9
7
|
def initialize(context)
|
10
8
|
@cookies = context.send(:cookies)
|
9
|
+
@expires = Time.now + cookie_length_config
|
11
10
|
end
|
12
11
|
|
13
12
|
def [](key)
|
@@ -31,7 +30,7 @@ module Split
|
|
31
30
|
def set_cookie(value)
|
32
31
|
@cookies[:split] = {
|
33
32
|
:value => JSON.generate(value),
|
34
|
-
:expires =>
|
33
|
+
:expires => @expires
|
35
34
|
}
|
36
35
|
end
|
37
36
|
|
@@ -47,6 +46,10 @@ module Split
|
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
49
|
+
def cookie_length_config
|
50
|
+
Split.configuration.persistence_cookie_length
|
51
|
+
end
|
52
|
+
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
data/lib/split/version.rb
CHANGED
data/spec/configuration_spec.rb
CHANGED
@@ -210,4 +210,34 @@ describe Split::Configuration do
|
|
210
210
|
|
211
211
|
expect(@config.normalized_experiments).to eq({:my_experiment=>{:alternatives=>[{"control_opt"=>0.67}, [{"second_opt"=>0.1}, {"third_opt"=>0.23}]]}})
|
212
212
|
end
|
213
|
+
|
214
|
+
context "configuration URL" do
|
215
|
+
it "should default to local redis server" do
|
216
|
+
expect(@config.redis_url).to eq("localhost:6379")
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should allow for redis url to be configured" do
|
220
|
+
@config.redis_url = "custom_redis_url"
|
221
|
+
expect(@config.redis_url).to eq("custom_redis_url")
|
222
|
+
end
|
223
|
+
|
224
|
+
context "provided REDIS_URL environment variable" do
|
225
|
+
it "should use the ENV variable" do
|
226
|
+
ENV['REDIS_URL'] = "env_redis_url"
|
227
|
+
expect(Split::Configuration.new.redis_url).to eq("env_redis_url")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context "persistence cookie length" do
|
233
|
+
it "should default to 1 year" do
|
234
|
+
expect(@config.persistence_cookie_length).to eq(31536000)
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should allow the persistence cookie length to be configured" do
|
238
|
+
@config.persistence_cookie_length = 2592000
|
239
|
+
expect(@config.persistence_cookie_length).to eq(2592000)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
213
243
|
end
|
data/spec/experiment_spec.rb
CHANGED
@@ -428,6 +428,13 @@ describe Split::Experiment do
|
|
428
428
|
p_goal2 = alt.p_winner(goal2)
|
429
429
|
expect(p_goal1).not_to be_within(0.04).of(p_goal2)
|
430
430
|
end
|
431
|
+
|
432
|
+
it "should return nil and not re-calculate probabilities if they have already been calculated today" do
|
433
|
+
experiment = Split::ExperimentCatalog.find_or_create({'link_color3' => ["purchase", "refund"]}, 'blue', 'red', 'green')
|
434
|
+
experiment_calc_time = Time.now.utc.to_i / 86400
|
435
|
+
experiment.calc_time = experiment_calc_time
|
436
|
+
expect(experiment.calc_winning_alternatives).to be nil
|
437
|
+
end
|
431
438
|
end
|
432
439
|
|
433
440
|
end
|
data/spec/helper_spec.rb
CHANGED
@@ -449,6 +449,14 @@ describe Split::Helper do
|
|
449
449
|
expect(active_experiments.first[0]).to eq "def"
|
450
450
|
expect(active_experiments.first[1]).to eq alternative
|
451
451
|
end
|
452
|
+
|
453
|
+
it 'should show an active test when an experiment is on a later version' do
|
454
|
+
experiment.reset
|
455
|
+
expect(experiment.version).to eq(1)
|
456
|
+
ab_test('link_color', 'blue', 'red')
|
457
|
+
expect(active_experiments.count).to eq 1
|
458
|
+
expect(active_experiments.first[0]).to eq "link_color"
|
459
|
+
end
|
452
460
|
|
453
461
|
it 'should show multiple tests' do
|
454
462
|
Split.configure do |config|
|
data/split.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.licenses = ['MIT']
|
11
11
|
s.email = ["andrewnez@gmail.com"]
|
12
12
|
s.homepage = "https://github.com/splitrb/split"
|
13
|
-
s.summary =
|
13
|
+
s.summary = "Rack based split testing framework"
|
14
14
|
|
15
15
|
s.required_ruby_version = '>= 1.9.2'
|
16
16
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: split
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Nesbitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -229,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
229
229
|
version: '0'
|
230
230
|
requirements: []
|
231
231
|
rubyforge_project: split
|
232
|
-
rubygems_version: 2.4.
|
232
|
+
rubygems_version: 2.4.8
|
233
233
|
signing_key:
|
234
234
|
specification_version: 4
|
235
235
|
summary: Rack based split testing framework
|
@@ -252,3 +252,4 @@ test_files:
|
|
252
252
|
- spec/spec_helper.rb
|
253
253
|
- spec/support/cookies_mock.rb
|
254
254
|
- spec/trial_spec.rb
|
255
|
+
has_rdoc:
|