split 3.3.2 → 4.0.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc +1 -1
- data/.github/FUNDING.yml +1 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/ci.yml +61 -0
- data/.rspec +1 -0
- data/.rubocop.yml +71 -1044
- data/.rubocop_todo.yml +226 -0
- data/Appraisals +1 -1
- data/CHANGELOG.md +62 -0
- data/CODE_OF_CONDUCT.md +3 -3
- data/Gemfile +2 -0
- data/README.md +40 -18
- data/Rakefile +2 -0
- data/gemfiles/6.0.gemfile +1 -1
- data/lib/split/algorithms/block_randomization.rb +2 -0
- data/lib/split/algorithms/weighted_sample.rb +2 -1
- data/lib/split/algorithms/whiplash.rb +3 -2
- data/lib/split/alternative.rb +4 -3
- data/lib/split/cache.rb +28 -0
- data/lib/split/combined_experiments_helper.rb +2 -1
- data/lib/split/configuration.rb +13 -14
- data/lib/split/dashboard/helpers.rb +1 -0
- data/lib/split/dashboard/pagination_helpers.rb +3 -3
- data/lib/split/dashboard/paginator.rb +1 -0
- data/lib/split/dashboard/public/dashboard.js +10 -0
- data/lib/split/dashboard/public/style.css +5 -0
- data/lib/split/dashboard/views/_controls.erb +13 -0
- data/lib/split/dashboard/views/layout.erb +1 -1
- data/lib/split/dashboard.rb +19 -1
- data/lib/split/encapsulated_helper.rb +3 -2
- data/lib/split/engine.rb +7 -4
- data/lib/split/exceptions.rb +1 -0
- data/lib/split/experiment.rb +98 -65
- data/lib/split/experiment_catalog.rb +1 -3
- data/lib/split/extensions/string.rb +1 -0
- data/lib/split/goals_collection.rb +2 -0
- data/lib/split/helper.rb +28 -8
- data/lib/split/metric.rb +2 -1
- data/lib/split/persistence/cookie_adapter.rb +6 -1
- data/lib/split/persistence/dual_adapter.rb +54 -12
- data/lib/split/persistence/redis_adapter.rb +5 -0
- data/lib/split/persistence/session_adapter.rb +1 -0
- data/lib/split/persistence.rb +4 -2
- data/lib/split/redis_interface.rb +9 -28
- data/lib/split/trial.rb +21 -11
- data/lib/split/user.rb +20 -4
- data/lib/split/version.rb +2 -4
- data/lib/split/zscore.rb +1 -0
- data/lib/split.rb +9 -3
- data/spec/alternative_spec.rb +1 -1
- data/spec/cache_spec.rb +88 -0
- data/spec/configuration_spec.rb +17 -15
- data/spec/dashboard/pagination_helpers_spec.rb +3 -1
- data/spec/dashboard_helpers_spec.rb +2 -2
- data/spec/dashboard_spec.rb +78 -17
- data/spec/encapsulated_helper_spec.rb +2 -2
- data/spec/experiment_spec.rb +116 -12
- data/spec/goals_collection_spec.rb +1 -1
- data/spec/helper_spec.rb +186 -112
- data/spec/persistence/cookie_adapter_spec.rb +1 -1
- data/spec/persistence/dual_adapter_spec.rb +160 -68
- data/spec/persistence/redis_adapter_spec.rb +9 -0
- data/spec/redis_interface_spec.rb +0 -69
- data/spec/spec_helper.rb +5 -6
- data/spec/trial_spec.rb +45 -19
- data/spec/user_spec.rb +45 -3
- data/split.gemspec +8 -9
- metadata +28 -36
- data/.travis.yml +0 -66
- data/gemfiles/4.2.gemfile +0 -9
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2020-07-05 01:43:26 UTC using RuboCop version 0.86.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
Layout/CommentIndentation:
|
12
|
+
Exclude:
|
13
|
+
- 'lib/split/experiment.rb'
|
14
|
+
|
15
|
+
# Offense count: 1
|
16
|
+
# Cop supports --auto-correct.
|
17
|
+
Layout/ElseAlignment:
|
18
|
+
Exclude:
|
19
|
+
- 'lib/split/experiment.rb'
|
20
|
+
|
21
|
+
# Offense count: 14
|
22
|
+
# Cop supports --auto-correct.
|
23
|
+
# Configuration parameters: EnforcedStyle.
|
24
|
+
# SupportedStyles: around, only_before
|
25
|
+
Layout/EmptyLinesAroundAccessModifier:
|
26
|
+
Exclude:
|
27
|
+
- 'lib/split/algorithms/block_randomization.rb'
|
28
|
+
- 'lib/split/algorithms/whiplash.rb'
|
29
|
+
- 'lib/split/alternative.rb'
|
30
|
+
- 'lib/split/configuration.rb'
|
31
|
+
- 'lib/split/dashboard/pagination_helpers.rb'
|
32
|
+
- 'lib/split/encapsulated_helper.rb'
|
33
|
+
- 'lib/split/experiment.rb'
|
34
|
+
- 'lib/split/goals_collection.rb'
|
35
|
+
- 'lib/split/persistence/cookie_adapter.rb'
|
36
|
+
- 'lib/split/persistence/dual_adapter.rb'
|
37
|
+
- 'lib/split/redis_interface.rb'
|
38
|
+
- 'lib/split/trial.rb'
|
39
|
+
- 'lib/split/user.rb'
|
40
|
+
|
41
|
+
# Offense count: 8
|
42
|
+
# Cop supports --auto-correct.
|
43
|
+
# Configuration parameters: EnforcedStyle.
|
44
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
|
45
|
+
Layout/EmptyLinesAroundClassBody:
|
46
|
+
Exclude:
|
47
|
+
- 'lib/split/experiment_catalog.rb'
|
48
|
+
- 'lib/split/goals_collection.rb'
|
49
|
+
- 'lib/split/metric.rb'
|
50
|
+
- 'lib/split/persistence/cookie_adapter.rb'
|
51
|
+
- 'lib/split/persistence/redis_adapter.rb'
|
52
|
+
- 'lib/split/persistence/session_adapter.rb'
|
53
|
+
- 'lib/split/zscore.rb'
|
54
|
+
|
55
|
+
# Offense count: 2
|
56
|
+
# Cop supports --auto-correct.
|
57
|
+
Layout/EmptyLinesAroundMethodBody:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/split/dashboard/helpers.rb'
|
60
|
+
- 'lib/split/zscore.rb'
|
61
|
+
|
62
|
+
# Offense count: 1
|
63
|
+
# Cop supports --auto-correct.
|
64
|
+
# Configuration parameters: EnforcedStyle.
|
65
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
|
66
|
+
Layout/EmptyLinesAroundModuleBody:
|
67
|
+
Exclude:
|
68
|
+
- 'lib/split/encapsulated_helper.rb'
|
69
|
+
|
70
|
+
# Offense count: 4
|
71
|
+
# Cop supports --auto-correct.
|
72
|
+
# Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity.
|
73
|
+
# SupportedStylesAlignWith: keyword, variable, start_of_line
|
74
|
+
Layout/EndAlignment:
|
75
|
+
Exclude:
|
76
|
+
- 'lib/split/configuration.rb'
|
77
|
+
- 'lib/split/experiment.rb'
|
78
|
+
- 'lib/split/trial.rb'
|
79
|
+
|
80
|
+
# Offense count: 17
|
81
|
+
# Cop supports --auto-correct.
|
82
|
+
# Configuration parameters: Width, IgnoredPatterns.
|
83
|
+
Layout/IndentationWidth:
|
84
|
+
Exclude:
|
85
|
+
- 'lib/split/algorithms/block_randomization.rb'
|
86
|
+
- 'lib/split/algorithms/whiplash.rb'
|
87
|
+
- 'lib/split/alternative.rb'
|
88
|
+
- 'lib/split/configuration.rb'
|
89
|
+
- 'lib/split/dashboard/pagination_helpers.rb'
|
90
|
+
- 'lib/split/encapsulated_helper.rb'
|
91
|
+
- 'lib/split/experiment.rb'
|
92
|
+
- 'lib/split/goals_collection.rb'
|
93
|
+
- 'lib/split/persistence/cookie_adapter.rb'
|
94
|
+
- 'lib/split/persistence/dual_adapter.rb'
|
95
|
+
- 'lib/split/redis_interface.rb'
|
96
|
+
- 'lib/split/trial.rb'
|
97
|
+
- 'lib/split/user.rb'
|
98
|
+
|
99
|
+
# Offense count: 4
|
100
|
+
# Cop supports --auto-correct.
|
101
|
+
# Configuration parameters: EnforcedStyle.
|
102
|
+
# SupportedStyles: space, no_space
|
103
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
104
|
+
Exclude:
|
105
|
+
- 'lib/split/goals_collection.rb'
|
106
|
+
- 'lib/split/persistence/dual_adapter.rb'
|
107
|
+
- 'lib/split/persistence/redis_adapter.rb'
|
108
|
+
- 'lib/split/user.rb'
|
109
|
+
|
110
|
+
# Offense count: 15
|
111
|
+
# Cop supports --auto-correct.
|
112
|
+
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
|
113
|
+
# SupportedStyles: space, no_space
|
114
|
+
# SupportedStylesForEmptyBraces: space, no_space
|
115
|
+
Layout/SpaceBeforeBlockBraces:
|
116
|
+
Exclude:
|
117
|
+
- 'lib/split/configuration.rb'
|
118
|
+
- 'lib/split/experiment.rb'
|
119
|
+
- 'lib/split/experiment_catalog.rb'
|
120
|
+
- 'lib/split/helper.rb'
|
121
|
+
- 'lib/split/trial.rb'
|
122
|
+
|
123
|
+
# Offense count: 35
|
124
|
+
# Cop supports --auto-correct.
|
125
|
+
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
|
126
|
+
# SupportedStyles: space, no_space
|
127
|
+
# SupportedStylesForEmptyBraces: space, no_space
|
128
|
+
Layout/SpaceInsideBlockBraces:
|
129
|
+
Exclude:
|
130
|
+
- 'lib/split.rb'
|
131
|
+
- 'lib/split/configuration.rb'
|
132
|
+
- 'lib/split/experiment.rb'
|
133
|
+
- 'lib/split/experiment_catalog.rb'
|
134
|
+
- 'lib/split/helper.rb'
|
135
|
+
- 'lib/split/trial.rb'
|
136
|
+
- 'lib/split/user.rb'
|
137
|
+
|
138
|
+
# Offense count: 10
|
139
|
+
# Cop supports --auto-correct.
|
140
|
+
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
|
141
|
+
# SupportedStyles: space, no_space, compact
|
142
|
+
# SupportedStylesForEmptyBraces: space, no_space
|
143
|
+
Layout/SpaceInsideHashLiteralBraces:
|
144
|
+
Exclude:
|
145
|
+
- 'lib/split/experiment.rb'
|
146
|
+
- 'lib/split/helper.rb'
|
147
|
+
- 'lib/split/persistence/redis_adapter.rb'
|
148
|
+
|
149
|
+
# Offense count: 1
|
150
|
+
# Cop supports --auto-correct.
|
151
|
+
# Configuration parameters: EnforcedStyle.
|
152
|
+
# SupportedStyles: final_newline, final_blank_line
|
153
|
+
Layout/TrailingEmptyLines:
|
154
|
+
Exclude:
|
155
|
+
- 'Rakefile'
|
156
|
+
|
157
|
+
# Offense count: 3
|
158
|
+
# Cop supports --auto-correct.
|
159
|
+
# Configuration parameters: AllowInHeredoc.
|
160
|
+
Layout/TrailingWhitespace:
|
161
|
+
Exclude:
|
162
|
+
- 'lib/split/helper.rb'
|
163
|
+
|
164
|
+
# Offense count: 1
|
165
|
+
Lint/UselessAssignment:
|
166
|
+
Exclude:
|
167
|
+
- 'lib/split/goals_collection.rb'
|
168
|
+
|
169
|
+
# Offense count: 1
|
170
|
+
# Cop supports --auto-correct.
|
171
|
+
# Configuration parameters: EnforcedStyle.
|
172
|
+
# SupportedStyles: always, conditionals
|
173
|
+
Style/AndOr:
|
174
|
+
Exclude:
|
175
|
+
- 'lib/split/experiment_catalog.rb'
|
176
|
+
|
177
|
+
# Offense count: 1
|
178
|
+
# Cop supports --auto-correct.
|
179
|
+
Style/ColonMethodCall:
|
180
|
+
Exclude:
|
181
|
+
- 'lib/split/combined_experiments_helper.rb'
|
182
|
+
|
183
|
+
# Offense count: 1
|
184
|
+
# Cop supports --auto-correct.
|
185
|
+
Style/DefWithParentheses:
|
186
|
+
Exclude:
|
187
|
+
- 'lib/split/helper.rb'
|
188
|
+
|
189
|
+
# Offense count: 23
|
190
|
+
# Cop supports --auto-correct.
|
191
|
+
# Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
|
192
|
+
# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
|
193
|
+
Style/HashSyntax:
|
194
|
+
Exclude:
|
195
|
+
- 'Rakefile'
|
196
|
+
- 'lib/split/experiment.rb'
|
197
|
+
- 'lib/split/experiment_catalog.rb'
|
198
|
+
- 'lib/split/helper.rb'
|
199
|
+
- 'lib/split/metric.rb'
|
200
|
+
- 'lib/split/persistence.rb'
|
201
|
+
- 'lib/split/persistence/redis_adapter.rb'
|
202
|
+
|
203
|
+
# Offense count: 1
|
204
|
+
# Cop supports --auto-correct.
|
205
|
+
# Configuration parameters: EnforcedStyle.
|
206
|
+
# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline
|
207
|
+
Style/MethodDefParentheses:
|
208
|
+
Exclude:
|
209
|
+
- 'lib/split/configuration.rb'
|
210
|
+
|
211
|
+
# Offense count: 9
|
212
|
+
# Cop supports --auto-correct.
|
213
|
+
# Configuration parameters: AllowMultipleReturnValues.
|
214
|
+
Style/RedundantReturn:
|
215
|
+
Exclude:
|
216
|
+
- 'lib/split/alternative.rb'
|
217
|
+
- 'lib/split/experiment.rb'
|
218
|
+
- 'lib/split/helper.rb'
|
219
|
+
- 'lib/split/zscore.rb'
|
220
|
+
|
221
|
+
# Offense count: 258
|
222
|
+
# Cop supports --auto-correct.
|
223
|
+
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
224
|
+
# SupportedStyles: single_quotes, double_quotes
|
225
|
+
Style/StringLiterals:
|
226
|
+
Enabled: false
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,65 @@
|
|
1
|
+
## 4.0.0.pre2
|
2
|
+
|
3
|
+
Bugfixes:
|
4
|
+
- ab_test must return metadata on error or if split is disabled/excluded user (@andrehjr, #622)
|
5
|
+
- Fix versioned experiments when used with allow_multiple_experiments=control (@andrehjr, #613)
|
6
|
+
- Only block Pinterest bot (@huoxito, #606)
|
7
|
+
- Respect experiment defaults when loading experiments in initializer. (@mattwd7, #599)
|
8
|
+
- Removes metadata key when it updated to nil (@andrehjr, #633)
|
9
|
+
- Force experiment does not count for metrics (@andrehjr, #637)
|
10
|
+
- Fix cleanup_old_versions! misbehaviour (@serggi, #661)
|
11
|
+
|
12
|
+
Features:
|
13
|
+
- Make goals accessible via on_trial_complete callbacks (@robin-phung, #625)
|
14
|
+
- Replace usage of SimpleRandom with RubyStats(Used for Beta Distribution RNG) (@andrehjr, #616)
|
15
|
+
- Introduce enable/disable experiment cohorting (@robin-phung, #615)
|
16
|
+
- Add on_experiment_winner_choose callback (@GenaMinenkov, #574)
|
17
|
+
- Add Split::Cache to reduce load on Redis (@rdh, #648)
|
18
|
+
- Caching based optimization in the experiment#save path (@amangup, #652)
|
19
|
+
- Adds config option for cookie domain (@joedelia, #664)
|
20
|
+
|
21
|
+
Misc:
|
22
|
+
- Drop support for Ruby < 2.5 (@andrehjr, #627)
|
23
|
+
- Drop support for Rails < 5 (@andrehjr, #607)
|
24
|
+
- Bump minimum required redis to 4.2 (@andrehjr, #628)
|
25
|
+
- Removed repeated loading from config (@robin-phung, #619)
|
26
|
+
- Simplify RedisInterface usage when persisting Experiment alternatives (@andrehjr, #632)
|
27
|
+
- Remove redis_url impl. Deprecated on version 2.2 (@andrehjr, #631)
|
28
|
+
- Remove thread_safe config as redis-rb is thread_safe by default (@andrehjr, #630)
|
29
|
+
- Fix typo of in `Split::Trial` class variable (TomasBarry, #644)
|
30
|
+
- Single HSET to update values, instead of multiple ones (@andrehjr, #640)
|
31
|
+
- Use Redis#hmset to keep compatibility with Redis < 4.0 (@andrehjr, #659)
|
32
|
+
- Remove 'set' parsing for alternatives. Sets were used as storage and deprecated on 0.x (@andrehjr, #639)
|
33
|
+
- Adding documentation related to what is stored on cookies. (@andrehjr, #634)
|
34
|
+
- Keep railtie defined under the Split gem namespace (@avit, #666)
|
35
|
+
|
36
|
+
## 3.4.1 (November 12th, 2019)
|
37
|
+
|
38
|
+
Bugfixes:
|
39
|
+
- Reference ActionController directly when including split helpers, to avoid breaking Rails API Controllers (@andrehjr, #602)
|
40
|
+
|
41
|
+
## 3.4.0 (November 9th, 2019)
|
42
|
+
|
43
|
+
Features:
|
44
|
+
- Improve DualAdapter (@santib, #588), adds a new configuration for the DualAdapter, making it possible to keep consistency for logged_out/logged_in users. It's a opt-in flag. No Behavior was changed on this release.
|
45
|
+
- Make dashboard pagination default "per" param configurable (@alopatin, #597)
|
46
|
+
|
47
|
+
Bugfixes:
|
48
|
+
- Fix `force_alternative` for experiments with incremented version (@giraffate, #568)
|
49
|
+
- Persist alternative weights (@giraffate, #570)
|
50
|
+
- Combined experiment performance improvements (@gnanou, #575)
|
51
|
+
- Handle correctly case when ab_finished is called before ab_test for a user (@gnanou, #577)
|
52
|
+
- When loading active_experiments, it should not look into user's 'finished' keys (@andrehjr, #582)
|
53
|
+
|
54
|
+
Misc:
|
55
|
+
- Remove `rubyforge_project` from gemspec (@giraffate, #583)
|
56
|
+
- Fix URLs to replace http with https (@giraffate , #584)
|
57
|
+
- Lazily include split helpers in ActionController::Base (@hasghari, #586)
|
58
|
+
- Fix unused variable warnings (@andrehjr, #592)
|
59
|
+
- Fix ruby warnings (@andrehjr, #593)
|
60
|
+
- Update rubocop.yml config (@andrehjr, #594)
|
61
|
+
- Add frozen_string_literal to all files that were missing it (@andrehjr, #595)
|
62
|
+
|
1
63
|
## 3.3.2 (April 12th, 2019)
|
2
64
|
|
3
65
|
Features:
|
data/CODE_OF_CONDUCT.md
CHANGED
@@ -68,7 +68,7 @@ members of the project's leadership.
|
|
68
68
|
## Attribution
|
69
69
|
|
70
70
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
-
available at [
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
72
72
|
|
73
|
-
[homepage]:
|
74
|
-
[version]:
|
73
|
+
[homepage]: https://contributor-covenant.org
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
# [Split](
|
1
|
+
# [Split](https://libraries.io/rubygems/split)
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/split.svg)](http://badge.fury.io/rb/split)
|
4
|
-
|
4
|
+
![Build status](https://github.com/splitrb/split/actions/workflows/ci.yml/badge.svg?branch=main)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/splitrb/split/badges/gpa.svg)](https://codeclimate.com/github/splitrb/split)
|
6
6
|
[![Test Coverage](https://codeclimate.com/github/splitrb/split/badges/coverage.svg)](https://codeclimate.com/github/splitrb/split/coverage)
|
7
7
|
[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
|
8
8
|
[![Open Source Helpers](https://www.codetriage.com/splitrb/split/badges/users.svg)](https://www.codetriage.com/splitrb/split)
|
9
9
|
|
10
|
-
> 📈 The Rack Based A/B testing framework
|
10
|
+
> 📈 The Rack Based A/B testing framework https://libraries.io/rubygems/split
|
11
11
|
|
12
12
|
Split is a rack based A/B testing framework designed to work with Rails, Sinatra or any other rack based app.
|
13
13
|
|
@@ -110,9 +110,9 @@ Split has two options for you to use to determine which alternative is the best.
|
|
110
110
|
|
111
111
|
The first option (default on the dashboard) uses a z test (n>30) for the difference between your control and alternative conversion rates to calculate statistical significance. This test will tell you whether an alternative is better or worse than your control, but it will not distinguish between which alternative is the best in an experiment with multiple alternatives. Split will only tell you if your experiment is 90%, 95%, or 99% significant, and this test only works if you have more than 30 participants and 5 conversions for each branch.
|
112
112
|
|
113
|
-
As per this [blog post](
|
113
|
+
As per this [blog post](https://www.evanmiller.org/how-not-to-run-an-ab-test.html) on the pitfalls of A/B testing, it is highly recommended that you determine your requisite sample size for each branch before running the experiment. Otherwise, you'll have an increased rate of false positives (experiments which show a significant effect where really there is none).
|
114
114
|
|
115
|
-
[Here](
|
115
|
+
[Here](https://www.evanmiller.org/ab-testing/sample-size.html) is a sample size calculator for your convenience.
|
116
116
|
|
117
117
|
The second option uses simulations from a beta distribution to determine the probability that the given alternative is the winner compared to all other alternatives. You can view these probabilities by clicking on the drop-down menu labeled "Confidence." This option should be used when the experiment has more than just 1 control and 1 alternative. It can also be used for a simple, 2-alternative A/B test.
|
118
118
|
|
@@ -175,8 +175,10 @@ module SplitHelper
|
|
175
175
|
# use_ab_test(signup_form: "single_page", pricing: "show_enterprise_prices")
|
176
176
|
#
|
177
177
|
def use_ab_test(alternatives_by_experiment)
|
178
|
-
allow_any_instance_of(Split::Helper).to receive(:ab_test) do |_receiver, experiment|
|
179
|
-
alternatives_by_experiment.fetch(experiment) { |key| raise "Unknown experiment '#{key}'" }
|
178
|
+
allow_any_instance_of(Split::Helper).to receive(:ab_test) do |_receiver, experiment, &block|
|
179
|
+
variant = alternatives_by_experiment.fetch(experiment) { |key| raise "Unknown experiment '#{key}'" }
|
180
|
+
block.call(variant) unless block.nil?
|
181
|
+
variant
|
180
182
|
end
|
181
183
|
end
|
182
184
|
end
|
@@ -263,7 +265,7 @@ Split.configure do |config|
|
|
263
265
|
end
|
264
266
|
```
|
265
267
|
|
266
|
-
|
268
|
+
When using the cookie persistence, Split stores data into an anonymous tracking cookie named 'split', which expires in 1 year. To change that, set the `persistence_cookie_length` in the configuration (unit of time in seconds).
|
267
269
|
|
268
270
|
```ruby
|
269
271
|
Split.configure do |config|
|
@@ -272,6 +274,8 @@ Split.configure do |config|
|
|
272
274
|
end
|
273
275
|
```
|
274
276
|
|
277
|
+
The data stored consists of the experiment name and the variants the user is in. Example: { "experiment_name" => "variant_a" }
|
278
|
+
|
275
279
|
__Note:__ Using cookies depends on `ActionDispatch::Cookies` or any identical API
|
276
280
|
|
277
281
|
#### Redis
|
@@ -360,7 +364,7 @@ end
|
|
360
364
|
|
361
365
|
If you are running `ab_test` from a view, you must define your event
|
362
366
|
hook callback as a
|
363
|
-
[helper_method](
|
367
|
+
[helper_method](https://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper_method)
|
364
368
|
in the controller:
|
365
369
|
|
366
370
|
``` ruby
|
@@ -386,6 +390,8 @@ Split.configure do |config|
|
|
386
390
|
# before experiment reset or deleted
|
387
391
|
config.on_before_experiment_reset = -> (example) { # Do something on reset }
|
388
392
|
config.on_before_experiment_delete = -> (experiment) { # Do something else on delete }
|
393
|
+
# after experiment winner had been set
|
394
|
+
config.on_experiment_winner_choose = -> (experiment) { # Do something on winner choose }
|
389
395
|
end
|
390
396
|
```
|
391
397
|
|
@@ -446,7 +452,7 @@ match "/split" => Split::Dashboard, anchor: false, via: [:get, :post, :delete],
|
|
446
452
|
end
|
447
453
|
```
|
448
454
|
|
449
|
-
More information on this [here](
|
455
|
+
More information on this [here](https://steve.dynedge.co.uk/2011/12/09/controlling-access-to-routes-and-rack-apps-in-rails-3-with-devise-and-warden/)
|
450
456
|
|
451
457
|
### Screenshot
|
452
458
|
|
@@ -556,7 +562,7 @@ and:
|
|
556
562
|
ab_finished(:my_first_experiment)
|
557
563
|
```
|
558
564
|
|
559
|
-
You can also add meta data for each experiment, very useful when you need more than an alternative name to change behaviour:
|
565
|
+
You can also add meta data for each experiment, which is very useful when you need more than an alternative name to change behaviour:
|
560
566
|
|
561
567
|
```ruby
|
562
568
|
Split.configure do |config|
|
@@ -601,6 +607,8 @@ or in views:
|
|
601
607
|
<% end %>
|
602
608
|
```
|
603
609
|
|
610
|
+
The keys used in meta data should be Strings
|
611
|
+
|
604
612
|
#### Metrics
|
605
613
|
|
606
614
|
You might wish to track generic metrics, such as conversions, and use
|
@@ -642,7 +650,7 @@ The API to define goals for an experiment is this:
|
|
642
650
|
ab_test({link_color: ["purchase", "refund"]}, "red", "blue")
|
643
651
|
```
|
644
652
|
|
645
|
-
or you can
|
653
|
+
or you can define them in a configuration file:
|
646
654
|
|
647
655
|
```ruby
|
648
656
|
Split.configure do |config|
|
@@ -750,6 +758,20 @@ split_config = YAML.load_file(Rails.root.join('config', 'split.yml'))
|
|
750
758
|
Split.redis = split_config[Rails.env]
|
751
759
|
```
|
752
760
|
|
761
|
+
### Redis Caching (v4.0+)
|
762
|
+
|
763
|
+
In some high-volume usage scenarios, Redis load can be incurred by repeated
|
764
|
+
fetches for fairly static data. Enabling caching will reduce this load.
|
765
|
+
|
766
|
+
```ruby
|
767
|
+
Split.configuration.cache = true
|
768
|
+
````
|
769
|
+
|
770
|
+
This currently caches:
|
771
|
+
- `Split::ExperimentCatalog.find`
|
772
|
+
- `Split::Experiment.start_time`
|
773
|
+
- `Split::Experiment.winner`
|
774
|
+
|
753
775
|
## Namespaces
|
754
776
|
|
755
777
|
If you're running multiple, separate instances of Split you may want
|
@@ -766,7 +788,7 @@ library. To configure Split to use `Redis::Namespace`, do the following:
|
|
766
788
|
```
|
767
789
|
|
768
790
|
2. Configure `Split.redis` to use a `Redis::Namespace` instance (possible in an
|
769
|
-
|
791
|
+
initializer):
|
770
792
|
|
771
793
|
```ruby
|
772
794
|
redis = Redis.new(url: ENV['REDIS_URL']) # or whatever config you want
|
@@ -824,8 +846,8 @@ end
|
|
824
846
|
|
825
847
|
## Extensions
|
826
848
|
|
827
|
-
- [Split::Export](
|
828
|
-
- [Split::Analytics](
|
849
|
+
- [Split::Export](https://github.com/splitrb/split-export) - Easily export A/B test data out of Split.
|
850
|
+
- [Split::Analytics](https://github.com/splitrb/split-analytics) - Push test data to Google Analytics.
|
829
851
|
- [Split::Mongoid](https://github.com/MongoHQ/split-mongoid) - Store experiment data in mongoid (still uses redis).
|
830
852
|
- [Split::Cacheable](https://github.com/harrystech/split_cacheable) - Automatically create cache buckets per test.
|
831
853
|
- [Split::Counters](https://github.com/bernardkroes/split-counters) - Add counters per experiment and alternative.
|
@@ -837,7 +859,7 @@ Ryan bates has produced an excellent 10 minute screencast about split on the Rai
|
|
837
859
|
|
838
860
|
## Blogposts
|
839
861
|
|
840
|
-
* [Recipe: A/B testing with KISSMetrics and the split gem](
|
862
|
+
* [Recipe: A/B testing with KISSMetrics and the split gem](https://robots.thoughtbot.com/post/9595887299/recipe-a-b-testing-with-kissmetrics-and-the-split-gem)
|
841
863
|
* [Rails A/B testing with Split on Heroku](http://blog.nathanhumbert.com/2012/02/rails-ab-testing-with-split-on-heroku.html)
|
842
864
|
|
843
865
|
## Backers
|
@@ -917,9 +939,9 @@ Please do! Over 70 different people have contributed to the project, you can see
|
|
917
939
|
|
918
940
|
### Development
|
919
941
|
|
920
|
-
The source code is hosted at [GitHub](
|
942
|
+
The source code is hosted at [GitHub](https://github.com/splitrb/split).
|
921
943
|
|
922
|
-
Report issues and feature requests on [GitHub Issues](
|
944
|
+
Report issues and feature requests on [GitHub Issues](https://github.com/splitrb/split/issues).
|
923
945
|
|
924
946
|
You can find a discussion form on [Google Groups](https://groups.google.com/d/forum/split-ruby).
|
925
947
|
|
data/Rakefile
CHANGED
data/gemfiles/6.0.gemfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Split
|
3
4
|
module Algorithms
|
4
5
|
module WeightedSample
|
@@ -8,7 +9,7 @@ module Split
|
|
8
9
|
total = weights.inject(:+)
|
9
10
|
point = rand * total
|
10
11
|
|
11
|
-
experiment.alternatives.zip(weights).each do |n,w|
|
12
|
+
experiment.alternatives.zip(weights).each do |n, w|
|
12
13
|
return n if w >= point
|
13
14
|
point -= w
|
14
15
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# A multi-armed bandit implementation inspired by
|
3
4
|
# @aaronsw and victorykit/whiplash
|
4
|
-
require '
|
5
|
+
require 'rubystats'
|
5
6
|
|
6
7
|
module Split
|
7
8
|
module Algorithms
|
@@ -16,7 +17,7 @@ module Split
|
|
16
17
|
def arm_guess(participants, completions)
|
17
18
|
a = [participants, 0].max
|
18
19
|
b = [participants-completions, 0].max
|
19
|
-
|
20
|
+
Rubystats::BetaDistribution.new(a+fairness_constant, b+fairness_constant).rng
|
20
21
|
end
|
21
22
|
|
22
23
|
def best_guess(alternatives)
|
data/lib/split/alternative.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Split
|
3
4
|
class Alternative
|
4
5
|
attr_accessor :name
|
@@ -15,7 +16,7 @@ module Split
|
|
15
16
|
@name = name
|
16
17
|
@weight = 1
|
17
18
|
end
|
18
|
-
p_winner = 0.0
|
19
|
+
@p_winner = 0.0
|
19
20
|
end
|
20
21
|
|
21
22
|
def to_s
|
@@ -75,7 +76,7 @@ module Split
|
|
75
76
|
return field
|
76
77
|
end
|
77
78
|
|
78
|
-
def set_completed_count
|
79
|
+
def set_completed_count(count, goal = nil)
|
79
80
|
field = set_field(goal)
|
80
81
|
Split.redis.hset(key, field, count.to_i)
|
81
82
|
end
|
@@ -122,7 +123,7 @@ module Split
|
|
122
123
|
# can't calculate zscore for P(x) > 1
|
123
124
|
return 'N/A' if p_a > 1 || p_c > 1
|
124
125
|
|
125
|
-
|
126
|
+
Split::Zscore.calculate(p_a, n_a, p_c, n_c)
|
126
127
|
end
|
127
128
|
|
128
129
|
def extra_info
|
data/lib/split/cache.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Split
|
4
|
+
class Cache
|
5
|
+
|
6
|
+
def self.clear
|
7
|
+
@cache = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.fetch(namespace, key)
|
11
|
+
return yield unless Split.configuration.cache
|
12
|
+
|
13
|
+
@cache ||= {}
|
14
|
+
@cache[namespace] ||= {}
|
15
|
+
|
16
|
+
value = @cache[namespace][key]
|
17
|
+
return value if value
|
18
|
+
|
19
|
+
@cache[namespace][key] = yield
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.clear_key(key)
|
23
|
+
@cache&.keys&.each do |namespace|
|
24
|
+
@cache[namespace]&.delete(key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Split
|
3
4
|
module CombinedExperimentsHelper
|
4
5
|
def ab_combined_test(metric_descriptor, control = nil, *alternatives)
|
@@ -31,7 +32,7 @@ module Split
|
|
31
32
|
raise(Split::InvalidExperimentsFormatError, 'Invalid descriptor class (String or Symbol required)') unless metric_descriptor.class == String || metric_descriptor.class == Symbol
|
32
33
|
raise(Split::InvalidExperimentsFormatError, 'Enable configuration') unless Split.configuration.enabled
|
33
34
|
raise(Split::InvalidExperimentsFormatError, 'Enable `allow_multiple_experiments`') unless Split.configuration.allow_multiple_experiments
|
34
|
-
|
35
|
+
Split::configuration.experiments[metric_descriptor.to_sym]
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|