verdict 0.2.0 → 0.2.1
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/.travis.yml +1 -0
- data/Gemfile +4 -0
- data/README.md +56 -33
- data/lib/verdict.rb +2 -1
- data/lib/verdict/experiment.rb +15 -7
- data/lib/verdict/segmenters.rb +4 -0
- data/lib/verdict/segmenters/base_segmenter.rb +84 -0
- data/lib/verdict/segmenters/fixed_percentage_segmenter.rb +56 -0
- data/lib/verdict/segmenters/rollout_segmenter.rb +55 -0
- data/lib/verdict/segmenters/static_segmenter.rb +27 -0
- data/lib/verdict/storage.rb +3 -138
- data/lib/verdict/storage/memory_storage.rb +40 -0
- data/lib/verdict/storage/mock_storage.rb +38 -0
- data/lib/verdict/storage/redis_storage.rb +62 -0
- data/lib/verdict/tasks.rake +8 -8
- data/lib/verdict/version.rb +1 -1
- data/test/assignment_test.rb +4 -4
- data/test/conversion_test.rb +1 -1
- data/test/event_logger_test.rb +1 -1
- data/test/experiment_test.rb +12 -3
- data/test/experiments_repository_test.rb +1 -1
- data/test/group_test.rb +2 -2
- data/test/metadata_test.rb +1 -1
- data/test/{fixed_percentage_segmenter_test.rb → segmenters/fixed_percentage_segmenter_test.rb} +11 -11
- data/test/{rollout_segmenter_test.rb → segmenters/rollout_segmenter_test.rb} +2 -2
- data/test/{static_segmenter_test.rb → segmenters/static_segmenter_test.rb} +2 -2
- data/test/{memory_subject_storage_test.rb → storage/memory_subject_storage_test.rb} +1 -1
- data/test/{redis_subject_storage_test.rb → storage/redis_subject_storage_test.rb} +2 -2
- data/test/test_helper.rb +8 -0
- data/verdict.gemspec +6 -6
- metadata +24 -19
- data/lib/verdict/fixed_percentage_segmenter.rb +0 -52
- data/lib/verdict/rollout_segmenter.rb +0 -53
- data/lib/verdict/segmenter.rb +0 -87
- data/lib/verdict/static_segmenter.rb +0 -24
data/test/metadata_test.rb
CHANGED
data/test/{fixed_percentage_segmenter_test.rb → segmenters/fixed_percentage_segmenter_test.rb}
RENAMED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class FixedPercentageSegmenterTest <
|
3
|
+
class FixedPercentageSegmenterTest < Minitest::Test
|
4
4
|
|
5
5
|
def test_add_up_to_100_percent
|
6
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
6
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
7
7
|
s.group :segment1, 1
|
8
8
|
s.group :segment2, 54
|
9
9
|
s.group :segment3, 27
|
@@ -18,7 +18,7 @@ class FixedPercentageSegmenterTest < MiniTest::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_definition_ofhalf_and_rest
|
21
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
21
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
22
22
|
s.group :first_half, :half
|
23
23
|
s.group :second_half, :rest
|
24
24
|
s.verify!
|
@@ -30,7 +30,7 @@ class FixedPercentageSegmenterTest < MiniTest::Unit::TestCase
|
|
30
30
|
|
31
31
|
def test_raises_if_less_than_100_percent
|
32
32
|
assert_raises(Verdict::SegmentationError) do
|
33
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
33
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
34
34
|
s.group :too_little, 99
|
35
35
|
s.verify!
|
36
36
|
end
|
@@ -38,34 +38,34 @@ class FixedPercentageSegmenterTest < MiniTest::Unit::TestCase
|
|
38
38
|
|
39
39
|
def test_raises_if_greather_than_100_percent
|
40
40
|
assert_raises(Verdict::SegmentationError) do
|
41
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
41
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
42
42
|
s.group :too_much, 101
|
43
43
|
s.verify!
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
def test_consistent_assignment_for_subjects
|
48
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
48
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
49
49
|
s.group :first_half, :half
|
50
50
|
s.group :second_half, :rest
|
51
51
|
s.verify!
|
52
52
|
|
53
|
-
3.times do
|
53
|
+
3.times do
|
54
54
|
assert s.groups['first_half'] === s.assign(1, nil, nil)
|
55
55
|
assert s.groups['second_half'] === s.assign(2, nil, nil)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_fair_segmenting
|
60
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
60
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
61
61
|
s.group :first_third, 33
|
62
62
|
s.group :second_third, 33
|
63
63
|
s.group :final_third, :rest
|
64
64
|
s.verify!
|
65
65
|
|
66
66
|
assignments = { :first_third => 0, :second_third => 0, :final_third => 0 }
|
67
|
-
200.times do |n|
|
68
|
-
assignment = s.assign(n, nil, nil)
|
67
|
+
200.times do |n|
|
68
|
+
assignment = s.assign(n, nil, nil)
|
69
69
|
assignments[assignment.to_sym] += 1
|
70
70
|
end
|
71
71
|
|
@@ -76,7 +76,7 @@ class FixedPercentageSegmenterTest < MiniTest::Unit::TestCase
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def test_group_json_export
|
79
|
-
s = Verdict::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
79
|
+
s = Verdict::Segmenters::FixedPercentageSegmenter.new(Verdict::Experiment.new('test'))
|
80
80
|
s.group :first_third, 33
|
81
81
|
s.group :rest, :rest
|
82
82
|
s.verify!
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class RolloutPercentageSegmenterTest <
|
3
|
+
class RolloutPercentageSegmenterTest < Minitest::Test
|
4
4
|
|
5
5
|
def setup
|
6
6
|
@experiment = Verdict::Experiment.new('test') do
|
@@ -11,7 +11,7 @@ class RolloutPercentageSegmenterTest < MiniTest::Unit::TestCase
|
|
11
11
|
def test_assignment
|
12
12
|
included_subject = stub(id: 1)
|
13
13
|
excluded_subject = stub(id: 2)
|
14
|
-
|
14
|
+
|
15
15
|
included_assignment = @experiment.assign(included_subject)
|
16
16
|
assert included_assignment.qualified?
|
17
17
|
assert included_assignment.permanent?
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class StaticSegmenterTest <
|
3
|
+
class StaticSegmenterTest < Minitest::Test
|
4
4
|
|
5
5
|
def setup
|
6
|
-
@segmenter = Verdict::StaticSegmenter.new(Verdict::Experiment.new('test'))
|
6
|
+
@segmenter = Verdict::Segmenters::StaticSegmenter.new(Verdict::Experiment.new('test'))
|
7
7
|
@segmenter.group :beta, ['id1', 'id2']
|
8
8
|
end
|
9
9
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class RedisSubjectStorageTest <
|
3
|
+
class RedisSubjectStorageTest < Minitest::Test
|
4
4
|
|
5
5
|
def setup
|
6
6
|
@redis = ::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
@@ -80,7 +80,7 @@ class RedisSubjectStorageTest < MiniTest::Unit::TestCase
|
|
80
80
|
|
81
81
|
def test_started_at
|
82
82
|
key = @storage.send(:generate_experiment_start_timestamp_key, @experiment)
|
83
|
-
|
83
|
+
|
84
84
|
assert !@redis.exists(key)
|
85
85
|
a = @experiment.send(:ensure_experiment_has_started)
|
86
86
|
assert @redis.exists(key)
|
data/test/test_helper.rb
CHANGED
data/verdict.gemspec
CHANGED
@@ -16,10 +16,10 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
-
|
20
|
-
gem.add_development_dependency
|
21
|
-
gem.add_development_dependency
|
22
|
-
gem.add_development_dependency
|
23
|
-
gem.add_development_dependency
|
24
|
-
gem.add_development_dependency
|
19
|
+
|
20
|
+
gem.add_development_dependency("minitest", '~> 5.2')
|
21
|
+
gem.add_development_dependency("rake")
|
22
|
+
gem.add_development_dependency("mocha")
|
23
|
+
gem.add_development_dependency("timecop")
|
24
|
+
gem.add_development_dependency("redis")
|
25
25
|
end
|
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.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,14 +99,18 @@ files:
|
|
99
99
|
- lib/verdict/conversion.rb
|
100
100
|
- lib/verdict/event_logger.rb
|
101
101
|
- lib/verdict/experiment.rb
|
102
|
-
- lib/verdict/fixed_percentage_segmenter.rb
|
103
102
|
- lib/verdict/group.rb
|
104
103
|
- lib/verdict/metadata.rb
|
105
104
|
- lib/verdict/railtie.rb
|
106
|
-
- lib/verdict/
|
107
|
-
- lib/verdict/
|
108
|
-
- lib/verdict/
|
105
|
+
- lib/verdict/segmenters.rb
|
106
|
+
- lib/verdict/segmenters/base_segmenter.rb
|
107
|
+
- lib/verdict/segmenters/fixed_percentage_segmenter.rb
|
108
|
+
- lib/verdict/segmenters/rollout_segmenter.rb
|
109
|
+
- lib/verdict/segmenters/static_segmenter.rb
|
109
110
|
- lib/verdict/storage.rb
|
111
|
+
- lib/verdict/storage/memory_storage.rb
|
112
|
+
- lib/verdict/storage/mock_storage.rb
|
113
|
+
- lib/verdict/storage/redis_storage.rb
|
110
114
|
- lib/verdict/tasks.rake
|
111
115
|
- lib/verdict/version.rb
|
112
116
|
- test/assignment_test.rb
|
@@ -114,13 +118,13 @@ files:
|
|
114
118
|
- test/event_logger_test.rb
|
115
119
|
- test/experiment_test.rb
|
116
120
|
- test/experiments_repository_test.rb
|
117
|
-
- test/fixed_percentage_segmenter_test.rb
|
118
121
|
- test/group_test.rb
|
119
|
-
- test/memory_subject_storage_test.rb
|
120
122
|
- test/metadata_test.rb
|
121
|
-
- test/
|
122
|
-
- test/rollout_segmenter_test.rb
|
123
|
-
- test/static_segmenter_test.rb
|
123
|
+
- test/segmenters/fixed_percentage_segmenter_test.rb
|
124
|
+
- test/segmenters/rollout_segmenter_test.rb
|
125
|
+
- test/segmenters/static_segmenter_test.rb
|
126
|
+
- test/storage/memory_subject_storage_test.rb
|
127
|
+
- test/storage/redis_subject_storage_test.rb
|
124
128
|
- test/test_helper.rb
|
125
129
|
- verdict.gemspec
|
126
130
|
homepage: http://github.com/Shopify/verdict
|
@@ -142,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
146
|
version: '0'
|
143
147
|
requirements: []
|
144
148
|
rubyforge_project:
|
145
|
-
rubygems_version: 2.0.
|
149
|
+
rubygems_version: 2.0.3
|
146
150
|
signing_key:
|
147
151
|
specification_version: 4
|
148
152
|
summary: A library to centrally define experiments for your application, and collect
|
@@ -153,11 +157,12 @@ test_files:
|
|
153
157
|
- test/event_logger_test.rb
|
154
158
|
- test/experiment_test.rb
|
155
159
|
- test/experiments_repository_test.rb
|
156
|
-
- test/fixed_percentage_segmenter_test.rb
|
157
160
|
- test/group_test.rb
|
158
|
-
- test/memory_subject_storage_test.rb
|
159
161
|
- test/metadata_test.rb
|
160
|
-
- test/
|
161
|
-
- test/rollout_segmenter_test.rb
|
162
|
-
- test/static_segmenter_test.rb
|
162
|
+
- test/segmenters/fixed_percentage_segmenter_test.rb
|
163
|
+
- test/segmenters/rollout_segmenter_test.rb
|
164
|
+
- test/segmenters/static_segmenter_test.rb
|
165
|
+
- test/storage/memory_subject_storage_test.rb
|
166
|
+
- test/storage/redis_subject_storage_test.rb
|
163
167
|
- test/test_helper.rb
|
168
|
+
has_rdoc:
|
@@ -1,52 +0,0 @@
|
|
1
|
-
class Verdict::FixedPercentageSegmenter < Verdict::Segmenter
|
2
|
-
|
3
|
-
class Group < Verdict::Group
|
4
|
-
|
5
|
-
attr_reader :percentile_range
|
6
|
-
|
7
|
-
def initialize(experiment, handle, percentile_range)
|
8
|
-
super(experiment, handle)
|
9
|
-
@percentile_range = percentile_range
|
10
|
-
end
|
11
|
-
|
12
|
-
def percentage_size
|
13
|
-
percentile_range.end - percentile_range.begin
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
"#{handle} (#{percentage_size}%)"
|
18
|
-
end
|
19
|
-
|
20
|
-
def as_json(options = {})
|
21
|
-
super(options).merge(percentage: percentage_size)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(experiment)
|
26
|
-
super
|
27
|
-
@total_percentage_segmented = 0
|
28
|
-
end
|
29
|
-
|
30
|
-
def verify!
|
31
|
-
raise Verdict::SegmentationError, "Should segment exactly 100% of the cases, but segments add up to #{@total_percentage_segmented}%." if @total_percentage_segmented != 100
|
32
|
-
end
|
33
|
-
|
34
|
-
def register_group(handle, size)
|
35
|
-
percentage = size.kind_of?(Hash) && size[:percentage] ? size[:percentage] : size
|
36
|
-
n = case percentage
|
37
|
-
when :rest; 100 - @total_percentage_segmented
|
38
|
-
when :half; 50
|
39
|
-
when Integer; percentage
|
40
|
-
else Integer(percentage)
|
41
|
-
end
|
42
|
-
|
43
|
-
group = Group.new(experiment, handle, @total_percentage_segmented ... (@total_percentage_segmented + n))
|
44
|
-
@total_percentage_segmented += n
|
45
|
-
return group
|
46
|
-
end
|
47
|
-
|
48
|
-
def assign(identifier, subject, context)
|
49
|
-
percentile = Digest::MD5.hexdigest("#{@experiment.handle}#{identifier}").to_i(16) % 100
|
50
|
-
groups.values.find { |group| group.percentile_range.include?(percentile) }
|
51
|
-
end
|
52
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
class Verdict::RolloutSegmenter < Verdict::Segmenter
|
2
|
-
|
3
|
-
class Group < Verdict::Group
|
4
|
-
|
5
|
-
attr_reader :percentile_range
|
6
|
-
|
7
|
-
def initialize(experiment, handle, percentile_range)
|
8
|
-
super(experiment, handle)
|
9
|
-
@percentile_range = percentile_range
|
10
|
-
end
|
11
|
-
|
12
|
-
def percentage_size
|
13
|
-
percentile_range.end - percentile_range.begin
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
"#{handle} (#{percentage_size}%)"
|
18
|
-
end
|
19
|
-
|
20
|
-
def as_json(options = {})
|
21
|
-
super(options).merge(percentage: percentage_size)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(experiment)
|
26
|
-
super
|
27
|
-
@total_percentage_segmented = 0
|
28
|
-
end
|
29
|
-
|
30
|
-
def verify!
|
31
|
-
raise Verdict::SegmentationError, "Should segment less than 100% of the cases, but segments add up to #{@total_percentage_segmented}%." if @total_percentage_segmented >= 100
|
32
|
-
end
|
33
|
-
|
34
|
-
def register_group(handle, size)
|
35
|
-
percentage = size.kind_of?(Hash) && size[:percentage] ? size[:percentage] : size
|
36
|
-
n = case percentage
|
37
|
-
when :rest; 100 - @total_percentage_segmented
|
38
|
-
when :half; 50
|
39
|
-
when Integer; percentage
|
40
|
-
else Integer(percentage)
|
41
|
-
end
|
42
|
-
|
43
|
-
group = Group.new(experiment, handle, @total_percentage_segmented ... (@total_percentage_segmented + n))
|
44
|
-
@total_percentage_segmented += n
|
45
|
-
return group
|
46
|
-
end
|
47
|
-
|
48
|
-
def assign(identifier, subject, context)
|
49
|
-
percentile = Digest::MD5.hexdigest("#{@experiment.handle}#{identifier}").to_i(16) % 100
|
50
|
-
groups.values.find { |group| group.percentile_range.include?(percentile) }
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
data/lib/verdict/segmenter.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
|
-
# Base class of all segmenters.
|
4
|
-
#
|
5
|
-
# The segmenter is responsible for assigning subjects to groups. You can
|
6
|
-
# implement any assignment strategy you like by subclassing this class and
|
7
|
-
# using it in your experiment.
|
8
|
-
#
|
9
|
-
# - You should implement the register_group method for the experiment definition DSL
|
10
|
-
# to make the system aware of the groups that the segmenter could return.
|
11
|
-
# - The verify! method is called after all the groups have been defined, so it can
|
12
|
-
# detect internal inconsistencies in the group definitions.
|
13
|
-
# - The assign method is where your assignment magic lives.
|
14
|
-
class Verdict::Segmenter
|
15
|
-
|
16
|
-
# The experiment to which this segmenter is associated
|
17
|
-
attr_reader :experiment
|
18
|
-
|
19
|
-
# A hash of the groups that are defined in this experiment, indexed by their
|
20
|
-
# handle. The assign method should return one of the groups in this hash
|
21
|
-
attr_reader :groups
|
22
|
-
|
23
|
-
def initialize(experiment)
|
24
|
-
@experiment = experiment
|
25
|
-
@groups = {}
|
26
|
-
end
|
27
|
-
|
28
|
-
# DSL method to register a group. It calls the register_group method of the
|
29
|
-
# segmenter implementation
|
30
|
-
def group(handle, *args, &block)
|
31
|
-
group = register_group(handle, *args)
|
32
|
-
@groups[group.handle] = group
|
33
|
-
group.instance_eval(&block) if block_given?
|
34
|
-
end
|
35
|
-
|
36
|
-
# The group method is called from the experiment definition DSL.
|
37
|
-
# It should register a new group to the segmenter, with the given handle.
|
38
|
-
#
|
39
|
-
# - The handle parameter is a symbol that uniquely identifies the group within
|
40
|
-
# this experiment.
|
41
|
-
# - The return value of this method should be a Verdict::Group instance.
|
42
|
-
def register_group(handle, *args)
|
43
|
-
raise NotImplementedError
|
44
|
-
end
|
45
|
-
|
46
|
-
# The verify! method is called after all the groups have been defined in the
|
47
|
-
# experiment definition DSL. You can run any consistency checks in this method,
|
48
|
-
# and if anything is off, you can raise a Verdict::SegmentationError to
|
49
|
-
# signify the problem.
|
50
|
-
def verify!
|
51
|
-
# noop by default
|
52
|
-
end
|
53
|
-
|
54
|
-
# The assign method is called to assign a subject to one of the groups that have been defined
|
55
|
-
# in the segmenter implementation.
|
56
|
-
#
|
57
|
-
# - The identifier parameter is a string that uniquely identifies the subject.
|
58
|
-
# - The subject paramater is the subject instance that was passed to the framework,
|
59
|
-
# when the application code calls Experiment#assign or Experiment#switch.
|
60
|
-
# - The context parameter is an object that was passed to the framework, you can use this
|
61
|
-
# object any way you like in your segmenting logic.
|
62
|
-
#
|
63
|
-
# This method should return the Verdict::Group instance to which the subject should be assigned.
|
64
|
-
# This instance should be one of the group instance that was registered in the definition DSL.
|
65
|
-
def assign(identifier, subject, context)
|
66
|
-
raise NotImplementedError
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
# This method is called whenever a subjects converts to a goal, i.e., when Experiment#convert
|
71
|
-
# is called. You can use this to implement a feedback loop in your segmenter.
|
72
|
-
#
|
73
|
-
# - The identifier parameter is a string that uniquely identifies the subject.
|
74
|
-
# - The subject paramater is the subject instance that was passed to the framework,
|
75
|
-
# when the application code calls Experiment#assign or Experiment#switch.
|
76
|
-
# - The conversion parameter is a Verdict::Conversion instance that describes what
|
77
|
-
# goal the subject converted to.
|
78
|
-
#
|
79
|
-
# The return value of this method is not used.
|
80
|
-
def conversion_feedback(identifier, subject, conversion)
|
81
|
-
# noop by default
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
require 'verdict/static_segmenter'
|
86
|
-
require 'verdict/fixed_percentage_segmenter'
|
87
|
-
require 'verdict/rollout_segmenter'
|