split 3.4.1 → 4.0.4

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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/dependabot.yml +7 -0
  4. data/.github/workflows/ci.yml +76 -0
  5. data/.rubocop.yml +177 -4
  6. data/CHANGELOG.md +87 -0
  7. data/CONTRIBUTING.md +1 -1
  8. data/Gemfile +2 -1
  9. data/README.md +37 -9
  10. data/Rakefile +5 -5
  11. data/gemfiles/5.2.gemfile +1 -3
  12. data/gemfiles/6.0.gemfile +1 -3
  13. data/gemfiles/{5.0.gemfile → 6.1.gemfile} +2 -4
  14. data/gemfiles/{5.1.gemfile → 7.0.gemfile} +2 -4
  15. data/lib/split/algorithms/block_randomization.rb +6 -6
  16. data/lib/split/algorithms/weighted_sample.rb +2 -1
  17. data/lib/split/algorithms/whiplash.rb +17 -18
  18. data/lib/split/algorithms.rb +14 -0
  19. data/lib/split/alternative.rb +22 -22
  20. data/lib/split/cache.rb +27 -0
  21. data/lib/split/combined_experiments_helper.rb +5 -4
  22. data/lib/split/configuration.rb +89 -94
  23. data/lib/split/dashboard/helpers.rb +7 -7
  24. data/lib/split/dashboard/pagination_helpers.rb +54 -54
  25. data/lib/split/dashboard/paginator.rb +1 -0
  26. data/lib/split/dashboard/public/dashboard.js +10 -0
  27. data/lib/split/dashboard/public/style.css +10 -2
  28. data/lib/split/dashboard/views/_controls.erb +13 -0
  29. data/lib/split/dashboard/views/_experiment.erb +2 -1
  30. data/lib/split/dashboard/views/index.erb +19 -4
  31. data/lib/split/dashboard.rb +42 -21
  32. data/lib/split/encapsulated_helper.rb +15 -8
  33. data/lib/split/engine.rb +1 -0
  34. data/lib/split/exceptions.rb +1 -0
  35. data/lib/split/experiment.rb +151 -124
  36. data/lib/split/experiment_catalog.rb +7 -8
  37. data/lib/split/extensions/string.rb +2 -1
  38. data/lib/split/goals_collection.rb +9 -10
  39. data/lib/split/helper.rb +50 -23
  40. data/lib/split/metric.rb +6 -6
  41. data/lib/split/persistence/cookie_adapter.rb +46 -44
  42. data/lib/split/persistence/dual_adapter.rb +7 -8
  43. data/lib/split/persistence/redis_adapter.rb +8 -4
  44. data/lib/split/persistence/session_adapter.rb +1 -2
  45. data/lib/split/persistence.rb +8 -6
  46. data/lib/split/redis_interface.rb +15 -29
  47. data/lib/split/trial.rb +43 -34
  48. data/lib/split/user.rb +25 -14
  49. data/lib/split/version.rb +2 -4
  50. data/lib/split/zscore.rb +2 -3
  51. data/lib/split.rb +34 -27
  52. data/spec/algorithms/block_randomization_spec.rb +6 -5
  53. data/spec/algorithms/weighted_sample_spec.rb +6 -5
  54. data/spec/algorithms/whiplash_spec.rb +4 -5
  55. data/spec/alternative_spec.rb +35 -36
  56. data/spec/cache_spec.rb +84 -0
  57. data/spec/combined_experiments_helper_spec.rb +18 -17
  58. data/spec/configuration_spec.rb +41 -45
  59. data/spec/dashboard/pagination_helpers_spec.rb +69 -67
  60. data/spec/dashboard/paginator_spec.rb +10 -9
  61. data/spec/dashboard_helpers_spec.rb +19 -18
  62. data/spec/dashboard_spec.rb +122 -38
  63. data/spec/encapsulated_helper_spec.rb +46 -22
  64. data/spec/experiment_catalog_spec.rb +14 -13
  65. data/spec/experiment_spec.rb +198 -118
  66. data/spec/goals_collection_spec.rb +18 -16
  67. data/spec/helper_spec.rb +454 -385
  68. data/spec/metric_spec.rb +14 -14
  69. data/spec/persistence/cookie_adapter_spec.rb +26 -11
  70. data/spec/persistence/dual_adapter_spec.rb +71 -71
  71. data/spec/persistence/redis_adapter_spec.rb +35 -27
  72. data/spec/persistence/session_adapter_spec.rb +2 -3
  73. data/spec/persistence_spec.rb +1 -2
  74. data/spec/redis_interface_spec.rb +25 -82
  75. data/spec/spec_helper.rb +35 -24
  76. data/spec/split_spec.rb +11 -11
  77. data/spec/support/cookies_mock.rb +1 -2
  78. data/spec/trial_spec.rb +102 -75
  79. data/spec/user_spec.rb +60 -29
  80. data/split.gemspec +22 -21
  81. metadata +43 -40
  82. data/.rubocop_todo.yml +0 -679
  83. data/.travis.yml +0 -60
  84. data/Appraisals +0 -19
  85. data/gemfiles/4.2.gemfile +0 -9
