verdict 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/verdict/experiment.rb +14 -6
- data/lib/verdict/version.rb +1 -1
- data/test/experiment_test.rb +81 -14
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1929eb3266514c557ec595e7fa7f80065a1b600
|
4
|
+
data.tar.gz: 1f34b0ad26c876f348649ad04d96a9f0c077ea48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63801e6ec63ae74dcb33c418090fec977a4598e23da4ff6bc642e2041025915f5ea8eda536ff7c5415dfd18106d1dc1c1fe339b6901b26663b70bfcad4d8803f
|
7
|
+
data.tar.gz: 982e62386331ba13174d3e758a9624ba76b80e45e30ba59f788c65a239585a74abd6386f79622e0dd7314328c0e7843852a01bb6a4b4f7597f6b1d3618f99644
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## v0.7.0
|
2
|
+
**This version has breaking changes**
|
3
|
+
|
4
|
+
* Experiment can now specify multiple qualify blocks
|
5
|
+
* `Verdict::Experiment#qualifier` has been removed in favor for `Verdict::Experiment#qualifiers`, which returns an array of procs
|
6
|
+
* Allow pass of an argument to qualify with a method name as a symbol, instead of a block
|
7
|
+
|
1
8
|
## v0.6.3
|
2
9
|
|
3
10
|
* Fix bug were Verdict.directory is overwritten
|
data/lib/verdict/experiment.rb
CHANGED
@@ -2,7 +2,7 @@ class Verdict::Experiment
|
|
2
2
|
|
3
3
|
include Verdict::Metadata
|
4
4
|
|
5
|
-
attr_reader :handle, :
|
5
|
+
attr_reader :handle, :qualifiers, :storage, :event_logger
|
6
6
|
|
7
7
|
def self.define(handle, *args, &block)
|
8
8
|
experiment = self.new(handle, *args, &block)
|
@@ -14,7 +14,7 @@ class Verdict::Experiment
|
|
14
14
|
@handle = handle.to_s
|
15
15
|
|
16
16
|
options = default_options.merge(options)
|
17
|
-
@
|
17
|
+
@qualifiers = Array(options[:qualifier] || options[:qualifiers])
|
18
18
|
@event_logger = options[:event_logger] || Verdict::EventLogger.new(Verdict.default_logger)
|
19
19
|
@storage = storage(options[:storage] || :memory)
|
20
20
|
@store_unqualified = options[:store_unqualified]
|
@@ -58,8 +58,16 @@ class Verdict::Experiment
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def qualify(&block)
|
62
|
-
|
61
|
+
def qualify(method_name = nil, &block)
|
62
|
+
if block_given?
|
63
|
+
@qualifiers << block
|
64
|
+
elsif method_name.nil?
|
65
|
+
raise ArgumentError, "no method nor blocked passed!"
|
66
|
+
elsif respond_to?(method_name, true)
|
67
|
+
@qualifiers << method(method_name).to_proc
|
68
|
+
else
|
69
|
+
raise ArgumentError, "No helper for #{method_name.inspect}"
|
70
|
+
end
|
63
71
|
end
|
64
72
|
|
65
73
|
def storage(storage = nil, options = {})
|
@@ -169,7 +177,7 @@ class Verdict::Experiment
|
|
169
177
|
end
|
170
178
|
|
171
179
|
def has_qualifier?
|
172
|
-
|
180
|
+
@qualifiers.any?
|
173
181
|
end
|
174
182
|
|
175
183
|
def everybody_qualifies?
|
@@ -204,7 +212,7 @@ class Verdict::Experiment
|
|
204
212
|
|
205
213
|
def subject_qualifies?(subject, context = nil)
|
206
214
|
ensure_experiment_has_started
|
207
|
-
everybody_qualifies? || @qualifier.call(subject, context)
|
215
|
+
everybody_qualifies? || @qualifiers.all? { |qualifier| qualifier.call(subject, context) }
|
208
216
|
end
|
209
217
|
|
210
218
|
protected
|
data/lib/verdict/version.rb
CHANGED
data/test/experiment_test.rb
CHANGED
@@ -5,7 +5,7 @@ class ExperimentTest < Minitest::Test
|
|
5
5
|
|
6
6
|
def test_no_qualifier
|
7
7
|
e = Verdict::Experiment.new('test')
|
8
|
-
|
8
|
+
refute e.has_qualifier?
|
9
9
|
assert e.everybody_qualifies?
|
10
10
|
end
|
11
11
|
|
@@ -18,14 +18,14 @@ class ExperimentTest < Minitest::Test
|
|
18
18
|
end
|
19
19
|
|
20
20
|
assert e.has_qualifier?
|
21
|
-
|
21
|
+
refute e.everybody_qualifies?
|
22
22
|
|
23
23
|
subject_stub = Struct.new(:id, :country)
|
24
24
|
ca_subject = subject_stub.new(1, 'CA')
|
25
25
|
us_subject = subject_stub.new(1, 'US')
|
26
26
|
|
27
|
-
assert e.
|
28
|
-
|
27
|
+
assert e.qualifiers.all? { |q| q.call(ca_subject) }
|
28
|
+
refute e.qualifiers.all? { |q| q.call(us_subject) }
|
29
29
|
|
30
30
|
qualified = e.assign(ca_subject)
|
31
31
|
assert_kind_of Verdict::Assignment, qualified
|
@@ -33,7 +33,74 @@ class ExperimentTest < Minitest::Test
|
|
33
33
|
|
34
34
|
non_qualified = e.assign(us_subject)
|
35
35
|
assert_kind_of Verdict::Assignment, non_qualified
|
36
|
-
|
36
|
+
refute non_qualified.qualified?
|
37
|
+
assert_equal nil, non_qualified.group
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_multiple_qualifier
|
41
|
+
e = Verdict::Experiment.new('test') do |experiment|
|
42
|
+
qualify { |subject| subject.language == 'fr' }
|
43
|
+
qualify { |subject| subject.country == 'CA' }
|
44
|
+
|
45
|
+
groups do
|
46
|
+
group :all, 100
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
assert e.has_qualifier?
|
51
|
+
refute e.everybody_qualifies?
|
52
|
+
|
53
|
+
subject_stub = Struct.new(:id, :country, :language)
|
54
|
+
fr_subject = subject_stub.new(1, 'CA', 'fr')
|
55
|
+
en_subject = subject_stub.new(2, 'CA', 'en')
|
56
|
+
|
57
|
+
assert e.qualifiers.all? { |q| q.call(fr_subject) }
|
58
|
+
refute e.qualifiers.all? { |q| q.call(en_subject) }
|
59
|
+
|
60
|
+
qualified = e.assign(fr_subject)
|
61
|
+
assert_kind_of Verdict::Assignment, qualified
|
62
|
+
assert_equal e.group(:all), qualified.group
|
63
|
+
|
64
|
+
non_qualified = e.assign(en_subject)
|
65
|
+
assert_kind_of Verdict::Assignment, non_qualified
|
66
|
+
refute non_qualified.qualified?
|
67
|
+
assert_equal nil, non_qualified.group
|
68
|
+
end
|
69
|
+
|
70
|
+
module CountryIsCanadaHelper
|
71
|
+
def country_is_canada(subject, _context)
|
72
|
+
subject.country == 'CA'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def test_method_qualifier
|
76
|
+
|
77
|
+
e = Verdict::Experiment.new('test') do |experiment|
|
78
|
+
extend CountryIsCanadaHelper
|
79
|
+
|
80
|
+
qualify :country_is_canada
|
81
|
+
|
82
|
+
groups do
|
83
|
+
group :all, 100
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
assert e.has_qualifier?
|
88
|
+
refute e.everybody_qualifies?
|
89
|
+
|
90
|
+
subject_stub = Struct.new(:id, :country)
|
91
|
+
ca_subject = subject_stub.new(1, 'CA')
|
92
|
+
us_subject = subject_stub.new(1, 'US')
|
93
|
+
|
94
|
+
assert e.qualifiers.all? { |q| q.call(ca_subject, nil) }
|
95
|
+
refute e.qualifiers.all? { |q| q.call(us_subject, nil) }
|
96
|
+
|
97
|
+
qualified = e.assign(ca_subject)
|
98
|
+
assert_kind_of Verdict::Assignment, qualified
|
99
|
+
assert_equal e.group(:all), qualified.group
|
100
|
+
|
101
|
+
non_qualified = e.assign(us_subject)
|
102
|
+
assert_kind_of Verdict::Assignment, non_qualified
|
103
|
+
refute non_qualified.qualified?
|
37
104
|
assert_equal nil, non_qualified.group
|
38
105
|
end
|
39
106
|
|
@@ -45,7 +112,7 @@ class ExperimentTest < Minitest::Test
|
|
45
112
|
end
|
46
113
|
end
|
47
114
|
|
48
|
-
|
115
|
+
refute e.assign(nil).qualified?
|
49
116
|
assert_equal nil, e.convert('', :mygoal)
|
50
117
|
end
|
51
118
|
|
@@ -63,12 +130,12 @@ class ExperimentTest < Minitest::Test
|
|
63
130
|
assignment = e.assign(1)
|
64
131
|
assert_kind_of Verdict::Assignment, assignment
|
65
132
|
assert assignment.qualified?
|
66
|
-
|
133
|
+
refute assignment.returning?
|
67
134
|
assert_equal assignment.group, e.group(:a)
|
68
135
|
|
69
136
|
assignment = e.assign(3)
|
70
137
|
assert_kind_of Verdict::Assignment, assignment
|
71
|
-
|
138
|
+
refute assignment.qualified?
|
72
139
|
|
73
140
|
assert_equal :a, e.switch(1)
|
74
141
|
assert_equal :b, e.switch(2)
|
@@ -175,7 +242,7 @@ class ExperimentTest < Minitest::Test
|
|
175
242
|
original_assignment = e.assign(subject)
|
176
243
|
assert original_assignment.qualified?
|
177
244
|
new_assignment = e.disqualify_manually(subject)
|
178
|
-
|
245
|
+
refute new_assignment.qualified?
|
179
246
|
end
|
180
247
|
|
181
248
|
def test_disqualify_manually_fails_with_store_unqualified_disabled
|
@@ -212,7 +279,7 @@ class ExperimentTest < Minitest::Test
|
|
212
279
|
mock_store.expects(:store_assignment).never
|
213
280
|
|
214
281
|
assignment = e.assign(mock('subject'))
|
215
|
-
|
282
|
+
refute assignment.qualified?
|
216
283
|
end
|
217
284
|
|
218
285
|
def test_assignment_event_logging
|
@@ -273,7 +340,7 @@ class ExperimentTest < Minitest::Test
|
|
273
340
|
|
274
341
|
storage_mock.stubs(:retrieve_assignment).raises(Verdict::StorageError, 'storage read issues')
|
275
342
|
rescued_assignment = e.assign(stub(id: 123))
|
276
|
-
|
343
|
+
refute rescued_assignment.qualified?
|
277
344
|
end
|
278
345
|
|
279
346
|
def test_storage_write_failure
|
@@ -286,7 +353,7 @@ class ExperimentTest < Minitest::Test
|
|
286
353
|
storage_mock.expects(:retrieve_assignment).returns(e.subject_assignment(mock('subject_identifier'), e.group(:all), nil))
|
287
354
|
storage_mock.expects(:store_assignment).raises(Verdict::StorageError, 'storage write issues')
|
288
355
|
rescued_assignment = e.assign(stub(id: 456))
|
289
|
-
|
356
|
+
refute rescued_assignment.qualified?
|
290
357
|
end
|
291
358
|
|
292
359
|
def test_initial_started_at
|
@@ -328,7 +395,7 @@ class ExperimentTest < Minitest::Test
|
|
328
395
|
end
|
329
396
|
|
330
397
|
subject = stub(id: 'old', created_at: Time.new(2011))
|
331
|
-
|
398
|
+
refute e.assign(subject).qualified?
|
332
399
|
|
333
400
|
subject = stub(id: 'new', created_at: Time.new(2013))
|
334
401
|
assert e.assign(subject).qualified?
|
@@ -340,7 +407,7 @@ class ExperimentTest < Minitest::Test
|
|
340
407
|
groups { group :all, 100 }
|
341
408
|
end
|
342
409
|
|
343
|
-
|
410
|
+
refute e.started?, "The experiment should not have started yet"
|
344
411
|
|
345
412
|
e.assign(stub(id: '123'))
|
346
413
|
assert e.started?, "The experiment should have started after the first assignment"
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|