split 4.0.2 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +0 -1
- data/README.md +7 -1
- data/gemfiles/7.0.gemfile +0 -1
- data/lib/split/algorithms.rb +1 -9
- data/lib/split/dashboard/views/_experiment.erb +2 -1
- data/lib/split/experiment.rb +9 -0
- data/lib/split/helper.rb +1 -1
- data/lib/split/persistence/redis_adapter.rb +1 -1
- data/lib/split/redis_interface.rb +7 -0
- data/lib/split/version.rb +1 -1
- data/spec/dashboard_spec.rb +12 -0
- data/spec/experiment_spec.rb +11 -0
- data/spec/helper_spec.rb +36 -0
- data/spec/persistence/redis_adapter_spec.rb +3 -3
- data/spec/redis_interface_spec.rb +10 -0
- data/spec/spec_helper.rb +1 -0
- data/split.gemspec +1 -0
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 746dd3b526b5464f12e01e2f00f8ff62b71ac6483527632f6f0bca7bc5242e8c
|
4
|
+
data.tar.gz: bb40ca355a1aa9eec9cfb4067e6e2b3c381072830cffadffe414f8d7a4af043f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70466ddfe57955a43dda0506c4a23806f6bdab154b344e495879027ae2baebec93d59f34018e78a3e83ef945c776c7a2ddcf2b40cd71998fb0bc9207ed60df61
|
7
|
+
data.tar.gz: db1d46d7e7e1826aacf80ec1c8634f0502bd07d4b58d488e57f4917a8843954d568b4cd20c9e2db09ac2ff80811b14ec68a3158b870d091eeabf4e426af8172d
|
data/.github/workflows/ci.yml
CHANGED
@@ -37,6 +37,8 @@ jobs:
|
|
37
37
|
- gemfile: 7.0.gemfile
|
38
38
|
ruby: '3.1'
|
39
39
|
|
40
|
+
- gemfile: 7.0.gemfile
|
41
|
+
ruby: '3.2'
|
40
42
|
|
41
43
|
runs-on: ubuntu-latest
|
42
44
|
|
@@ -51,7 +53,7 @@ jobs:
|
|
51
53
|
--health-retries 5
|
52
54
|
|
53
55
|
steps:
|
54
|
-
- uses: actions/checkout@
|
56
|
+
- uses: actions/checkout@v4
|
55
57
|
|
56
58
|
- uses: ruby/setup-ruby@v1
|
57
59
|
with:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# 4.0.3 (November 15rd, 2023)
|
2
|
+
|
3
|
+
Bugfixes:
|
4
|
+
- Do not throw error if alternativas have data that can lead to negative numbers for probability calculation (@andrehjr, #703)
|
5
|
+
- Do not persist invalid extra_info on ab_record_extra_info. (@trostli @andrehjr, #717)
|
6
|
+
- CROSSSLOT keys issue fix when using redis cluster (@naveen-chidhambaram, #710)
|
7
|
+
- Convert value to string before saving it in RedisAdapter (@Jealrock, #714)
|
8
|
+
- Fix deprecation warning with Redis 4.8.0 (@martingregoire, #701)
|
9
|
+
|
10
|
+
Misc:
|
11
|
+
- Add matrix as a default dependency (@andrehjr, #705)
|
12
|
+
- Add Ruby 3.2 to Github Actions (@andrehjr, #702)
|
13
|
+
- Update documentation regarding finding users outside a web session (@andrehjr, #716)
|
14
|
+
- Update actions/checkout to v4 (@andrehjr, #718)
|
15
|
+
|
1
16
|
# 4.0.2 (December 2nd, 2022)
|
2
17
|
|
3
18
|
Bugfixes:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -807,10 +807,16 @@ conduct experiments that are not tied to a web session.
|
|
807
807
|
```ruby
|
808
808
|
# create a new experiment
|
809
809
|
experiment = Split::ExperimentCatalog.find_or_create('color', 'red', 'blue')
|
810
|
+
|
811
|
+
# find the user
|
812
|
+
user = Split::User.find(user_id, :redis)
|
813
|
+
|
810
814
|
# create a new trial
|
811
|
-
trial = Split::Trial.new(:
|
815
|
+
trial = Split::Trial.new(user: user, experiment: experiment)
|
816
|
+
|
812
817
|
# run trial
|
813
818
|
trial.choose!
|
819
|
+
|
814
820
|
# get the result, returns either red or blue
|
815
821
|
trial.alternative.name
|
816
822
|
|
data/gemfiles/7.0.gemfile
CHANGED
data/lib/split/algorithms.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "matrix"
|
5
|
-
rescue LoadError => error
|
6
|
-
if error.message.match?(/matrix/)
|
7
|
-
$stderr.puts "You don't have matrix installed in your application. Please add it to your Gemfile and run bundle install"
|
8
|
-
raise
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
3
|
+
require "matrix"
|
12
4
|
require "rubystats"
|
13
5
|
|
14
6
|
module Split
|
@@ -16,7 +16,8 @@
|
|
16
16
|
summary_texts = {}
|
17
17
|
extra_columns.each do |column|
|
18
18
|
extra_infos = experiment.alternatives.map(&:extra_info).select{|extra_info| extra_info && extra_info[column] }
|
19
|
-
|
19
|
+
|
20
|
+
if extra_infos.length > 0 && extra_infos.all? { |extra_info| extra_info[column].kind_of?(Numeric) }
|
20
21
|
summary_texts[column] = extra_infos.inject(0){|sum, extra_info| sum += extra_info[column]}
|
21
22
|
else
|
22
23
|
summary_texts[column] = "N/A"
|
data/lib/split/experiment.rb
CHANGED
@@ -270,7 +270,16 @@ module Split
|
|
270
270
|
set_alternatives_and_options(options)
|
271
271
|
end
|
272
272
|
|
273
|
+
def can_calculate_winning_alternatives?
|
274
|
+
self.alternatives.all? do |alternative|
|
275
|
+
alternative.participant_count >= 0 &&
|
276
|
+
(alternative.participant_count >= alternative.completed_count)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
273
280
|
def calc_winning_alternatives
|
281
|
+
return unless can_calculate_winning_alternatives?
|
282
|
+
|
274
283
|
# Cache the winning alternatives so we recalculate them once per the specified interval.
|
275
284
|
intervals_since_epoch =
|
276
285
|
Time.now.utc.to_i / Split.configuration.winning_alternative_recalculation_interval
|
data/lib/split/helper.rb
CHANGED
@@ -86,7 +86,7 @@ module Split
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def ab_record_extra_info(metric_descriptor, key, value = 1)
|
89
|
-
return if exclude_visitor? || Split.configuration.disabled?
|
89
|
+
return if exclude_visitor? || Split.configuration.disabled? || value.nil?
|
90
90
|
metric_descriptor, _ = normalize_metric(metric_descriptor)
|
91
91
|
experiments = Metric.possible_experiments(metric_descriptor)
|
92
92
|
|
@@ -27,7 +27,7 @@ module Split
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def []=(field, value)
|
30
|
-
Split.redis.hset(redis_key, field, value)
|
30
|
+
Split.redis.hset(redis_key, field, value.to_s)
|
31
31
|
expire_seconds = self.class.config[:expire_seconds]
|
32
32
|
Split.redis.expire(redis_key, expire_seconds) if expire_seconds
|
33
33
|
end
|
@@ -11,6 +11,7 @@ module Split
|
|
11
11
|
if list_values.length > 0
|
12
12
|
redis.multi do |multi|
|
13
13
|
tmp_list = "#{list_name}_tmp"
|
14
|
+
tmp_list += redis_namespace_used? ? "{#{Split.redis.namespace}:#{list_name}}" : "{#{list_name}}"
|
14
15
|
multi.rpush(tmp_list, list_values)
|
15
16
|
multi.rename(tmp_list, list_name)
|
16
17
|
end
|
@@ -20,10 +21,16 @@ module Split
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def add_to_set(set_name, value)
|
24
|
+
return redis.sadd?(set_name, value) if redis.respond_to?(:sadd?)
|
25
|
+
|
23
26
|
redis.sadd(set_name, value)
|
24
27
|
end
|
25
28
|
|
26
29
|
private
|
27
30
|
attr_accessor :redis
|
31
|
+
|
32
|
+
def redis_namespace_used?
|
33
|
+
Redis.const_defined?("Namespace") && Split.redis.is_a?(Redis::Namespace)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
end
|
data/lib/split/version.rb
CHANGED
data/spec/dashboard_spec.rb
CHANGED
@@ -279,4 +279,16 @@ describe Split::Dashboard do
|
|
279
279
|
|
280
280
|
expect(last_response.body).to include("<small>Unknown</small>")
|
281
281
|
end
|
282
|
+
|
283
|
+
it "should be explode with experiments with invalid data" do
|
284
|
+
red_link.participant_count = 1
|
285
|
+
red_link.set_completed_count(10)
|
286
|
+
|
287
|
+
blue_link.participant_count = 3
|
288
|
+
blue_link.set_completed_count(2)
|
289
|
+
|
290
|
+
get "/"
|
291
|
+
|
292
|
+
expect(last_response).to be_ok
|
293
|
+
end
|
282
294
|
end
|
data/spec/experiment_spec.rb
CHANGED
@@ -576,6 +576,17 @@ describe Split::Experiment do
|
|
576
576
|
expect(p_goal1).not_to be_within(0.04).of(p_goal2)
|
577
577
|
end
|
578
578
|
|
579
|
+
it "should not calculate when data is not valid for beta distribution" do
|
580
|
+
experiment = Split::ExperimentCatalog.find_or_create("scientists", "einstein", "bohr")
|
581
|
+
|
582
|
+
experiment.alternatives.each do |alternative|
|
583
|
+
alternative.participant_count = 9
|
584
|
+
alternative.set_completed_count(10)
|
585
|
+
end
|
586
|
+
|
587
|
+
expect { experiment.calc_winning_alternatives }.to_not raise_error
|
588
|
+
end
|
589
|
+
|
579
590
|
it "should return nil and not re-calculate probabilities if they have already been calculated today" do
|
580
591
|
experiment = Split::ExperimentCatalog.find_or_create({ "link_color3" => ["purchase", "refund"] }, "blue", "red", "green")
|
581
592
|
expect(experiment.calc_winning_alternatives).not_to be nil
|
data/spec/helper_spec.rb
CHANGED
@@ -558,6 +558,42 @@ describe Split::Helper do
|
|
558
558
|
end
|
559
559
|
end
|
560
560
|
|
561
|
+
|
562
|
+
describe "ab_record_extra_info" do
|
563
|
+
context "for an experiment that the user participates in" do
|
564
|
+
before(:each) do
|
565
|
+
@experiment_name = "link_color"
|
566
|
+
@alternatives = ["blue", "red"]
|
567
|
+
@experiment = Split::ExperimentCatalog.find_or_create(@experiment_name, *@alternatives)
|
568
|
+
@alternative_name = ab_test(@experiment_name, *@alternatives)
|
569
|
+
end
|
570
|
+
|
571
|
+
it "records extra data for a given experiment" do
|
572
|
+
alternative = Split::Alternative.new(@alternative_name, "link_color")
|
573
|
+
|
574
|
+
ab_record_extra_info(@experiment_name, "some_data", 10)
|
575
|
+
|
576
|
+
expect(alternative.extra_info).to eql({ "some_data" => 10 })
|
577
|
+
end
|
578
|
+
|
579
|
+
it "records extra data for a given experiment" do
|
580
|
+
alternative = Split::Alternative.new(@alternative_name, "link_color")
|
581
|
+
|
582
|
+
ab_record_extra_info(@experiment_name, "some_data")
|
583
|
+
|
584
|
+
expect(alternative.extra_info).to eql({ "some_data" => 1 })
|
585
|
+
end
|
586
|
+
|
587
|
+
it "records extra data for a given experiment" do
|
588
|
+
alternative = Split::Alternative.new(@alternative_name, "link_color")
|
589
|
+
|
590
|
+
ab_record_extra_info(@experiment_name, "some_data", nil)
|
591
|
+
|
592
|
+
expect(alternative.extra_info).to eql({})
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
561
597
|
describe "conversions" do
|
562
598
|
it "should return a conversion rate for an alternative" do
|
563
599
|
alternative_name = ab_test("link_color", "blue", "red")
|
@@ -73,9 +73,9 @@ describe Split::Persistence::RedisAdapter do
|
|
73
73
|
before { Split::Persistence::RedisAdapter.with_config(lookup_by: "lookup") }
|
74
74
|
|
75
75
|
describe "#[] and #[]=" do
|
76
|
-
it "should set and return the value for given key" do
|
77
|
-
subject["my_key"] =
|
78
|
-
expect(subject["my_key"]).to eq("
|
76
|
+
it "should convert to string, set and return the value for given key" do
|
77
|
+
subject["my_key"] = true
|
78
|
+
expect(subject["my_key"]).to eq("true")
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -40,5 +40,15 @@ describe Split::RedisInterface do
|
|
40
40
|
add_to_set
|
41
41
|
expect(Split.redis.sismember(set_name, "something")).to be true
|
42
42
|
end
|
43
|
+
|
44
|
+
context "when a Redis version is used that supports the 'sadd?' method" do
|
45
|
+
before { expect(Split.redis).to receive(:respond_to?).with(:sadd?).and_return(true) }
|
46
|
+
|
47
|
+
it "will use this method instead of 'sadd'" do
|
48
|
+
expect(Split.redis).to receive(:sadd?).with(set_name, "something")
|
49
|
+
expect(Split.redis).not_to receive(:sadd).with(set_name, "something")
|
50
|
+
add_to_set
|
51
|
+
end
|
52
|
+
end
|
43
53
|
end
|
44
54
|
end
|
data/spec/spec_helper.rb
CHANGED
data/split.gemspec
CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.add_dependency "redis", ">= 4.2"
|
34
34
|
s.add_dependency "sinatra", ">= 1.2.6"
|
35
35
|
s.add_dependency "rubystats", ">= 0.3.0"
|
36
|
+
s.add_dependency "matrix"
|
36
37
|
|
37
38
|
s.add_development_dependency "bundler", ">= 1.17"
|
38
39
|
s.add_development_dependency "simplecov", "~> 0.15"
|
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: 4.0.
|
4
|
+
version: 4.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Nesbitt
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.3.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: matrix
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,7 +164,7 @@ dependencies:
|
|
150
164
|
- - ">="
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '5.0'
|
153
|
-
description:
|
167
|
+
description:
|
154
168
|
email:
|
155
169
|
- andrewnez@gmail.com
|
156
170
|
executables: []
|
@@ -260,7 +274,7 @@ metadata:
|
|
260
274
|
bug_tracker_uri: https://github.com/splitrb/split/issues
|
261
275
|
wiki_uri: https://github.com/splitrb/split/wiki
|
262
276
|
mailing_list_uri: https://groups.google.com/d/forum/split-ruby
|
263
|
-
post_install_message:
|
277
|
+
post_install_message:
|
264
278
|
rdoc_options: []
|
265
279
|
require_paths:
|
266
280
|
- lib
|
@@ -275,8 +289,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
289
|
- !ruby/object:Gem::Version
|
276
290
|
version: 2.0.0
|
277
291
|
requirements: []
|
278
|
-
rubygems_version: 3.
|
279
|
-
signing_key:
|
292
|
+
rubygems_version: 3.1.6
|
293
|
+
signing_key:
|
280
294
|
specification_version: 4
|
281
295
|
summary: Rack based split testing framework
|
282
296
|
test_files:
|