data/spec/user_spec.rb CHANGED
@@ -1,57 +1,73 @@
1
- require 'spec_helper'
2
- require 'split/experiment_catalog'
3
- require 'split/experiment'
4
- require 'split/user'
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "split/experiment_catalog"
5
+ require "split/experiment"
6
+ require "split/user"
5
7
 
6
8
  describe Split::User do
7
- let(:user_keys) { { 'link_color' => 'blue' } }
8
- let(:context) { double(:session => { split: user_keys }) }
9
- let(:experiment) { Split::Experiment.new('link_color') }
9
+ let(:user_keys) { { "link_color" => "blue" } }
10
+ let(:context) { double(session: { split: user_keys }) }
11
+ let(:experiment) { Split::Experiment.new("link_color") }
10
12
 
11
13
  before(:each) do
12
14
  @subject = described_class.new(context)
13
15
  end
14
16
 
15
- it 'delegates methods correctly' do
16
- expect(@subject['link_color']).to eq(@subject.user['link_color'])
17
+ it "delegates methods correctly" do
18
+ expect(@subject["link_color"]).to eq(@subject.user["link_color"])
17
19
  end
18
20
 
19
- context '#cleanup_old_versions!' do
20
- let(:user_keys) { { 'link_color:1' => 'blue' } }
21
+ context "#cleanup_old_versions!" do
22
+ let(:experiment_version) { "#{experiment.name}:1" }
23
+ let(:second_experiment_version) { "#{experiment.name}_another:1" }
24
+ let(:third_experiment_version) { "variation_of_#{experiment.name}:1" }
25
+ let(:user_keys) do
26
+ {
27
+ experiment_version => "blue",
28
+ second_experiment_version => "red",
29
+ third_experiment_version => "yellow"
30
+ }
31
+ end
32
+
33
+ before(:each) { @subject.cleanup_old_versions!(experiment) }
21
34
 
22
- it 'removes key if old experiment is found' do
23
- @subject.cleanup_old_versions!(experiment)
24
- expect(@subject.keys).to be_empty
35
+ it "removes key if old experiment is found" do
36
+ expect(@subject.keys).not_to include(experiment_version)
25
37
  end
26
- end
27
38
 
28
- context '#cleanup_old_experiments!' do
29
- it 'removes key if experiment is not found' do
39
+ it "does not remove other keys" do
40
+ expect(@subject.keys).to include(second_experiment_version, third_experiment_version)
41
+ end
42
+ end
43
+
44
+ context "#cleanup_old_experiments!" do
45
+ it "removes key if experiment is not found" do
30
46
  @subject.cleanup_old_experiments!
31
47
  expect(@subject.keys).to be_empty
32
48
  end
33
49
 
