split 4.0.2 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -1
- data/CHANGELOG.md +23 -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/encapsulated_helper.rb +8 -0
- data/lib/split/experiment.rb +9 -0
- data/lib/split/helper.rb +16 -8
- 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/encapsulated_helper_spec.rb +38 -12
- data/spec/experiment_spec.rb +11 -0
- data/spec/helper_spec.rb +36 -0
- data/spec/persistence/cookie_adapter_spec.rb +3 -3
- data/spec/persistence/redis_adapter_spec.rb +3 -3
- data/spec/redis_interface_spec.rb +10 -0
- data/spec/spec_helper.rb +17 -7
- data/split.gemspec +1 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80d095f07432d336e773b30c3c5a873b554dd682f65ace33886d1a83697d447a
|
4
|
+
data.tar.gz: 9a504a3c9d4c67391528e1640c2c7eac3cfcafe91305cadb901e48541726f04c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68e5919618103f315fa9f4f0d18384392c02c01c680c13d50a77cfa0507bd19a1c01fb5c1db66619329c1ef38013c3ebe90e880f6af5c9bac9ba2d1a76b3963a
|
7
|
+
data.tar.gz: 1ae5fe0187e16b4bb3efc14a0e0ded4df98527fbc530dab4a89c544d133b1a7e8cd0adc6579f0f43ea3c84aa6899a2d123e97c5af105ce158e804a5ac8286728
|
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,26 @@
|
|
1
|
+
# 4.0.4 (March 3rd, 2024)
|
2
|
+
|
3
|
+
Bugfixes:
|
4
|
+
- Better integration for EncapsulatedHelper when needing params/request info (@henrique-ft, #721 and #723)
|
5
|
+
|
6
|
+
Misc:
|
7
|
+
- Make specs compatible with newer Rack versions (@andrehjr, #722)
|
8
|
+
|
9
|
+
# 4.0.3 (November 15th, 2023)
|
10
|
+
|
11
|
+
Bugfixes:
|
12
|
+
- Do not throw error if alternativas have data that can lead to negative numbers for probability calculation (@andrehjr, #703)
|
13
|
+
- Do not persist invalid extra_info on ab_record_extra_info. (@trostli @andrehjr, #717)
|
14
|
+
- CROSSSLOT keys issue fix when using redis cluster (@naveen-chidhambaram, #710)
|
15
|
+
- Convert value to string before saving it in RedisAdapter (@Jealrock, #714)
|
16
|
+
- Fix deprecation warning with Redis 4.8.0 (@martingregoire, #701)
|
17
|
+
|
18
|
+
Misc:
|
19
|
+
- Add matrix as a default dependency (@andrehjr, #705)
|
20
|
+
- Add Ruby 3.2 to Github Actions (@andrehjr, #702)
|
21
|
+
- Update documentation regarding finding users outside a web session (@andrehjr, #716)
|
22
|
+
- Update actions/checkout to v4 (@andrehjr, #718)
|
23
|
+
|
1
24
|
# 4.0.2 (December 2nd, 2022)
|
2
25
|
|
3
26
|
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"
|
@@ -23,6 +23,14 @@ module Split
|
|
23
23
|
@context = context
|
24
24
|
end
|
25
25
|
|
26
|
+
def params
|
27
|
+
request.params if request && request.respond_to?(:params)
|
28
|
+
end
|
29
|
+
|
30
|
+
def request
|
31
|
+
@context.request if @context.respond_to?(:request)
|
32
|
+
end
|
33
|
+
|
26
34
|
def ab_user
|
27
35
|
@ab_user ||= Split::User.new(@context)
|
28
36
|
end
|
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
|
|
@@ -121,11 +121,11 @@ module Split
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def override_alternative_by_params(experiment_name)
|
124
|
-
|
124
|
+
params_present? && params[OVERRIDE_PARAM_NAME] && params[OVERRIDE_PARAM_NAME][experiment_name]
|
125
125
|
end
|
126
126
|
|
127
127
|
def override_alternative_by_cookies(experiment_name)
|
128
|
-
return unless
|
128
|
+
return unless request_present?
|
129
129
|
|
130
130
|
if request.cookies && request.cookies.key?("split_override")
|
131
131
|
experiments = JSON.parse(request.cookies["split_override"]) rescue {}
|
@@ -134,7 +134,7 @@ module Split
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def split_generically_disabled?
|
137
|
-
|
137
|
+
params_present? && params["SPLIT_DISABLE"]
|
138
138
|
end
|
139
139
|
|
140
140
|
def ab_user
|
@@ -142,26 +142,34 @@ module Split
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def exclude_visitor?
|
145
|
-
|
145
|
+
request_present? && (instance_exec(request, &Split.configuration.ignore_filter) || is_ignored_ip_address? || is_robot? || is_preview?)
|
146
146
|
end
|
147
147
|
|
148
148
|
def is_robot?
|
149
|
-
|
149
|
+
request_present? && request.user_agent =~ Split.configuration.robot_regex
|
150
150
|
end
|
151
151
|
|
152
152
|
def is_preview?
|
153
|
-
|
153
|
+
request_present? && defined?(request.headers) && request.headers["x-purpose"] == "preview"
|
154
154
|
end
|
155
155
|
|
156
156
|
def is_ignored_ip_address?
|
157
157
|
return false if Split.configuration.ignore_ip_addresses.empty?
|
158
158
|
|
159
159
|
Split.configuration.ignore_ip_addresses.each do |ip|
|
160
|
-
return true if
|
160
|
+
return true if request_present? && (request.ip == ip || (ip.class == Regexp && request.ip =~ ip))
|
161
161
|
end
|
162
162
|
false
|
163
163
|
end
|
164
164
|
|
165
|
+
def params_present?
|
166
|
+
defined?(params) && params
|
167
|
+
end
|
168
|
+
|
169
|
+
def request_present?
|
170
|
+
defined?(request) && request
|
171
|
+
end
|
172
|
+
|
165
173
|
def active_experiments
|
166
174
|
ab_user.active_experiments
|
167
175
|
end
|
@@ -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
|
@@ -3,11 +3,7 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Split::EncapsulatedHelper do
|
6
|
-
|
7
|
-
|
8
|
-
def params
|
9
|
-
raise NoMethodError, "This method is not really defined"
|
10
|
-
end
|
6
|
+
let(:context_shim) { Split::EncapsulatedHelper::ContextShim.new(double(request: request)) }
|
11
7
|
|
12
8
|
describe "ab_test" do
|
13
9
|
before do
|
@@ -15,20 +11,15 @@ describe Split::EncapsulatedHelper do
|
|
15
11
|
.and_return(mock_user)
|
16
12
|
end
|
17
13
|
|
18
|
-
it "should not raise an error when params raises an error" do
|
19
|
-
expect { params }.to raise_error(NoMethodError)
|
20
|
-
expect { ab_test("link_color", "blue", "red") }.not_to raise_error
|
21
|
-
end
|
22
|
-
|
23
14
|
it "calls the block with selected alternative" do
|
24
|
-
expect { |block| ab_test("link_color", "red", "red", &block) }.to yield_with_args("red", {})
|
15
|
+
expect { |block| context_shim.ab_test("link_color", "red", "red", &block) }.to yield_with_args("red", {})
|
25
16
|
end
|
26
17
|
|
27
18
|
context "inside a view" do
|
28
19
|
it "works inside ERB" do
|
29
20
|
require "erb"
|
30
21
|
template = ERB.new(<<-ERB.split(/\s+/s).map(&:strip).join(" "), nil, "%")
|
31
|
-
foo <% ab_test(:foo, '1', '2') do |alt, meta| %>
|
22
|
+
foo <% context_shim.ab_test(:foo, '1', '2') do |alt, meta| %>
|
32
23
|
static <%= alt %>
|
33
24
|
<% end %>
|
34
25
|
ERB
|
@@ -43,8 +34,43 @@ describe Split::EncapsulatedHelper do
|
|
43
34
|
include Split::EncapsulatedHelper
|
44
35
|
public :session
|
45
36
|
}.new
|
37
|
+
|
46
38
|
expect(ctx).to receive(:session) { {} }
|
47
39
|
expect { ctx.ab_test("link_color", "blue", "red") }.not_to raise_error
|
48
40
|
end
|
41
|
+
|
42
|
+
context "when request is defined in context of ContextShim" do
|
43
|
+
context "when overriding by params" do
|
44
|
+
it do
|
45
|
+
ctx = Class.new {
|
46
|
+
public :session
|
47
|
+
def request
|
48
|
+
build_request(params: {
|
49
|
+
"ab_test" => { "link_color" => "blue" }
|
50
|
+
})
|
51
|
+
end
|
52
|
+
}.new
|
53
|
+
|
54
|
+
context_shim = Split::EncapsulatedHelper::ContextShim.new(ctx)
|
55
|
+
expect(context_shim.ab_test("link_color", "blue", "red")).to be("blue")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when overriding by cookies" do
|
60
|
+
it do
|
61
|
+
ctx = Class.new {
|
62
|
+
public :session
|
63
|
+
def request
|
64
|
+
build_request(cookies: {
|
65
|
+
"split_override" => '{ "link_color": "red" }'
|
66
|
+
})
|
67
|
+
end
|
68
|
+
}.new
|
69
|
+
|
70
|
+
context_shim = Split::EncapsulatedHelper::ContextShim.new(ctx)
|
71
|
+
expect(context_shim.ab_test("link_color", "blue", "red")).to be("red")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
49
75
|
end
|
50
76
|
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")
|
@@ -67,14 +67,14 @@ describe Split::Persistence::CookieAdapter do
|
|
67
67
|
it "puts multiple experiments in a single cookie" do
|
68
68
|
subject["foo"] = "FOO"
|
69
69
|
subject["bar"] = "BAR"
|
70
|
-
expect(context.response.headers["Set-Cookie"]).to
|
70
|
+
expect(Array(context.response.headers["Set-Cookie"])).to include(/\Asplit=%7B%22foo%22%3A%22FOO%22%2C%22bar%22%3A%22BAR%22%7D; path=\/; expires=[a-zA-Z]{3}, \d{2} [a-zA-Z]{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z]{3}\Z/)
|
71
71
|
end
|
72
72
|
|
73
73
|
it "ensure other added cookies are not overriden" do
|
74
74
|
context.response.set_cookie "dummy", "wow"
|
75
75
|
subject["foo"] = "FOO"
|
76
|
-
expect(context.response.headers["Set-Cookie"]).to include(
|
77
|
-
expect(context.response.headers["Set-Cookie"]).to include(
|
76
|
+
expect(Array(context.response.headers["Set-Cookie"])).to include(/dummy=wow/)
|
77
|
+
expect(Array(context.response.headers["Set-Cookie"])).to include(/split=/)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -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
@@ -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
|
|
@@ -43,11 +44,20 @@ def params
|
|
43
44
|
@params ||= {}
|
44
45
|
end
|
45
46
|
|
46
|
-
def request
|
47
|
-
@request ||=
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
def request
|
48
|
+
@request ||= build_request
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_request(
|
52
|
+
ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
|
53
|
+
ip: "192.168.1.1",
|
54
|
+
params: {},
|
55
|
+
cookies: {}
|
56
|
+
)
|
57
|
+
r = OpenStruct.new
|
58
|
+
r.user_agent = ua
|
59
|
+
r.ip = ip
|
60
|
+
r.params = params
|
61
|
+
r.cookies = cookies
|
62
|
+
r
|
53
63
|
end
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Nesbitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-03 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
|
@@ -275,7 +289,7 @@ 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
|
292
|
+
rubygems_version: 3.5.3
|
279
293
|
signing_key:
|
280
294
|
specification_version: 4
|
281
295
|
summary: Rack based split testing framework
|