split 1.2.1 → 1.3.0
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.
- 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:
|