34
- it 'removes key if experiment has a winner' do
35
- allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment)
50
+ it "removes key if experiment has a winner" do
51
+ allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
36
52
  allow(experiment).to receive(:start_time).and_return(Date.today)
37
53
  allow(experiment).to receive(:has_winner?).and_return(true)
38
54
  @subject.cleanup_old_experiments!
39
55
  expect(@subject.keys).to be_empty
40
56
  end
41
57
 
42
- it 'removes key if experiment has not started yet' do
43
- allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment)
58
+ it "removes key if experiment has not started yet" do
59
+ allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
44
60
  allow(experiment).to receive(:has_winner?).and_return(false)
45
61
  @subject.cleanup_old_experiments!
46
62
  expect(@subject.keys).to be_empty
47
63
  end
48
64
 
49
- context 'with finished key' do
50
- let(:user_keys) { { 'link_color' => 'blue', 'link_color:finished' => true } }
65
+ context "with finished key" do
66
+ let(:user_keys) { { "link_color" => "blue", "link_color:finished" => true } }
51
67
 
52
- it 'does not remove finished key for experiment without a winner' do
53
- allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment)
54
- allow(Split::ExperimentCatalog).to receive(:find).with('link_color:finished').and_return(nil)
68
+ it "does not remove finished key for experiment without a winner" do
69
+ allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
70
+ allow(Split::ExperimentCatalog).to receive(:find).with("link_color:finished").and_return(nil)
55
71
  allow(experiment).to receive(:start_time).and_return(Date.today)
56
72
  allow(experiment).to receive(:has_winner?).and_return(false)
57
73
  @subject.cleanup_old_experiments!
@@ -60,18 +76,34 @@ describe Split::User do
60
76
  end
61
77
  end
62
78
 
63
- context 'when already cleaned up' do
79
+ context "when already cleaned up" do
64
80
  before do
65
81
  @subject.cleanup_old_experiments!
66
82
  end
67
83
 
68
- it 'does not clean up again' do
84
+ it "does not clean up again" do
69
85
  expect(@subject).to_not receive(:keys_without_finished)
70
86
  @subject.cleanup_old_experiments!
71
87
  end
72
88
  end
73
89
  end
74
90
 
91
+ context "allows user to be loaded from adapter" do
92
+ it "loads user from adapter (RedisAdapter)" do
93
+ user = Split::Persistence::RedisAdapter.new(nil, 112233)
94
+ user["foo"] = "bar"
95
+
96
+ ab_user = Split::User.find(112233, :redis)
97
+
98
+ expect(ab_user["foo"]).to eql("bar")
99
+ end
100
+
101
+ it "returns nil if adapter does not implement a finder method" do
102
+ ab_user = Split::User.find(112233, :dual_adapter)
103
+ expect(ab_user).to be_nil
104
+ end
105
+ end
106
+
75
107
  context "instantiated with custom adapter" do
76
108
  let(:custom_adapter) { double(:persistence_adapter) }
77
109
 
@@ -83,5 +115,4 @@ describe Split::User do
83
115
  expect(@subject.user).to eq(custom_adapter)
84
116
  end
85
117
  end
86
-
87
118
  end
data/split.gemspec CHANGED
@@ -1,5 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
+
3
4
  $:.push File.expand_path("../lib", __FILE__)
4
5
  require "split/version"
5
6
 
@@ -8,37 +9,37 @@ Gem::Specification.new do |s|
8
9
  s.version = Split::VERSION
9
10
  s.platform = Gem::Platform::RUBY
10
11
  s.authors = ["Andrew Nesbitt"]
11
- s.licenses = ['MIT']
12
+ s.licenses = ["MIT"]
12
13
  s.email = ["andrewnez@gmail.com"]
13
14
  s.homepage = "https://github.com/splitrb/split"
14
15
  s.summary = "Rack based split testing framework"
15
16
 
