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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1c97063d4d1ccf4c2cd5dfd2e83b98b3f55a934b9dfa9b5862ede3ee5b8c58c
4
- data.tar.gz: 28e30003a6d2059baf91482f5ac9a97b0b7d2e37c61a425710cbf1adf21a4a83
3
+ metadata.gz: 746dd3b526b5464f12e01e2f00f8ff62b71ac6483527632f6f0bca7bc5242e8c
4
+ data.tar.gz: bb40ca355a1aa9eec9cfb4067e6e2b3c381072830cffadffe414f8d7a4af043f
5
5
  SHA512:
6
- metadata.gz: 988f115f0f96870188221552cca45f6a7207f548500dbf2a5446abaa47ac7365571644a5a231443ca7616182b68c3139f8d62fdcf18d1593fca8898092a332e2
7
- data.tar.gz: b6a668159d8b6fe9529bae5bc650f120b9de1bdf593bc89d0d949a2e07e8cfda7c96b4c12e5e4615696cf31b845215f95ce60b4abcb9ff3d7ef056669a4ae51d
6
+ metadata.gz: 70466ddfe57955a43dda0506c4a23806f6bdab154b344e495879027ae2baebec93d59f34018e78a3e83ef945c776c7a2ddcf2b40cd71998fb0bc9207ed60df61
7
+ data.tar.gz: db1d46d7e7e1826aacf80ec1c8634f0502bd07d4b58d488e57f4917a8843954d568b4cd20c9e2db09ac2ff80811b14ec68a3158b870d091eeabf4e426af8172d
@@ -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@v3
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
@@ -5,5 +5,4 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
 
7
7
  gem "rubocop", require: false
8
- gem "matrix"
9
8
  gem "codeclimate-test-reporter"
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(:experiment => experiment)
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
@@ -3,6 +3,5 @@ source "https://rubygems.org"
3
3
  gem "rubocop", require: false
4
4
  gem "codeclimate-test-reporter"
5
5
  gem "rails", "~> 7.0"
6
- gem "matrix"
7
6
 
8
7
  gemspec path: "../"
@@ -1,14 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
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
- if extra_infos[0][column].kind_of?(Numeric)
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"
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Split
4
- VERSION = "4.0.2"
4
+ VERSION = "4.0.3"
5
5
  end
@@ -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
@@ -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"] = "my_value"
78
- expect(subject["my_key"]).to eq("my_value")
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
@@ -11,6 +11,7 @@ SimpleCov.start
11
11
  require "split"
12
12
  require "ostruct"
13
13
  require "yaml"
14
+ require "pry"
14
15
 
15
16
  Dir["./spec/support/*.rb"].each { |f| require f }
16
17
 
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.2
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: 2022-12-02 00:00:00.000000000 Z
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.3.7
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: