split 4.0.2 → 4.0.3
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/.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:
|