16
17
  s.metadata = {
17
- "homepage_uri" => "https://github.com/splitrb/split",
18
- "changelog_uri" => "https://github.com/splitrb/split/blob/master/CHANGELOG.md",
19
- "source_code_uri" => "https://github.com/splitrb/split",
20
- "bug_tracker_uri" => "https://github.com/splitrb/split/issues",
21
- "wiki_uri" => "https://github.com/splitrb/split/wiki",
22
- "mailing_list_uri" => "https://groups.google.com/d/forum/split-ruby"
23
- }
18
+ "homepage_uri" => "https://github.com/splitrb/split",
19
+ "changelog_uri" => "https://github.com/splitrb/split/blob/main/CHANGELOG.md",
20
+ "source_code_uri" => "https://github.com/splitrb/split",
21
+ "bug_tracker_uri" => "https://github.com/splitrb/split/issues",
22
+ "wiki_uri" => "https://github.com/splitrb/split/wiki",
23
+ "mailing_list_uri" => "https://groups.google.com/d/forum/split-ruby"
24
+ }
24
25
 
25
- s.required_ruby_version = '>= 1.9.3'
26
- s.required_rubygems_version = '>= 2.0.0'
26
+ s.required_ruby_version = ">= 2.5.0"
27
+ s.required_rubygems_version = ">= 2.0.0"
27
28
 
28
29
  s.files = `git ls-files`.split("\n")
29
30
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
31
  s.require_paths = ["lib"]
31
32
 
32
- s.add_dependency 'redis', '>= 2.1'
33
- s.add_dependency 'sinatra', '>= 1.2.6'
34
- s.add_dependency 'simple-random', '>= 0.9.3'
33
+ s.add_dependency "redis", ">= 4.2"
34
+ s.add_dependency "sinatra", ">= 1.2.6"
35
+ s.add_dependency "rubystats", ">= 0.3.0"
36
+ s.add_dependency "matrix"
35
37
 
36
- s.add_development_dependency 'bundler', '>= 1.17'
37
- s.add_development_dependency 'simplecov', '~> 0.15'
38
- s.add_development_dependency 'rack-test', '~> 0.6'
39
- s.add_development_dependency 'rake', '~> 12'
40
- s.add_development_dependency 'rspec', '~> 3.7'
41
- s.add_development_dependency 'pry', '~> 0.10'
42
- s.add_development_dependency 'fakeredis', '~> 0.7'
43
- s.add_development_dependency 'rails', '>= 4.2'
38
+ s.add_development_dependency "bundler", ">= 1.17"
39
+ s.add_development_dependency "simplecov", "~> 0.15"
40
+ s.add_development_dependency "rack-test", "~> 2.0"
41
+ s.add_development_dependency "rake", "~> 13"
42
+ s.add_development_dependency "rspec", "~> 3.7"
43
+ s.add_development_dependency "pry", "~> 0.10"
44
+ s.add_development_dependency "rails", ">= 5.0"
44
45
  end
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: 3.4.1
4
+ version: 4.0.4
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: 2019-11-12 00:00:00.000000000 Z
11
+ date: 2024-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: '4.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.1'
26
+ version: '4.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sinatra
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +39,33 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.2.6
41
41
  - !ruby/object:Gem::Dependency
42
- name: simple-random
42
+ name: rubystats
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: matrix
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: 0.9.3
61
+ version: '0'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: 0.9.3
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +100,28 @@ dependencies:
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '0.6'
103
+ version: '2.0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '0.6'
110
+ version: '2.0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rake
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '12'
117
+ version: '13'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '12'
124
+ version: '13'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -136,35 +150,21 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '0.10'
139
- - !ruby/object:Gem::Dependency
140
- name: fakeredis
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '0.7'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '0.7'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: rails
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - ">="
158
158
  - !ruby/object:Gem::Version
159
- version: '4.2'
159
+ version: '5.0'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
- version: '4.2'
167
- description:
166
+ version: '5.0'
167
+ description:
168
168
  email:
169
169
  - andrewnez@gmail.com
170
170
  executables: []
@@ -175,13 +175,13 @@ files:
175
175
  - ".csslintrc"
176
176
  - ".eslintignore"
