split 4.0.1 → 4.0.2
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 +6 -3
- data/.rubocop.yml +2 -5
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +2 -1
- data/README.md +4 -2
- data/Rakefile +4 -5
- data/gemfiles/5.2.gemfile +1 -3
- data/gemfiles/6.0.gemfile +1 -3
- data/gemfiles/6.1.gemfile +1 -3
- data/gemfiles/7.0.gemfile +2 -3
- data/lib/split/algorithms/block_randomization.rb +5 -6
- data/lib/split/algorithms/whiplash.rb +16 -18
- data/lib/split/algorithms.rb +22 -0
- data/lib/split/alternative.rb +21 -22
- data/lib/split/cache.rb +0 -1
- data/lib/split/combined_experiments_helper.rb +4 -4
- data/lib/split/configuration.rb +83 -84
- data/lib/split/dashboard/helpers.rb +6 -7
- data/lib/split/dashboard/pagination_helpers.rb +53 -54
- data/lib/split/dashboard/public/style.css +5 -2
- data/lib/split/dashboard/views/index.erb +19 -4
- data/lib/split/dashboard.rb +29 -23
- data/lib/split/encapsulated_helper.rb +4 -6
- data/lib/split/experiment.rb +84 -88
- data/lib/split/experiment_catalog.rb +6 -5
- data/lib/split/extensions/string.rb +1 -1
- data/lib/split/goals_collection.rb +8 -10
- data/lib/split/helper.rb +19 -19
- data/lib/split/metric.rb +4 -5
- data/lib/split/persistence/cookie_adapter.rb +44 -47
- data/lib/split/persistence/dual_adapter.rb +7 -8
- data/lib/split/persistence/redis_adapter.rb +2 -3
- data/lib/split/persistence/session_adapter.rb +0 -2
- data/lib/split/persistence.rb +4 -4
- data/lib/split/redis_interface.rb +1 -2
- data/lib/split/trial.rb +23 -24
- data/lib/split/user.rb +12 -13
- data/lib/split/version.rb +1 -1
- data/lib/split/zscore.rb +1 -3
- data/lib/split.rb +26 -25
- data/spec/algorithms/block_randomization_spec.rb +6 -5
- data/spec/algorithms/weighted_sample_spec.rb +6 -5
- data/spec/algorithms/whiplash_spec.rb +4 -5
- data/spec/alternative_spec.rb +35 -36
- data/spec/cache_spec.rb +15 -19
- data/spec/combined_experiments_helper_spec.rb +18 -17
- data/spec/configuration_spec.rb +32 -38
- data/spec/dashboard/pagination_helpers_spec.rb +69 -67
- data/spec/dashboard/paginator_spec.rb +10 -9
- data/spec/dashboard_helpers_spec.rb +19 -18
- data/spec/dashboard_spec.rb +67 -35
- data/spec/encapsulated_helper_spec.rb +12 -14
- data/spec/experiment_catalog_spec.rb +14 -13
- data/spec/experiment_spec.rb +121 -123
- data/spec/goals_collection_spec.rb +17 -15
- data/spec/helper_spec.rb +379 -382
- data/spec/metric_spec.rb +14 -14
- data/spec/persistence/cookie_adapter_spec.rb +23 -8
- data/spec/persistence/dual_adapter_spec.rb +71 -71
- data/spec/persistence/redis_adapter_spec.rb +25 -26
- data/spec/persistence/session_adapter_spec.rb +2 -3
- data/spec/persistence_spec.rb +1 -2
- data/spec/redis_interface_spec.rb +16 -14
- data/spec/spec_helper.rb +15 -13
- data/spec/split_spec.rb +11 -11
- data/spec/support/cookies_mock.rb +1 -2
- data/spec/trial_spec.rb +61 -60
- data/spec/user_spec.rb +36 -36
- data/split.gemspec +20 -20
- metadata +7 -10
- data/.rubocop_todo.yml +0 -226
- data/Appraisals +0 -19
- data/gemfiles/5.0.gemfile +0 -9
- data/gemfiles/5.1.gemfile +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1c97063d4d1ccf4c2cd5dfd2e83b98b3f55a934b9dfa9b5862ede3ee5b8c58c
|
4
|
+
data.tar.gz: 28e30003a6d2059baf91482f5ac9a97b0b7d2e37c61a425710cbf1adf21a4a83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 988f115f0f96870188221552cca45f6a7207f548500dbf2a5446abaa47ac7365571644a5a231443ca7616182b68c3139f8d62fdcf18d1593fca8898092a332e2
|
7
|
+
data.tar.gz: b6a668159d8b6fe9529bae5bc650f120b9de1bdf593bc89d0d949a2e07e8cfda7c96b4c12e5e4615696cf31b845215f95ce60b4abcb9ff3d7ef056669a4ae51d
|
data/.github/workflows/ci.yml
CHANGED
@@ -34,8 +34,8 @@ jobs:
|
|
34
34
|
- gemfile: 7.0.gemfile
|
35
35
|
ruby: '3.0'
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
- gemfile: 7.0.gemfile
|
38
|
+
ruby: '3.1'
|
39
39
|
|
40
40
|
|
41
41
|
runs-on: ubuntu-latest
|
@@ -51,7 +51,7 @@ jobs:
|
|
51
51
|
--health-retries 5
|
52
52
|
|
53
53
|
steps:
|
54
|
-
- uses: actions/checkout@
|
54
|
+
- uses: actions/checkout@v3
|
55
55
|
|
56
56
|
- uses: ruby/setup-ruby@v1
|
57
57
|
with:
|
@@ -69,3 +69,6 @@ jobs:
|
|
69
69
|
run: bundle exec rspec
|
70
70
|
env:
|
71
71
|
REDIS_URL: redis:6379
|
72
|
+
|
73
|
+
- name: Rubocop
|
74
|
+
run: bundle exec rubocop
|
data/.rubocop.yml
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
1
|
AllCops:
|
4
2
|
TargetRubyVersion: 2.5
|
5
3
|
DisabledByDefault: true
|
4
|
+
SuggestExtensions: false
|
6
5
|
Exclude:
|
7
|
-
- 'Appraisals'
|
8
6
|
- 'gemfiles/**/*'
|
9
|
-
- 'spec/**/*.rb'
|
10
7
|
|
11
8
|
Style/AndOr:
|
12
9
|
Enabled: true
|
@@ -114,7 +111,7 @@ Layout/SpaceInsideParens:
|
|
114
111
|
Enabled: true
|
115
112
|
|
116
113
|
Style/StringLiterals:
|
117
|
-
Enabled:
|
114
|
+
Enabled: true
|
118
115
|
EnforcedStyle: double_quotes
|
119
116
|
|
120
117
|
Layout/IndentationStyle:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
# 4.0.2 (December 2nd, 2022)
|
2
|
+
|
3
|
+
Bugfixes:
|
4
|
+
- Stop crashing on non-hash json (@knarewski, #697)
|
5
|
+
- Handle when Rails is partially loaded as a Gem (@TSMMark, #687)
|
6
|
+
|
7
|
+
Features:
|
8
|
+
- Add support for redis-client, which does not automatically cast types to strings (@knarewski, #696)
|
9
|
+
- Add ability to initialize experiments (@robin-phung, #673)
|
10
|
+
|
11
|
+
Misc:
|
12
|
+
- Fix default branch name and gem metadata indentation (@ursm, #693)
|
13
|
+
- Update actions/checkout to v3 (@andrehjr, #683)
|
14
|
+
- Enforce double quotes (@andrehjr, #682)
|
15
|
+
- Fix Rubocop Style/* Offenses (@andrehjr, #681)
|
16
|
+
- Enable rubocop on Github Actions (@andrehjr, #680)
|
17
|
+
- Fix all Layout issues on the project (@andrehjr, #679)
|
18
|
+
- Fix Style/HashSyntax offenses (@andrehjr, #678)
|
19
|
+
- Remove usage of deprecated implicit block expectation from specs (@andrehjr, #677)
|
20
|
+
- Remove appraisals configuration (@andrehjr, #676)
|
21
|
+
- Add Ruby 3.1 (@andrehjr, #675)
|
22
|
+
- Encapsulate Split::Algorithms at our own module to avoid explicit calling rubystats everywhere (@andrehjr, #674)
|
23
|
+
|
1
24
|
## 4.0.1 (December 30th, 2021)
|
2
25
|
|
3
26
|
Bugfixes:
|
data/CONTRIBUTING.md
CHANGED
@@ -25,7 +25,7 @@ Want to contribute to Split? That's great! Here are a couple of guidelines that
|
|
25
25
|
|
26
26
|
## Setup instructions
|
27
27
|
|
28
|
-
You can find in-depth instructions to install in our [README](https://github.com/splitrb/split/blob/
|
28
|
+
You can find in-depth instructions to install in our [README](https://github.com/splitrb/split/blob/main/README.md).
|
29
29
|
|
30
30
|
*Note*: Split requires Ruby 1.9.2 or higher.
|
31
31
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -19,11 +19,13 @@ Split is designed to be hacker friendly, allowing for maximum customisation and
|
|
19
19
|
|
20
20
|
### Requirements
|
21
21
|
|
22
|
-
Split
|
22
|
+
Split v4.0+ is currently tested with Ruby >= 2.5 and Rails >= 5.2.
|
23
|
+
|
24
|
+
If your project requires compatibility with Ruby 2.4.x or older Rails versions. You can try v3.0 or v0.8.0(for Ruby 1.9.3)
|
23
25
|
|
24
26
|
Split uses Redis as a datastore.
|
25
27
|
|
26
|
-
Split only supports Redis
|
28
|
+
Split only supports Redis 4.0 or greater.
|
27
29
|
|
28
30
|
If you're on OS X, Homebrew is the simplest way to install Redis:
|
29
31
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require 'appraisal'
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require "rspec/core/rake_task"
|
7
6
|
|
8
|
-
RSpec::Core::RakeTask.new(
|
7
|
+
RSpec::Core::RakeTask.new("spec")
|
9
8
|
|
10
|
-
task :
|
9
|
+
task default: :spec
|
data/gemfiles/5.2.gemfile
CHANGED
data/gemfiles/6.0.gemfile
CHANGED
data/gemfiles/6.1.gemfile
CHANGED
data/gemfiles/7.0.gemfile
CHANGED
@@ -12,12 +12,11 @@ module Split
|
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
15
|
+
def minimum_participant_alternatives(alternatives)
|
16
|
+
alternatives_by_count = alternatives.group_by(&:participant_count)
|
17
|
+
min_group = alternatives_by_count.min_by { |k, v| k }
|
18
|
+
min_group.last
|
19
|
+
end
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
# A multi-armed bandit implementation inspired by
|
4
4
|
# @aaronsw and victorykit/whiplash
|
5
|
-
require 'rubystats'
|
6
5
|
|
7
6
|
module Split
|
8
7
|
module Algorithms
|
@@ -13,26 +12,25 @@ module Split
|
|
13
12
|
end
|
14
13
|
|
15
14
|
private
|
15
|
+
def arm_guess(participants, completions)
|
16
|
+
a = [participants, 0].max
|
17
|
+
b = [participants-completions, 0].max
|
18
|
+
Split::Algorithms.beta_distribution_rng(a + fairness_constant, b + fairness_constant)
|
19
|
+
end
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
alternatives.each do |alternative|
|
26
|
-
guesses[alternative.name] = arm_guess(alternative.participant_count, alternative.all_completed_count)
|
21
|
+
def best_guess(alternatives)
|
22
|
+
guesses = {}
|
23
|
+
alternatives.each do |alternative|
|
24
|
+
guesses[alternative.name] = arm_guess(alternative.participant_count, alternative.all_completed_count)
|
25
|
+
end
|
26
|
+
gmax = guesses.values.max
|
27
|
+
best = guesses.keys.select { |name| guesses[name] == gmax }
|
28
|
+
best.sample
|
27
29
|
end
|
28
|
-
gmax = guesses.values.max
|
29
|
-
best = guesses.keys.select { |name| guesses[name] == gmax }
|
30
|
-
best.sample
|
31
|
-
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
def fairness_constant
|
32
|
+
7
|
33
|
+
end
|
36
34
|
end
|
37
35
|
end
|
38
36
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
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
|
+
|
12
|
+
require "rubystats"
|
13
|
+
|
14
|
+
module Split
|
15
|
+
module Algorithms
|
16
|
+
class << self
|
17
|
+
def beta_distribution_rng(a, b)
|
18
|
+
Rubystats::BetaDistribution.new(a, b).rng
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/split/alternative.rb
CHANGED
@@ -38,11 +38,11 @@ module Split
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def participant_count
|
41
|
-
Split.redis.hget(key,
|
41
|
+
Split.redis.hget(key, "participant_count").to_i
|
42
42
|
end
|
43
43
|
|
44
44
|
def participant_count=(count)
|
45
|
-
Split.redis.hset(key,
|
45
|
+
Split.redis.hset(key, "participant_count", count.to_i)
|
46
46
|
end
|
47
47
|
|
48
48
|
def completed_count(goal = nil)
|
@@ -67,13 +67,13 @@ module Split
|
|
67
67
|
def set_field(goal)
|
68
68
|
field = "completed_count"
|
69
69
|
field += ":" + goal unless goal.nil?
|
70
|
-
|
70
|
+
field
|
71
71
|
end
|
72
72
|
|
73
73
|
def set_prob_field(goal)
|
74
74
|
field = "p_winner"
|
75
75
|
field += ":" + goal unless goal.nil?
|
76
|
-
|
76
|
+
field
|
77
77
|
end
|
78
78
|
|
79
79
|
def set_completed_count(count, goal = nil)
|
@@ -82,7 +82,7 @@ module Split
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def increment_participation
|
85
|
-
Split.redis.hincrby key,
|
85
|
+
Split.redis.hincrby key, "participant_count", 1
|
86
86
|
end
|
87
87
|
|
88
88
|
def increment_completion(goal = nil)
|
@@ -112,7 +112,7 @@ module Split
|
|
112
112
|
control = experiment.control
|
113
113
|
alternative = self
|
114
114
|
|
115
|
-
return
|
115
|
+
return "N/A" if control.name == alternative.name
|
116
116
|
|
117
117
|
p_a = alternative.conversion_rate(goal)
|
118
118
|
p_c = control.conversion_rate(goal)
|
@@ -121,13 +121,13 @@ module Split
|
|
121
121
|
n_c = control.participant_count
|
122
122
|
|
123
123
|
# can't calculate zscore for P(x) > 1
|
124
|
-
return
|
124
|
+
return "N/A" if p_a > 1 || p_c > 1
|
125
125
|
|
126
126
|
Split::Zscore.calculate(p_a, n_a, p_c, n_c)
|
127
127
|
end
|
128
128
|
|
129
129
|
def extra_info
|
130
|
-
data = Split.redis.hget(key,
|
130
|
+
data = Split.redis.hget(key, "recorded_info")
|
131
131
|
if data && data.length > 1
|
132
132
|
begin
|
133
133
|
JSON.parse(data)
|
@@ -149,24 +149,24 @@ module Split
|
|
149
149
|
@recorded_info[k] = value
|
150
150
|
end
|
151
151
|
|
152
|
-
Split.redis.hset key,
|
152
|
+
Split.redis.hset key, "recorded_info", (@recorded_info || {}).to_json
|
153
153
|
end
|
154
154
|
|
155
155
|
def save
|
156
|
-
Split.redis.hsetnx key,
|
157
|
-
Split.redis.hsetnx key,
|
158
|
-
Split.redis.hsetnx key,
|
159
|
-
Split.redis.hsetnx key,
|
156
|
+
Split.redis.hsetnx key, "participant_count", 0
|
157
|
+
Split.redis.hsetnx key, "completed_count", 0
|
158
|
+
Split.redis.hsetnx key, "p_winner", p_winner
|
159
|
+
Split.redis.hsetnx key, "recorded_info", (@recorded_info || {}).to_json
|
160
160
|
end
|
161
161
|
|
162
162
|
def validate!
|
163
163
|
unless String === @name || hash_with_correct_values?(@name)
|
164
|
-
raise ArgumentError,
|
164
|
+
raise ArgumentError, "Alternative must be a string"
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
168
|
def reset
|
169
|
-
Split.redis.hmset key,
|
169
|
+
Split.redis.hmset key, "participant_count", 0, "completed_count", 0, "recorded_info", ""
|
170
170
|
unless goals.empty?
|
171
171
|
goals.each do |g|
|
172
172
|
field = "completed_count:#{g}"
|
@@ -180,13 +180,12 @@ module Split
|
|
180
180
|
end
|
181
181
|
|
182
182
|
private
|
183
|
+
def hash_with_correct_values?(name)
|
184
|
+
Hash === name && String === name.keys.first && Float(name.values.first) rescue false
|
185
|
+
end
|
183
186
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
def key
|
189
|
-
"#{experiment_name}:#{name}"
|
190
|
-
end
|
187
|
+
def key
|
188
|
+
"#{experiment_name}:#{name}"
|
189
|
+
end
|
191
190
|
end
|
192
191
|
end
|
data/lib/split/cache.rb
CHANGED
@@ -29,10 +29,10 @@ module Split
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def find_combined_experiment(metric_descriptor)
|
32
|
-
raise(Split::InvalidExperimentsFormatError,
|
33
|
-
raise(Split::InvalidExperimentsFormatError,
|
34
|
-
raise(Split::InvalidExperimentsFormatError,
|
35
|
-
Split
|
32
|
+
raise(Split::InvalidExperimentsFormatError, "Invalid descriptor class (String or Symbol required)") unless metric_descriptor.class == String || metric_descriptor.class == Symbol
|
33
|
+
raise(Split::InvalidExperimentsFormatError, "Enable configuration") unless Split.configuration.enabled
|
34
|
+
raise(Split::InvalidExperimentsFormatError, "Enable `allow_multiple_experiments`") unless Split.configuration.allow_multiple_experiments
|
35
|
+
Split.configuration.experiments[metric_descriptor.to_sym]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/split/configuration.rb
CHANGED
@@ -39,83 +39,83 @@ module Split
|
|
39
39
|
def bots
|
40
40
|
@bots ||= {
|
41
41
|
# Indexers
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
42
|
+
"AdsBot-Google" => "Google Adwords",
|
43
|
+
"Baidu" => "Chinese search engine",
|
44
|
+
"Baiduspider" => "Chinese search engine",
|
45
|
+
"bingbot" => "Microsoft bing bot",
|
46
|
+
"Butterfly" => "Topsy Labs",
|
47
|
+
"Gigabot" => "Gigabot spider",
|
48
|
+
"Googlebot" => "Google spider",
|
49
|
+
"MJ12bot" => "Majestic-12 spider",
|
50
|
+
"msnbot" => "Microsoft bot",
|
51
|
+
"rogerbot" => "SeoMoz spider",
|
52
|
+
"PaperLiBot" => "PaperLi is another content curation service",
|
53
|
+
"Slurp" => "Yahoo spider",
|
54
|
+
"Sogou" => "Chinese search engine",
|
55
|
+
"spider" => "generic web spider",
|
56
|
+
"UnwindFetchor" => "Gnip crawler",
|
57
|
+
"WordPress" => "WordPress spider",
|
58
|
+
"YandexAccessibilityBot" => "Yandex accessibility spider",
|
59
|
+
"YandexBot" => "Yandex spider",
|
60
|
+
"YandexMobileBot" => "Yandex mobile spider",
|
61
|
+
"ZIBB" => "ZIBB spider",
|
62
62
|
|
63
63
|
# HTTP libraries
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
64
|
+
"Apache-HttpClient" => "Java http library",
|
65
|
+
"AppEngine-Google" => "Google App Engine",
|
66
|
+
"curl" => "curl unix CLI http client",
|
67
|
+
"ColdFusion" => "ColdFusion http library",
|
68
|
+
"EventMachine HttpClient" => "Ruby http library",
|
69
|
+
"Go http package" => "Go http library",
|
70
|
+
"Go-http-client" => "Go http library",
|
71
|
+
"Java" => "Generic Java http library",
|
72
|
+
"libwww-perl" => "Perl client-server library loved by script kids",
|
73
|
+
"lwp-trivial" => "Another Perl library loved by script kids",
|
74
|
+
"Python-urllib" => "Python http library",
|
75
|
+
"PycURL" => "Python http library",
|
76
|
+
"Test Certificate Info" => "C http library?",
|
77
|
+
"Typhoeus" => "Ruby http library",
|
78
|
+
"Wget" => "wget unix CLI http client",
|
79
79
|
|
80
80
|
# URL expanders / previewers
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
81
|
+
"awe.sm" => "Awe.sm URL expander",
|
82
|
+
"bitlybot" => "bit.ly bot",
|
83
|
+
"bot@linkfluence.net" => "Linkfluence bot",
|
84
|
+
"facebookexternalhit" => "facebook bot",
|
85
|
+
"Facebot" => "Facebook crawler",
|
86
|
+
"Feedfetcher-Google" => "Google Feedfetcher",
|
87
|
+
"https://developers.google.com/+/web/snippet" => "Google+ Snippet Fetcher",
|
88
|
+
"LinkedInBot" => "LinkedIn bot",
|
89
|
+
"LongURL" => "URL expander service",
|
90
|
+
"NING" => "NING - Yet Another Twitter Swarmer",
|
91
|
+
"Pinterestbot" => "Pinterest Bot",
|
92
|
+
"redditbot" => "Reddit Bot",
|
93
|
+
"ShortLinkTranslate" => "Link shortener",
|
94
|
+
"Slackbot" => "Slackbot link expander",
|
95
|
+
"TweetmemeBot" => "TweetMeMe Crawler",
|
96
|
+
"Twitterbot" => "Twitter URL expander",
|
97
|
+
"UnwindFetch" => "Gnip URL expander",
|
98
|
+
"vkShare" => "VKontake Sharer",
|
99
99
|
|
100
100
|
# Uptime monitoring
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
101
|
+
"check_http" => "Nagios monitor",
|
102
|
+
"GoogleStackdriverMonitoring" => "Google Cloud monitor",
|
103
|
+
"NewRelicPinger" => "NewRelic monitor",
|
104
|
+
"Panopta" => "Monitoring service",
|
105
|
+
"Pingdom" => "Pingdom monitoring",
|
106
|
+
"SiteUptime" => "Site monitoring services",
|
107
|
+
"UptimeRobot" => "Monitoring service",
|
108
108
|
|
109
109
|
# ???
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
"DigitalPersona Fingerprint Software" => "HP Fingerprint scanner",
|
111
|
+
"ShowyouBot" => "Showyou iOS app spider",
|
112
|
+
"ZyBorg" => "Zyborg? Hmmm....",
|
113
|
+
"ELB-HealthChecker" => "ELB Health Check"
|
114
114
|
}
|
115
115
|
end
|
116
116
|
|
117
|
-
def experiments=
|
118
|
-
raise InvalidExperimentsFormatError.new(
|
117
|
+
def experiments=(experiments)
|
118
|
+
raise InvalidExperimentsFormatError.new("Experiments must be a Hash") unless experiments.respond_to?(:keys)
|
119
119
|
@experiments = experiments
|
120
120
|
end
|
121
121
|
|
@@ -157,8 +157,8 @@ module Split
|
|
157
157
|
|
158
158
|
@experiments.each do |experiment_name, settings|
|
159
159
|
alternatives = if (alts = value_for(settings, :alternatives))
|
160
|
-
|
161
|
-
|
160
|
+
normalize_alternatives(alts)
|
161
|
+
end
|
162
162
|
|
163
163
|
experiment_data = {
|
164
164
|
alternatives: alternatives,
|
@@ -213,14 +213,14 @@ module Split
|
|
213
213
|
|
214
214
|
def initialize
|
215
215
|
@ignore_ip_addresses = []
|
216
|
-
@ignore_filter = proc{ |request| is_robot? || is_ignored_ip_address? }
|
216
|
+
@ignore_filter = proc { |request| is_robot? || is_ignored_ip_address? }
|
217
217
|
@db_failover = false
|
218
|
-
@db_failover_on_db_error = proc{|error|} # e.g. use Rails logger here
|
219
|
-
@on_experiment_reset = proc{|experiment|}
|
220
|
-
@on_experiment_delete = proc{|experiment|}
|
221
|
-
@on_before_experiment_reset = proc{|experiment|}
|
222
|
-
@on_before_experiment_delete = proc{|experiment|}
|
223
|
-
@on_experiment_winner_choose = proc{|experiment|}
|
218
|
+
@db_failover_on_db_error = proc { |error| } # e.g. use Rails logger here
|
219
|
+
@on_experiment_reset = proc { |experiment| }
|
220
|
+
@on_experiment_delete = proc { |experiment| }
|
221
|
+
@on_before_experiment_reset = proc { |experiment| }
|
222
|
+
@on_before_experiment_delete = proc { |experiment| }
|
223
|
+
@on_experiment_winner_choose = proc { |experiment| }
|
224
224
|
@db_failover_allow_parameter_override = false
|
225
225
|
@allow_multiple_experiments = false
|
226
226
|
@enabled = true
|
@@ -232,20 +232,19 @@ module Split
|
|
232
232
|
@include_rails_helper = true
|
233
233
|
@beta_probability_simulations = 10000
|
234
234
|
@winning_alternative_recalculation_interval = 60 * 60 * 24 # 1 day
|
235
|
-
@redis = ENV.fetch(ENV.fetch(
|
235
|
+
@redis = ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), "redis://localhost:6379")
|
236
236
|
@dashboard_pagination_default_per_page = 10
|
237
237
|
end
|
238
238
|
|
239
239
|
private
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
240
|
+
def value_for(hash, key)
|
241
|
+
if hash.kind_of?(Hash)
|
242
|
+
hash.has_key?(key.to_s) ? hash[key.to_s] : hash[key.to_sym]
|
243
|
+
end
|
244
244
|
end
|
245
|
-
end
|
246
245
|
|
247
|
-
|
248
|
-
|
249
|
-
|
246
|
+
def escaped_bots
|
247
|
+
bots.map { |key, _| Regexp.escape(key) }
|
248
|
+
end
|
250
249
|
end
|
251
250
|
end
|