divisio 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/Gemfile +0 -9
- data/README.md +1 -1
- data/divisio.gemspec +6 -2
- data/lib/divisio.rb +0 -1
- data/lib/divisio/base_adapter.rb +2 -2
- data/lib/divisio/modulo_algorithm.rb +0 -1
- data/lib/divisio/mongoid_adapter.rb +1 -1
- data/lib/divisio/mongoid_adapter/experiment.rb +1 -3
- data/lib/divisio/version.rb +1 -1
- data/mongoid.yml +1 -3
- data/spec/divisio/base_adapter_spec.rb +0 -1
- data/spec/divisio/modulo_algorithm_spec.rb +4 -4
- data/spec/divisio/mongoid_adapter/experiment_spec.rb +8 -10
- data/spec/divisio/mongoid_adapter_spec.rb +3 -5
- data/spec/divisio/no_persistence_adapter_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/support/shared_examples_for_base_adapter.rb +2 -3
- metadata +47 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd36702ea18fb40ba9952362c384f24cb0a41da1
|
4
|
+
data.tar.gz: 6a12c149173875cf9d9cade5705c447f114dd792
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70781a34c9383b36b4e68b7cf2bc12af388cfb8db71a92ba3b97c6d4686a258bfafe823f449bff2e12fcd595b6242538cc253130ac550715e41e239b637bae7a
|
7
|
+
data.tar.gz: f038c089575bbb2ed3b61f0024f35c25a928a7f8a543f9ec69d3de647d3e1a0bc93ffbeda957f52b57a70084fc07535408251ffd7d17f82574629ad247b21769
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -43,7 +43,7 @@ Divisio.new.split(experiment_name, variants, identity) # ==>> 1
|
|
43
43
|
|
44
44
|
### Mongoid adaper
|
45
45
|
|
46
|
-
_Requires mongoid
|
46
|
+
_Requires mongoid v5.0.0 or greater_
|
47
47
|
|
48
48
|
This adapter will persist the experiment name, identifier, and variant information in a MongoDb collection called `experiments`. Note: The variant returned will be cast to a string.
|
49
49
|
|
data/divisio.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Divisio::VERSION
|
9
9
|
spec.authors = ['Dragos Miron']
|
10
10
|
spec.email = ['tech@simplybusiness.co.uk']
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
11
|
+
spec.summary = 'Provides a splitting framework similar to AB testing'
|
12
|
+
spec.description = 'Provides a splitting framework similar to AB testing'
|
13
13
|
spec.homepage = 'http://www.simplybusiness.co.uk'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
@@ -17,4 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'mongoid', '~> 6.0'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
23
|
+
spec.add_development_dependency 'rubocop', '~> 0.46'
|
20
24
|
end
|
data/lib/divisio.rb
CHANGED
data/lib/divisio/base_adapter.rb
CHANGED
@@ -3,10 +3,10 @@ class Divisio
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def split(experiment_name, variants, identity)
|
6
|
-
ModuloAlgorithm.new(experiment_name.to_s+identity.to_s, variants).calc
|
6
|
+
ModuloAlgorithm.new(experiment_name.to_s + identity.to_s, variants).calc
|
7
7
|
end
|
8
8
|
|
9
|
-
def delete_experiment_for_identity(
|
9
|
+
def delete_experiment_for_identity(_identity, _experiment_name)
|
10
10
|
raise NotImplementedError
|
11
11
|
end
|
12
12
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class Divisio
|
2
2
|
module MongoidAdapter
|
3
3
|
class Experiment
|
4
|
-
|
5
4
|
include Mongoid::Document
|
6
5
|
include Mongoid::Timestamps
|
7
6
|
|
@@ -12,8 +11,7 @@ class Divisio
|
|
12
11
|
field :identifier, type: String
|
13
12
|
field :variant, type: String
|
14
13
|
|
15
|
-
index({ name: 1, identifier: 1},
|
16
|
-
|
14
|
+
index({ name: 1, identifier: 1 }, unique: true)
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
data/lib/divisio/version.rb
CHANGED
data/mongoid.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
describe Divisio::ModuloAlgorithm do
|
2
2
|
describe '#calc' do
|
3
|
-
let(:variants) {
|
3
|
+
let(:variants) {}
|
4
4
|
|
5
5
|
context 'when there is one variant provided' do
|
6
6
|
let(:variants) { 1 }
|
@@ -11,9 +11,9 @@ describe Divisio::ModuloAlgorithm do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
context 'when there are multiple variants provided' do
|
14
|
-
let(:variants) {
|
14
|
+
let(:variants) { %w(1 2 3) }
|
15
15
|
|
16
|
-
{ blah8: '1', blah4: '2', blah0: '3'}.each_pair do |key, expected_variant|
|
16
|
+
{ blah8: '1', blah4: '2', blah0: '3' }.each_pair do |key, expected_variant|
|
17
17
|
it "returns the variant based on the key provided: #{key}" do
|
18
18
|
expect(Divisio::ModuloAlgorithm.new(key, variants).calc).to eq(expected_variant)
|
19
19
|
end
|
@@ -23,7 +23,7 @@ describe Divisio::ModuloAlgorithm do
|
|
23
23
|
context 'when there are multiple weighted variants provided' do
|
24
24
|
let(:variants) { { a: 1, b: 2, c: 3 } }
|
25
25
|
|
26
|
-
{ blah8: :a, blah4: :b, blah0: :c}.each_pair do |key, expected_variant|
|
26
|
+
{ blah8: :a, blah4: :b, blah0: :c }.each_pair do |key, expected_variant|
|
27
27
|
it "returns the variant based on the key provided and weight: #{key}" do
|
28
28
|
expect(Divisio::ModuloAlgorithm.new(key, variants).calc).to eq(expected_variant)
|
29
29
|
end
|
@@ -1,36 +1,34 @@
|
|
1
1
|
describe Divisio::MongoidAdapter::Experiment do
|
2
2
|
describe '#save' do
|
3
|
-
|
4
|
-
let(:required_fields) { {:name => 'experiment', :identifier => 'hash', :variant => '2'} }
|
3
|
+
let(:required_fields) { { name: 'experiment', identifier: 'hash', variant: '2' } }
|
5
4
|
subject { Divisio::MongoidAdapter::Experiment.new }
|
6
5
|
|
7
6
|
context 'all fields are present' do
|
8
7
|
it 'gets saved to the database' do
|
9
8
|
subject.attributes = required_fields
|
10
9
|
|
11
|
-
expect{ subject.save }.to change{ described_class.count }.from(0).to(1)
|
10
|
+
expect { subject.save }.to change { described_class.count }.from(0).to(1)
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
14
|
context 'missing fields' do
|
16
15
|
it 'does not get saved if identifier is missing' do
|
17
|
-
subject.attributes = required_fields.tap{ |h| h.delete(:identifier) }
|
16
|
+
subject.attributes = required_fields.tap { |h| h.delete(:identifier) }
|
18
17
|
expect(subject.save).to be_falsey
|
19
18
|
end
|
20
19
|
|
21
20
|
it 'does not get saved if name is missing' do
|
22
|
-
subject.attributes = required_fields.tap{ |h| h.delete(:name) }
|
21
|
+
subject.attributes = required_fields.tap { |h| h.delete(:name) }
|
23
22
|
expect(subject.save).to be_falsey
|
24
23
|
end
|
25
24
|
|
26
25
|
it 'does not get saved if variant is missing' do
|
27
|
-
subject.attributes = required_fields.tap{ |h| h.delete(:variant) }
|
26
|
+
subject.attributes = required_fields.tap { |h| h.delete(:variant) }
|
28
27
|
expect(subject.save).to be_falsey
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
31
|
context 'uniqueness' do
|
33
|
-
|
34
32
|
it 'does not save second object with the same name and identifier' do
|
35
33
|
subject.attributes = required_fields
|
36
34
|
subject.save
|
@@ -39,16 +37,16 @@ describe Divisio::MongoidAdapter::Experiment do
|
|
39
37
|
required_fields[:variant] = '3'
|
40
38
|
new_object = described_class.new(required_fields)
|
41
39
|
|
42
|
-
expect{ new_object.save }.to_not change(described_class, :count)
|
40
|
+
expect { new_object.save }.to_not change(described_class, :count)
|
43
41
|
end
|
44
42
|
|
45
43
|
it 'does not save second object in case of race condition because of mongo index' do
|
46
44
|
described_class.create_indexes
|
47
|
-
Mongoid.
|
45
|
+
Mongoid::Clients.default[:divisio_mongoid_adapter_experiments].insert_one(required_fields)
|
48
46
|
expect(described_class.count).to eq(1)
|
49
47
|
|
50
48
|
collection = described_class.collection
|
51
|
-
expect{collection.
|
49
|
+
expect { collection.insert_one(required_fields) }.to raise_exception(Mongo::Error::OperationFailure)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -4,12 +4,11 @@ describe Divisio::MongoidAdapter do
|
|
4
4
|
let(:identity) { 'identity' }
|
5
5
|
|
6
6
|
describe '::split' do
|
7
|
-
subject{ described_class.split(experiment, variants, identity) }
|
7
|
+
subject { described_class.split(experiment, variants, identity) }
|
8
8
|
|
9
9
|
it_behaves_like 'a base adapter'
|
10
10
|
|
11
11
|
context 'new record' do
|
12
|
-
|
13
12
|
it 'saves the experiment to the database' do
|
14
13
|
expect_any_instance_of(Divisio::MongoidAdapter::Experiment).to receive(:save)
|
15
14
|
subject
|
@@ -21,7 +20,6 @@ describe Divisio::MongoidAdapter do
|
|
21
20
|
end
|
22
21
|
|
23
22
|
context 'old record' do
|
24
|
-
|
25
23
|
before do
|
26
24
|
Divisio::MongoidAdapter::Experiment.create(name: experiment, identifier: identity, variant: 'random')
|
27
25
|
end
|
@@ -38,7 +36,7 @@ describe Divisio::MongoidAdapter do
|
|
38
36
|
end
|
39
37
|
|
40
38
|
describe '::delete_experiment_for_identity' do
|
41
|
-
subject{ described_class.delete_experiment_for_identity(identity, experiment) }
|
39
|
+
subject { described_class.delete_experiment_for_identity(identity, experiment) }
|
42
40
|
|
43
41
|
context 'record exists in the database' do
|
44
42
|
before do
|
@@ -46,7 +44,7 @@ describe Divisio::MongoidAdapter do
|
|
46
44
|
end
|
47
45
|
|
48
46
|
it 'deletes the record' do
|
49
|
-
expect{subject}.to change { Divisio::MongoidAdapter::Experiment.count }.from(1).to(0)
|
47
|
+
expect { subject }.to change { Divisio::MongoidAdapter::Experiment.count }.from(1).to(0)
|
50
48
|
end
|
51
49
|
|
52
50
|
it 'returns true' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
ENV['RACK_ENV'] = 'test'
|
2
2
|
|
3
|
-
Dir['./spec/support/**/*.rb'].sort.each { |f| require f}
|
3
|
+
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
4
4
|
|
5
5
|
require 'mongoid'
|
6
6
|
require 'divisio'
|
@@ -9,6 +9,6 @@ Mongoid.load!('mongoid.yml')
|
|
9
9
|
|
10
10
|
RSpec.configure do |config|
|
11
11
|
config.before(:each) do
|
12
|
-
Mongoid
|
12
|
+
Mongoid.purge!
|
13
13
|
end
|
14
14
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
shared_examples_for 'a base adapter' do
|
2
|
-
|
3
2
|
describe '::split' do
|
4
3
|
let(:experiment) { 'experiment' }
|
5
|
-
let(:variants) {
|
4
|
+
let(:variants) { %w(1 2 3) }
|
6
5
|
let(:identity) { 'identity' }
|
7
|
-
subject{ described_class.split(experiment, variants, identity) }
|
6
|
+
subject { described_class.split(experiment, variants, identity) }
|
8
7
|
|
9
8
|
it 'returns a variant for the given experiment and identity' do
|
10
9
|
expect(subject).to eq('3')
|
metadata
CHANGED
@@ -1,15 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: divisio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dragos Miron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2017-01-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mongoid
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.46'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.46'
|
13
55
|
description: Provides a splitting framework similar to AB testing
|
14
56
|
email:
|
15
57
|
- tech@simplybusiness.co.uk
|
@@ -19,6 +61,7 @@ extra_rdoc_files: []
|
|
19
61
|
files:
|
20
62
|
- ".gitignore"
|
21
63
|
- ".rspec"
|
64
|
+
- ".rubocop.yml"
|
22
65
|
- Gemfile
|
23
66
|
- LICENSE.txt
|
24
67
|
- README.md
|
@@ -59,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
102
|
version: '0'
|
60
103
|
requirements: []
|
61
104
|
rubyforge_project:
|
62
|
-
rubygems_version: 2.
|
105
|
+
rubygems_version: 2.4.5.1
|
63
106
|
signing_key:
|
64
107
|
specification_version: 4
|
65
108
|
summary: Provides a splitting framework similar to AB testing
|
@@ -72,4 +115,3 @@ test_files:
|
|
72
115
|
- spec/divisio_spec.rb
|
73
116
|
- spec/spec_helper.rb
|
74
117
|
- spec/support/shared_examples_for_base_adapter.rb
|
75
|
-
has_rdoc:
|