177
177
  - ".eslintrc"
178
+ - ".github/FUNDING.yml"
178
179
  - ".github/ISSUE_TEMPLATE/bug_report.md"
180
+ - ".github/dependabot.yml"
181
+ - ".github/workflows/ci.yml"
179
182
  - ".gitignore"
180
183
  - ".rspec"
181
184
  - ".rubocop.yml"
182
- - ".rubocop_todo.yml"
183
- - ".travis.yml"
184
- - Appraisals
185
185
  - CHANGELOG.md
186
186
  - CODE_OF_CONDUCT.md
187
187
  - CONTRIBUTING.md
@@ -189,16 +189,17 @@ files:
189
189
  - LICENSE
190
190
  - README.md
191
191
  - Rakefile
192
- - gemfiles/4.2.gemfile
193
- - gemfiles/5.0.gemfile
194
- - gemfiles/5.1.gemfile
195
192
  - gemfiles/5.2.gemfile
196
193
  - gemfiles/6.0.gemfile
194
+ - gemfiles/6.1.gemfile
195
+ - gemfiles/7.0.gemfile
197
196
  - lib/split.rb
197
+ - lib/split/algorithms.rb
198
198
  - lib/split/algorithms/block_randomization.rb
199
199
  - lib/split/algorithms/weighted_sample.rb
200
200
  - lib/split/algorithms/whiplash.rb
201
201
  - lib/split/alternative.rb
202
+ - lib/split/cache.rb
202
203
  - lib/split/combined_experiments_helper.rb
203
204
  - lib/split/configuration.rb
204
205
  - lib/split/dashboard.rb
@@ -238,6 +239,7 @@ files:
238
239
  - spec/algorithms/weighted_sample_spec.rb
239
240
  - spec/algorithms/whiplash_spec.rb
240
241
  - spec/alternative_spec.rb
242
+ - spec/cache_spec.rb
241
243
  - spec/combined_experiments_helper_spec.rb
242
244
  - spec/configuration_spec.rb
243
245
  - spec/dashboard/pagination_helpers_spec.rb
@@ -267,12 +269,12 @@ licenses:
267
269
  - MIT
268
270
  metadata:
269
271
  homepage_uri: https://github.com/splitrb/split
270
- changelog_uri: https://github.com/splitrb/split/blob/master/CHANGELOG.md
272
+ changelog_uri: https://github.com/splitrb/split/blob/main/CHANGELOG.md
271
273
  source_code_uri: https://github.com/splitrb/split
272
274
  bug_tracker_uri: https://github.com/splitrb/split/issues
273
275
  wiki_uri: https://github.com/splitrb/split/wiki
274
276
  mailing_list_uri: https://groups.google.com/d/forum/split-ruby
275
- post_install_message:
277
+ post_install_message:
276
278
  rdoc_options: []
277
279
  require_paths:
278
280
  - lib
@@ -280,15 +282,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
280
282
  requirements:
281
283
  - - ">="
282
284
  - !ruby/object:Gem::Version
283
- version: 1.9.3
285
+ version: 2.5.0
284
286
  required_rubygems_version: !ruby/object:Gem::Requirement
285
287
  requirements:
286
288
  - - ">="
287
289
  - !ruby/object:Gem::Version
288
290
  version: 2.0.0
289
291
  requirements: []
290
- rubygems_version: 3.0.3
291
- signing_key:
292
+ rubygems_version: 3.5.3
293
+ signing_key:
292
294
  specification_version: 4
293
295
  summary: Rack based split testing framework
294
296
  test_files:
@@ -296,6 +298,7 @@ test_files:
296
298
  - spec/algorithms/weighted_sample_spec.rb
297
299
  - spec/algorithms/whiplash_spec.rb
298
300
  - spec/alternative_spec.rb
301
+ - spec/cache_spec.rb
299
302
  - spec/combined_experiments_helper_spec.rb
300
303
  - spec/configuration_spec.rb
301
304
  - spec/dashboard/pagination_helpers_spec.rb