verdict 0.14.0 → 0.16.1
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 +39 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +0 -1
- data/dev.yml +11 -0
- data/lib/verdict/experiment.rb +25 -11
- data/lib/verdict/storage/redis_storage.rb +38 -9
- data/lib/verdict/version.rb +1 -1
- data/test/experiment_test.rb +49 -0
- data/test/storage/cookie_storage_test.rb +3 -1
- data/test/storage/redis_storage_test.rb +90 -48
- data/test/test_helper.rb +1 -0
- data/verdict.gemspec +1 -0
- metadata +18 -3
- data/.travis.yml +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6d20d61b4b74ae3cdc111f872f18c6bcedf9165bed983dfc489d826edad4e99
|
4
|
+
data.tar.gz: 5a23b9b6b678b0e7c7ccdd8c4df24f6029ee6f5c8a3ba16e9850147974b0a490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c931d20970cb9a75a8f9780d6648493dbf6ca2a5a9eb61f79f35896063b603b36f611cc1e6175710075d0271afad9f73efd795b09eb55c63848dc67def3a68c
|
7
|
+
data.tar.gz: '0599822d437c5e7e512a0a75e64312aaf97008d0ae31b75bacab6bdc8361a0750b22f9e14f31d70c7b41df771c20267a2719e883a0394b161e1d4367901252d7'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby:
|
11
|
+
- 2.5
|
12
|
+
- 2.6
|
13
|
+
- 2.7
|
14
|
+
- head
|
15
|
+
- jruby
|
16
|
+
services:
|
17
|
+
redis:
|
18
|
+
image: redis
|
19
|
+
options: >-
|
20
|
+
--health-cmd "redis-cli ping"
|
21
|
+
--health-interval 10s
|
22
|
+
--health-timeout 5s
|
23
|
+
--health-retries 5
|
24
|
+
ports:
|
25
|
+
- 6379:6379
|
26
|
+
name: Tests Ruby ${{ matrix.ruby }}
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v1
|
29
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
30
|
+
uses: ruby/setup-ruby@v1
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby }}
|
33
|
+
- name: Run tests
|
34
|
+
run: |
|
35
|
+
gem install bundler
|
36
|
+
bundle install --jobs 4 --retry 3
|
37
|
+
bundle exec rake
|
38
|
+
env:
|
39
|
+
REDIS_HOST: localhost
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## v0.16.1
|
2
|
+
* Change `RedisStorage` scrub to be iterative to avoid SystemStackError while cleaning big experiments
|
3
|
+
|
4
|
+
## v0.16.0
|
5
|
+
* Allow configuring the `RedisStorage` with a [`ConnectionPool`](https://github.com/mperham/connection_pool) instead of a raw `Redis` connection.
|
6
|
+
|
7
|
+
## v0.15.2
|
8
|
+
* Fix edge case where inheriting from `Verdict::Experiment` and overriding `subject_qualifies?` resulted in an error.
|
9
|
+
|
10
|
+
## v0.15.1
|
11
|
+
* Make the `dynamic_qualifies` parameter in `Verdict::Experiment#subject_qualifies?` optional. This fixes a bug where users that were previously calling this method directly experienced issues after v0.15.0
|
12
|
+
|
13
|
+
## v0.15.0
|
14
|
+
* Add optional `qualifiers` parameter to the `Verdict::Experiment#switch` method. This parameter accepts an array of procs and is used as additional qualifiers. The purpose of this parameter is to allow users to define qualification logic outside of the experiment definition.
|
15
|
+
|
1
16
|
## v0.14.0
|
2
17
|
* Add optional experiment definition method `schedule_stop_new_assignment_timestamp` to support limiting experiment's assignment lifetime with another pre-determined time interval. It allows users to have an assignment cooldown period for stable analysis of the experiment results. Experiment's lifetime now becomes: start experiment -> stop new assignments -> end experiment.
|
3
18
|
|
data/Gemfile
CHANGED
data/dev.yml
ADDED
data/lib/verdict/experiment.rb
CHANGED
@@ -142,18 +142,18 @@ class Verdict::Experiment
|
|
142
142
|
raise unless disqualify_empty_identifier?
|
143
143
|
end
|
144
144
|
|
145
|
-
def assign(subject, context = nil)
|
145
|
+
def assign(subject, context = nil, dynamic_qualifiers: [])
|
146
146
|
previous_assignment = lookup(subject)
|
147
147
|
|
148
148
|
subject_identifier = retrieve_subject_identifier(subject)
|
149
149
|
assignment = if previous_assignment
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
150
|
+
previous_assignment
|
151
|
+
elsif dynamic_subject_qualifies?(subject, dynamic_qualifiers, context) && is_make_new_assignments?
|
152
|
+
group = segmenter.assign(subject_identifier, subject, context)
|
153
|
+
subject_assignment(subject, group, nil, group.nil?)
|
154
|
+
else
|
155
|
+
nil_assignment(subject)
|
156
|
+
end
|
157
157
|
|
158
158
|
store_assignment(assignment)
|
159
159
|
rescue Verdict::StorageError
|
@@ -194,9 +194,11 @@ class Verdict::Experiment
|
|
194
194
|
@storage.remove_assignment(self, subject)
|
195
195
|
end
|
196
196
|
|
197
|
-
|
197
|
+
# The qualifiers param accepts an array of procs.
|
198
|
+
# This is intended for qualification logic that cannot be defined in the experiment definition
|
199
|
+
def switch(subject, context = nil, qualifiers: [])
|
198
200
|
return unless is_scheduled?
|
199
|
-
assign(subject, context).to_sym
|
201
|
+
assign(subject, context, dynamic_qualifiers: qualifiers).to_sym
|
200
202
|
end
|
201
203
|
|
202
204
|
def lookup(subject)
|
@@ -241,8 +243,9 @@ class Verdict::Experiment
|
|
241
243
|
@disqualify_empty_identifier
|
242
244
|
end
|
243
245
|
|
244
|
-
def subject_qualifies?(subject, context = nil)
|
246
|
+
def subject_qualifies?(subject, context = nil, dynamic_qualifiers: [])
|
245
247
|
ensure_experiment_has_started
|
248
|
+
return false unless dynamic_qualifiers.all? { |qualifier| qualifier.call(subject) }
|
246
249
|
everybody_qualifies? || @qualifiers.all? { |qualifier| qualifier.call(subject, context) }
|
247
250
|
end
|
248
251
|
|
@@ -292,4 +295,15 @@ class Verdict::Experiment
|
|
292
295
|
def is_make_new_assignments?
|
293
296
|
return !(@schedule_stop_new_assignment_timestamp && @schedule_stop_new_assignment_timestamp <= Time.now)
|
294
297
|
end
|
298
|
+
|
299
|
+
# Used when a Experiment class has overridden the subject_qualifies? method prior to v0.15.0
|
300
|
+
# The previous version of subject_qualifies did not accept dynamic qualifiers, thus this is used to
|
301
|
+
# determine how many parameters to pass
|
302
|
+
def dynamic_subject_qualifies?(subject, dynamic_qualifiers, context)
|
303
|
+
if method(:subject_qualifies?).parameters.include?([:key, :dynamic_qualifiers])
|
304
|
+
subject_qualifies?(subject, context, dynamic_qualifiers: dynamic_qualifiers)
|
305
|
+
else
|
306
|
+
subject_qualifies?(subject, context)
|
307
|
+
end
|
308
|
+
end
|
295
309
|
end
|
@@ -6,33 +6,56 @@ module Verdict
|
|
6
6
|
attr_accessor :redis, :key_prefix
|
7
7
|
|
8
8
|
def initialize(redis = nil, options = {})
|
9
|
-
|
9
|
+
if !redis.nil? && !redis.respond_to?(:with)
|
10
|
+
@redis = ConnectionPoolLike.new(redis)
|
11
|
+
else
|
12
|
+
@redis = redis
|
13
|
+
end
|
14
|
+
|
10
15
|
@key_prefix = options[:key_prefix] || 'experiments/'
|
11
16
|
end
|
12
17
|
|
13
18
|
protected
|
14
19
|
|
20
|
+
class ConnectionPoolLike
|
21
|
+
def initialize(redis)
|
22
|
+
@redis = redis
|
23
|
+
end
|
24
|
+
|
25
|
+
def with
|
26
|
+
yield @redis
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
15
30
|
def get(scope, key)
|
16
|
-
redis.
|
31
|
+
redis.with do |conn|
|
32
|
+
conn.hget(scope_key(scope), key)
|
33
|
+
end
|
17
34
|
rescue ::Redis::BaseError => e
|
18
35
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
19
36
|
end
|
20
37
|
|
21
38
|
def set(scope, key, value)
|
22
|
-
redis.
|
39
|
+
redis.with do |conn|
|
40
|
+
conn.hset(scope_key(scope), key, value)
|
41
|
+
end
|
23
42
|
rescue ::Redis::BaseError => e
|
24
43
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
25
44
|
end
|
26
45
|
|
27
46
|
def remove(scope, key)
|
28
|
-
redis.
|
47
|
+
redis.with do |conn|
|
48
|
+
conn.hdel(scope_key(scope), key)
|
49
|
+
end
|
29
50
|
rescue ::Redis::BaseError => e
|
30
51
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
31
52
|
end
|
32
53
|
|
33
54
|
def clear(scope, options)
|
34
55
|
scrub(scope)
|
35
|
-
redis.
|
56
|
+
redis.with do |conn|
|
57
|
+
conn.del(scope_key(scope))
|
58
|
+
end
|
36
59
|
rescue ::Redis::BaseError => e
|
37
60
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
38
61
|
end
|
@@ -44,11 +67,17 @@ module Verdict
|
|
44
67
|
end
|
45
68
|
|
46
69
|
def scrub(scope, cursor: 0)
|
47
|
-
|
48
|
-
|
49
|
-
|
70
|
+
loop do
|
71
|
+
cursor, results = redis.with do |conn|
|
72
|
+
conn.hscan(scope_key(scope), cursor, count: PAGE_SIZE)
|
73
|
+
end
|
74
|
+
|
75
|
+
results.map(&:first).each do |key|
|
76
|
+
remove(scope, key)
|
77
|
+
end
|
78
|
+
|
79
|
+
break if cursor.to_i.zero?
|
50
80
|
end
|
51
|
-
scrub(scope, cursor: cursor) unless cursor.to_i.zero?
|
52
81
|
end
|
53
82
|
end
|
54
83
|
end
|
data/lib/verdict/version.rb
CHANGED
data/test/experiment_test.rb
CHANGED
@@ -641,9 +641,58 @@ class ExperimentTest < Minitest::Test
|
|
641
641
|
assert_nil e.switch(1)
|
642
642
|
end
|
643
643
|
end
|
644
|
+
|
645
|
+
def test_custom_qualifiers_success
|
646
|
+
e = Verdict::Experiment.new('test') do
|
647
|
+
groups do
|
648
|
+
group :all, 100
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
subject = 2
|
653
|
+
custom_qualifier_a = Proc.new { |subject| subject.even? }
|
654
|
+
custom_qualifier_b = Proc.new { |subject| subject > 0 }
|
655
|
+
|
656
|
+
group = e.switch(subject, qualifiers: [custom_qualifier_a, custom_qualifier_b])
|
657
|
+
assert_equal e.group(:all).to_sym, group
|
658
|
+
end
|
659
|
+
|
660
|
+
def test_custom_qualifiers_failure
|
661
|
+
e = Verdict::Experiment.new('test') do
|
662
|
+
groups do
|
663
|
+
group :all, 100
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
subject = 3
|
668
|
+
custom_qualifier_a = Proc.new { |subject| subject.even? }
|
669
|
+
custom_qualifier_b = Proc.new { |subject| subject > 0 }
|
670
|
+
|
671
|
+
group = e.switch(subject, qualifiers: [custom_qualifier_a, custom_qualifier_b])
|
672
|
+
assert_nil group
|
673
|
+
end
|
674
|
+
|
675
|
+
def test_dynamic_subject_qualifies_call_overridden_method
|
676
|
+
e = MyExperiment.new('test') do
|
677
|
+
groups do
|
678
|
+
group :all, 100
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
group = e.switch(4)
|
683
|
+
assert_nil group
|
684
|
+
end
|
685
|
+
|
644
686
|
private
|
645
687
|
|
646
688
|
def redis
|
647
689
|
@redis ||= ::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
648
690
|
end
|
649
691
|
end
|
692
|
+
|
693
|
+
class MyExperiment < Verdict::Experiment
|
694
|
+
def subject_qualifies?(subject, context = nil)
|
695
|
+
return false if subject.even?
|
696
|
+
super
|
697
|
+
end
|
698
|
+
end
|
@@ -6,7 +6,9 @@ require 'fake_app'
|
|
6
6
|
class CookieStorageTest < Minitest::Test
|
7
7
|
def setup
|
8
8
|
@storage = Verdict::Storage::CookieStorage.new.tap do |s|
|
9
|
-
|
9
|
+
request = mock()
|
10
|
+
request.stubs(:cookies_same_site_protection).returns(proc { :none })
|
11
|
+
s.cookies = ActionDispatch::Cookies::CookieJar.new(request)
|
10
12
|
end
|
11
13
|
@experiment = Verdict::Experiment.new(:cookie_storage_test) do
|
12
14
|
groups { group :all, 100 }
|
@@ -1,97 +1,75 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class RedisStorageTest < Minitest::Test
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
@storage = storage = Verdict::Storage::RedisStorage.new(@redis)
|
8
|
-
@experiment = Verdict::Experiment.new(:redis_storage) do
|
9
|
-
qualify { |s| s == 'subject_1' }
|
10
|
-
groups { group :all, 100 }
|
11
|
-
storage storage, store_unqualified: true
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def teardown
|
16
|
-
@redis.del('experiments/redis_storage')
|
17
|
-
end
|
4
|
+
module SharedRedisStorageTests
|
5
|
+
attr_reader :redis, :experiment, :storage
|
18
6
|
|
19
7
|
def test_store_and_retrieve_qualified_assignment
|
20
|
-
refute
|
8
|
+
refute redis.hexists(experiment_key, 'assignment_subject_1')
|
21
9
|
|
22
|
-
new_assignment =
|
10
|
+
new_assignment = experiment.assign('subject_1')
|
23
11
|
assert new_assignment.qualified?
|
24
12
|
refute new_assignment.returning?
|
25
13
|
|
26
|
-
assert
|
14
|
+
assert redis.hexists(experiment_key, 'assignment_subject_1')
|
27
15
|
|
28
|
-
returning_assignment =
|
16
|
+
returning_assignment = experiment.assign('subject_1')
|
29
17
|
assert returning_assignment.returning?
|
30
18
|
assert_equal new_assignment.experiment, returning_assignment.experiment
|
31
19
|
assert_equal new_assignment.group, returning_assignment.group
|
32
20
|
end
|
33
21
|
|
34
22
|
def test_store_and_retrieve_unqualified_assignment
|
35
|
-
refute
|
23
|
+
refute redis.hexists(experiment_key, 'assignment_subject_2')
|
36
24
|
|
37
|
-
new_assignment =
|
25
|
+
new_assignment = experiment.assign('subject_2')
|
38
26
|
|
39
27
|
refute new_assignment.returning?
|
40
28
|
refute new_assignment.qualified?
|
41
|
-
assert
|
29
|
+
assert redis.hexists(experiment_key, 'assignment_subject_2')
|
42
30
|
|
43
|
-
returning_assignment =
|
31
|
+
returning_assignment = experiment.assign('subject_2')
|
44
32
|
assert returning_assignment.returning?
|
45
33
|
assert_equal new_assignment.experiment, returning_assignment.experiment
|
46
34
|
assert_nil new_assignment.group
|
47
35
|
assert_nil returning_assignment.group
|
48
36
|
end
|
49
37
|
|
50
|
-
def test_assign_should_return_unqualified_when_redis_is_unavailable_for_reads
|
51
|
-
@redis.stubs(:hget).raises(::Redis::BaseError, "Redis is down")
|
52
|
-
assert !@experiment.assign('subject_1').qualified?
|
53
|
-
end
|
54
|
-
|
55
|
-
def test_assign_should_return_unqualified_when_redis_is_unavailable_for_writes
|
56
|
-
@redis.stubs(:hset).raises(::Redis::BaseError, "Redis is down")
|
57
|
-
assert !@experiment.assign('subject_1').qualified?
|
58
|
-
end
|
59
|
-
|
60
38
|
def test_remove_subject_assignment
|
61
|
-
|
62
|
-
assert
|
63
|
-
|
64
|
-
refute
|
39
|
+
experiment.assign('subject_3')
|
40
|
+
assert redis.hexists(experiment_key, 'assignment_subject_3')
|
41
|
+
experiment.remove_subject_assignment('subject_3')
|
42
|
+
refute redis.hexists(experiment_key, 'assignment_subject_3')
|
65
43
|
end
|
66
44
|
|
67
45
|
def test_started_at
|
68
|
-
refute
|
69
|
-
a =
|
70
|
-
assert
|
71
|
-
assert_equal a,
|
46
|
+
refute redis.hexists(experiment_key, "started_at")
|
47
|
+
a = experiment.send(:ensure_experiment_has_started)
|
48
|
+
assert redis.hexists(experiment_key, "started_at")
|
49
|
+
assert_equal a, experiment.started_at
|
72
50
|
end
|
73
51
|
|
74
52
|
def test_cleanup_with_scope_argument
|
75
53
|
1000.times do |n|
|
76
|
-
|
54
|
+
experiment.assign("something_#{n}")
|
77
55
|
end
|
78
56
|
|
79
|
-
assert_operator
|
57
|
+
assert_operator redis, :exists, experiment_key
|
80
58
|
|
81
59
|
Verdict.default_logger.expects(:warn).with(regexp_matches(/deprecated/))
|
82
|
-
|
83
|
-
refute_operator
|
60
|
+
storage.cleanup(:redis_storage)
|
61
|
+
refute_operator redis, :exists, experiment_key
|
84
62
|
end
|
85
63
|
|
86
64
|
def test_cleanup
|
87
65
|
1000.times do |n|
|
88
|
-
|
66
|
+
experiment.assign("something_#{n}")
|
89
67
|
end
|
90
68
|
|
91
|
-
assert_operator
|
69
|
+
assert_operator redis, :exists, experiment_key
|
92
70
|
|
93
|
-
|
94
|
-
refute_operator
|
71
|
+
storage.cleanup(experiment)
|
72
|
+
refute_operator redis, :exists, experiment_key
|
95
73
|
end
|
96
74
|
|
97
75
|
private
|
@@ -100,3 +78,67 @@ class RedisStorageTest < Minitest::Test
|
|
100
78
|
"experiments/redis_storage"
|
101
79
|
end
|
102
80
|
end
|
81
|
+
|
82
|
+
class RedisStorageTest < Minitest::Test
|
83
|
+
include SharedRedisStorageTests
|
84
|
+
|
85
|
+
def setup
|
86
|
+
@redis = ::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
87
|
+
@storage = storage = Verdict::Storage::RedisStorage.new(@redis)
|
88
|
+
@experiment = Verdict::Experiment.new(:redis_storage) do
|
89
|
+
qualify { |s| s == 'subject_1' }
|
90
|
+
groups { group :all, 100 }
|
91
|
+
storage storage, store_unqualified: true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def teardown
|
96
|
+
@redis.del('experiments/redis_storage')
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_assign_should_return_unqualified_when_redis_is_unavailable_for_reads
|
100
|
+
redis.stubs(:hget).raises(::Redis::BaseError, "Redis is down")
|
101
|
+
assert !experiment.assign('subject_1').qualified?
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_assign_should_return_unqualified_when_redis_is_unavailable_for_writes
|
105
|
+
redis.stubs(:hset).raises(::Redis::BaseError, "Redis is down")
|
106
|
+
assert !experiment.assign('subject_1').qualified?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class RedisStorageConnectionPoolTest < Minitest::Test
|
111
|
+
include SharedRedisStorageTests
|
112
|
+
|
113
|
+
def setup
|
114
|
+
@redis = ::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
115
|
+
@redis_pool = ::ConnectionPool.new(size: 3) do
|
116
|
+
::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
117
|
+
end
|
118
|
+
@storage = storage = Verdict::Storage::RedisStorage.new(@redis_pool)
|
119
|
+
@experiment = Verdict::Experiment.new(:redis_storage) do
|
120
|
+
qualify { |s| s == 'subject_1' }
|
121
|
+
groups { group :all, 100 }
|
122
|
+
storage storage, store_unqualified: true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def teardown
|
127
|
+
@redis.del('experiments/redis_storage')
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_assign_should_return_unqualified_when_redis_is_unavailable_for_writes
|
131
|
+
redis_mock = stub
|
132
|
+
redis_mock.stubs(:hget).returns(nil)
|
133
|
+
redis_mock.stubs(:hset).raises(::Redis::BaseError, "Redis is down")
|
134
|
+
@redis_pool.stubs(:with).yields(redis_mock)
|
135
|
+
assert !@experiment.assign('subject_1').qualified?
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_remove_subject_assignment
|
139
|
+
@experiment.assign('subject_3')
|
140
|
+
assert @redis.hexists(experiment_key, 'assignment_subject_3')
|
141
|
+
@experiment.remove_subject_assignment('subject_3')
|
142
|
+
refute @redis.hexists(experiment_key, 'assignment_subject_3')
|
143
|
+
end
|
144
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -13,6 +13,7 @@ require "mocha/setup"
|
|
13
13
|
require "timecop"
|
14
14
|
require "verdict"
|
15
15
|
require "redis"
|
16
|
+
require "connection_pool"
|
16
17
|
|
17
18
|
REDIS_HOST = ENV['REDIS_HOST'].nil? ? '127.0.0.1' : ENV['REDIS_HOST']
|
18
19
|
REDIS_PORT = (ENV['REDIS_PORT'].nil? ? '6379' : ENV['REDIS_PORT']).to_i
|
data/verdict.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verdict
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: connection_pool
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Shopify Experiments classes
|
98
112
|
email:
|
99
113
|
- kevin.mcphillips@shopify.com
|
@@ -103,14 +117,15 @@ extensions: []
|
|
103
117
|
extra_rdoc_files: []
|
104
118
|
files:
|
105
119
|
- ".github/probots.yml"
|
120
|
+
- ".github/workflows/ci.yml"
|
106
121
|
- ".gitignore"
|
107
|
-
- ".travis.yml"
|
108
122
|
- CHANGELOG.md
|
109
123
|
- CONTRIBUTING.md
|
110
124
|
- Gemfile
|
111
125
|
- LICENSE
|
112
126
|
- README.md
|
113
127
|
- Rakefile
|
128
|
+
- dev.yml
|
114
129
|
- docs/concepts.md
|
115
130
|
- lib/verdict.rb
|
116
131
|
- lib/verdict/assignment.rb
|
data/.travis.yml
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
before_install: gem update bundler
|
4
|
-
script: bundle exec rake
|
5
|
-
rvm:
|
6
|
-
- 2.0
|
7
|
-
- 2.1
|
8
|
-
- 2.3.3
|
9
|
-
- ruby-head
|
10
|
-
- jruby
|
11
|
-
matrix:
|
12
|
-
allow_failures:
|
13
|
-
- rvm: ruby-head
|
14
|
-
- rvm: jruby
|
15
|
-
services:
|
16
|
-
- redis-server
|