split 4.0.2 → 4.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|