cellect-server 0.0.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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/README.md +19 -0
- data/Rakefile +9 -0
- data/cellect-server.gemspec +40 -0
- data/cellect.gemspec +33 -0
- data/lib/cellect.rb +7 -0
- data/lib/cellect/node_set.rb +38 -0
- data/lib/cellect/server.rb +30 -0
- data/lib/cellect/server/adapters.rb +13 -0
- data/lib/cellect/server/adapters/default.rb +60 -0
- data/lib/cellect/server/adapters/postgres.rb +64 -0
- data/lib/cellect/server/api.rb +48 -0
- data/lib/cellect/server/api/helpers.rb +44 -0
- data/lib/cellect/server/api/sets.rb +21 -0
- data/lib/cellect/server/api/users.rb +32 -0
- data/lib/cellect/server/grouped_project.rb +65 -0
- data/lib/cellect/server/node_set.rb +19 -0
- data/lib/cellect/server/project.rb +123 -0
- data/lib/cellect/server/user.rb +66 -0
- data/lib/cellect/version.rb +3 -0
- data/spec/fixtures/project_data/grouped_pairwise_priority.json +109 -0
- data/spec/fixtures/project_data/grouped_pairwise_random.json +89 -0
- data/spec/fixtures/project_data/grouped_priority.json +59 -0
- data/spec/fixtures/project_data/grouped_random.json +49 -0
- data/spec/fixtures/project_data/pairwise_priority.json +49 -0
- data/spec/fixtures/project_data/pairwise_random.json +39 -0
- data/spec/fixtures/project_data/priority.json +49 -0
- data/spec/fixtures/project_data/random.json +39 -0
- data/spec/fixtures/user_data/complete_user.json +118 -0
- data/spec/fixtures/user_data/new_user.json +26 -0
- data/spec/fixtures/user_data/partial_user.json +58 -0
- data/spec/server/api/add_seen_spec.rb +26 -0
- data/spec/server/api/add_spec.rb +40 -0
- data/spec/server/api/remove_spec.rb +35 -0
- data/spec/server/api/sample_spec.rb +34 -0
- data/spec/server/api/user_load_spec.rb +25 -0
- data/spec/server/grouped_project_spec.rb +76 -0
- data/spec/server/node_set_spec.rb +13 -0
- data/spec/server/project_spec.rb +62 -0
- data/spec/server/server_spec.rb +24 -0
- data/spec/server/user_spec.rb +32 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/cellect_helper.rb +12 -0
- data/spec/support/shared_api_context.rb +11 -0
- data/spec/support/shared_examples_for_node_set.rb +27 -0
- data/spec/support/shared_examples_for_project.rb +26 -0
- data/spec/support/shared_examples_for_set.rb +34 -0
- data/spec/support/spec_adapter.rb +43 -0
- data/spec/support/zk_setup.rb +26 -0
- metadata +337 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"random": [
|
3
|
+
|
4
|
+
],
|
5
|
+
"priority": [
|
6
|
+
|
7
|
+
],
|
8
|
+
"grouped_random": [
|
9
|
+
|
10
|
+
],
|
11
|
+
"grouped_priority": [
|
12
|
+
|
13
|
+
],
|
14
|
+
"pairwise_random": [
|
15
|
+
|
16
|
+
],
|
17
|
+
"pairwise_priority": [
|
18
|
+
|
19
|
+
],
|
20
|
+
"grouped_pairwise_random": [
|
21
|
+
|
22
|
+
],
|
23
|
+
"grouped_pairwise_priority": [
|
24
|
+
|
25
|
+
]
|
26
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
{
|
2
|
+
"random": [
|
3
|
+
1,
|
4
|
+
2,
|
5
|
+
3,
|
6
|
+
4,
|
7
|
+
5
|
8
|
+
],
|
9
|
+
"priority": [
|
10
|
+
7,
|
11
|
+
6,
|
12
|
+
1,
|
13
|
+
3,
|
14
|
+
10
|
15
|
+
],
|
16
|
+
"grouped_random": [
|
17
|
+
1,
|
18
|
+
2,
|
19
|
+
3,
|
20
|
+
4,
|
21
|
+
5
|
22
|
+
],
|
23
|
+
"grouped_priority": [
|
24
|
+
8,
|
25
|
+
7,
|
26
|
+
10,
|
27
|
+
5,
|
28
|
+
6
|
29
|
+
],
|
30
|
+
"pairwise_random": [
|
31
|
+
1,
|
32
|
+
2,
|
33
|
+
3,
|
34
|
+
4,
|
35
|
+
5
|
36
|
+
],
|
37
|
+
"pairwise_priority": [
|
38
|
+
10,
|
39
|
+
6,
|
40
|
+
5,
|
41
|
+
2,
|
42
|
+
4
|
43
|
+
],
|
44
|
+
"grouped_pairwise_random": [
|
45
|
+
1,
|
46
|
+
2,
|
47
|
+
3,
|
48
|
+
4,
|
49
|
+
5
|
50
|
+
],
|
51
|
+
"grouped_pairwise_priority": [
|
52
|
+
16,
|
53
|
+
17,
|
54
|
+
3,
|
55
|
+
8,
|
56
|
+
7
|
57
|
+
]
|
58
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe API do
|
5
|
+
include_context 'API'
|
6
|
+
|
7
|
+
{ 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
|
8
|
+
SET_TYPES.shuffle.each do |set_type|
|
9
|
+
context "#{ grouping_type } #{ set_type }" do
|
10
|
+
let(:project_type){ [grouping, set_type].compact.join '_' }
|
11
|
+
let(:project){ Project[project_type] }
|
12
|
+
let(:user){ project.user 123 }
|
13
|
+
before(:each){ pass_until project, is: :ready }
|
14
|
+
|
15
|
+
it 'should add seen subjects' do
|
16
|
+
async_project = double
|
17
|
+
project.should_receive(:async).and_return async_project
|
18
|
+
async_project.should_receive(:add_seen_for).with 123, 123
|
19
|
+
put "/projects/#{ project_type }/users/123/add_seen", subject_id: 123
|
20
|
+
last_response.status.should == 200
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe API do
|
5
|
+
include_context 'API'
|
6
|
+
|
7
|
+
{ 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
|
8
|
+
SET_TYPES.shuffle.each do |set_type|
|
9
|
+
context "#{ grouping_type } #{ set_type }" do
|
10
|
+
let(:project_type){ [grouping, set_type].compact.join '_' }
|
11
|
+
let(:project){ Project[project_type] }
|
12
|
+
let(:user){ project.user 123 }
|
13
|
+
before(:each){ pass_until project, is: :ready }
|
14
|
+
|
15
|
+
let(:opts) do
|
16
|
+
{ subject_id: 123 }.tap do |h|
|
17
|
+
h[:priority] = 456.0 if project.prioritized?
|
18
|
+
h[:group_id] = 1 if project.grouped?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should add subjects' do
|
23
|
+
if project.grouped? && project.prioritized?
|
24
|
+
project.should_receive(:add).with subject_id: 123, group_id: 1, priority: 456.0
|
25
|
+
elsif project.grouped?
|
26
|
+
project.should_receive(:add).with subject_id: 123, group_id: 1, priority: nil
|
27
|
+
elsif project.prioritized?
|
28
|
+
project.should_receive(:add).with subject_id: 123, group_id: nil, priority: 456.0
|
29
|
+
else
|
30
|
+
project.should_receive(:add).with subject_id: 123, group_id: nil, priority: nil
|
31
|
+
end
|
32
|
+
|
33
|
+
put "/projects/#{ project_type }/add", opts
|
34
|
+
last_response.status.should == 200
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe API do
|
5
|
+
include_context 'API'
|
6
|
+
|
7
|
+
{ 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
|
8
|
+
SET_TYPES.shuffle.each do |set_type|
|
9
|
+
context "#{ grouping_type } #{ set_type }" do
|
10
|
+
let(:project_type){ [grouping, set_type].compact.join '_' }
|
11
|
+
let(:project){ Project[project_type] }
|
12
|
+
let(:user){ project.user 123 }
|
13
|
+
before(:each){ pass_until project, is: :ready }
|
14
|
+
|
15
|
+
let(:opts) do
|
16
|
+
{ subject_id: 123 }.tap do |h|
|
17
|
+
h[:group_id] = 1 if project.grouped?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should remove subjects' do
|
22
|
+
if project.grouped?
|
23
|
+
project.should_receive(:remove).with subject_id: 123, group_id: 1, priority: nil
|
24
|
+
else
|
25
|
+
project.should_receive(:remove).with subject_id: 123, group_id: nil, priority: nil
|
26
|
+
end
|
27
|
+
|
28
|
+
put "/projects/#{ project_type }/remove", opts
|
29
|
+
last_response.status.should == 200
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe API do
|
5
|
+
include_context 'API'
|
6
|
+
|
7
|
+
{ 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
|
8
|
+
SET_TYPES.shuffle.each do |set_type|
|
9
|
+
context "#{ grouping_type } #{ set_type }" do
|
10
|
+
let(:project_type){ [grouping, set_type].compact.join '_' }
|
11
|
+
let(:project){ Project[project_type] }
|
12
|
+
let(:user){ project.user 123 }
|
13
|
+
before(:each){ pass_until project, is: :ready }
|
14
|
+
|
15
|
+
it 'should sample without a user, limit, or group' do
|
16
|
+
project.should_receive(:sample).with(limit: 5, user_id: nil, group_id: nil).and_call_original
|
17
|
+
get "/projects/#{ project_type }"
|
18
|
+
last_response.status.should == 200
|
19
|
+
json.should be_a Array
|
20
|
+
end
|
21
|
+
|
22
|
+
shoulda = grouping ? 'limit, group, and user' : 'limit and user'
|
23
|
+
it "should sample with a #{ shoulda }" do
|
24
|
+
group_id = grouping ? 1 : nil
|
25
|
+
project.should_receive(:sample).with(limit: 3, user_id: 123, group_id: group_id).and_call_original
|
26
|
+
get "/projects/#{ project_type }?limit=3&user_id=123#{ grouping ? '&group_id=1' : '' }"
|
27
|
+
last_response.status.should == 200
|
28
|
+
json.should be_a Array
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe API do
|
5
|
+
include_context 'API'
|
6
|
+
|
7
|
+
{ 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
|
8
|
+
SET_TYPES.shuffle.each do |set_type|
|
9
|
+
context "#{ grouping_type } #{ set_type }" do
|
10
|
+
let(:project_type){ [grouping, set_type].compact.join '_' }
|
11
|
+
let(:project){ Project[project_type] }
|
12
|
+
before(:each){ pass_until project, is: :ready }
|
13
|
+
|
14
|
+
it 'should load users' do
|
15
|
+
async_project = double
|
16
|
+
project.should_receive(:async).and_return async_project
|
17
|
+
async_project.should_receive(:user).with 123
|
18
|
+
post "/projects/#{ project_type }/users/123/load"
|
19
|
+
last_response.status.should == 201
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe GroupedProject do
|
5
|
+
SET_TYPES.collect{ |type| "grouped_#{ type }" }.each do |project_type|
|
6
|
+
context project_type do
|
7
|
+
it_behaves_like 'project', :project
|
8
|
+
let(:project){ GroupedProject[project_type] }
|
9
|
+
let(:user){ project.user 123 }
|
10
|
+
let(:set_klass){ project.prioritized? ? DiffSet::PrioritySet : DiffSet::RandomSet }
|
11
|
+
before(:each){ pass_until project, is: :ready }
|
12
|
+
|
13
|
+
it 'should provide unseen from a random group for users' do
|
14
|
+
project.groups = { }
|
15
|
+
project.groups[1] = set_klass.new
|
16
|
+
project.groups[1].should_receive(:subtract).with user.seen, 3
|
17
|
+
project.unseen_for 123, limit: 3
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should provide unseen from a specific group for users' do
|
21
|
+
3.times{ |i| project.groups[i] = set_klass.new }
|
22
|
+
project.group(1).should_receive(:subtract).with user.seen, 3
|
23
|
+
project.unseen_for 123, group_id: 1, limit: 3
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should sample subjects from a random group without a user' do
|
27
|
+
project.groups = { }
|
28
|
+
project.groups[1] = set_klass.new
|
29
|
+
project.group(1).should_receive(:sample).with 3
|
30
|
+
project.sample limit: 3
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should sample subjects from a specific group without a user' do
|
34
|
+
3.times{ |i| project.groups[i] = set_klass.new }
|
35
|
+
project.group(1).should_receive(:sample).with 3
|
36
|
+
project.sample group_id: 1, limit: 3
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should sample subjects from a random group for a user' do
|
40
|
+
project.groups = { }
|
41
|
+
project.groups[1] = set_klass.new
|
42
|
+
project.groups[1].should_receive(:subtract).with user.seen, 3
|
43
|
+
project.sample user_id: 123, limit: 3
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should sample subjects from a specific group for a user' do
|
47
|
+
3.times{ |i| project.groups[i] = set_klass.new }
|
48
|
+
project.group(1).should_receive(:subtract).with user.seen, 3
|
49
|
+
project.sample user_id: 123, group_id: 1, limit: 3
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should add subjects' do
|
53
|
+
project.groups[1] = set_klass.new
|
54
|
+
|
55
|
+
if project.prioritized?
|
56
|
+
project.groups[1].should_receive(:add).with 123, 456
|
57
|
+
project.add subject_id: 123, group_id: 1, priority: 456
|
58
|
+
else
|
59
|
+
project.groups[1].should_receive(:add).with 123
|
60
|
+
project.add subject_id: 123, group_id: 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should remove subjects' do
|
65
|
+
project.groups[1] = set_klass.new
|
66
|
+
project.groups[1].should_receive(:remove).with 123
|
67
|
+
project.remove subject_id: 123, group_id: 1
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should be grouped' do
|
71
|
+
project.should be_grouped
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe NodeSet do
|
5
|
+
it_behaves_like 'node set'
|
6
|
+
let(:node_set){ Cellect::Server.node_set.actors.first }
|
7
|
+
|
8
|
+
it 'should register this node' do
|
9
|
+
node_set.id.should == 'node0000000000'
|
10
|
+
node_set.zk.get('/nodes/node0000000000').first.should =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe Project do
|
5
|
+
SET_TYPES.each do |project_type|
|
6
|
+
context project_type do
|
7
|
+
it_behaves_like 'project', :project
|
8
|
+
let(:project){ Project[project_type] }
|
9
|
+
let(:user){ project.user 123 }
|
10
|
+
before(:each){ pass_until project, is: :ready }
|
11
|
+
|
12
|
+
it 'should provide unseen for users' do
|
13
|
+
project.subjects.should_receive(:subtract).with user.seen, 3
|
14
|
+
project.unseen_for 123, limit: 3
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should sample subjects without a user' do
|
18
|
+
project.subjects.should_receive(:sample).with 3
|
19
|
+
project.sample limit: 3
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should sample subjects with a user' do
|
23
|
+
project.subjects.should_receive(:subtract).with user.seen, 3
|
24
|
+
project.sample user_id: 123, limit: 3
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should add subjects' do
|
28
|
+
if project.prioritized?
|
29
|
+
project.subjects.should_receive(:add).with 123, 456
|
30
|
+
project.add subject_id: 123, priority: 456
|
31
|
+
else
|
32
|
+
project.subjects.should_receive(:add).with 123
|
33
|
+
project.add subject_id: 123
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should remove subjects' do
|
38
|
+
project.subjects.should_receive(:add).with 123
|
39
|
+
project.add subject_id: 123
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be notified of a user ttl expiry' do
|
43
|
+
async_project = double
|
44
|
+
project.should_receive(:async).and_return async_project
|
45
|
+
async_project.should_receive(:remove_user).with user.id
|
46
|
+
user.ttl_expired!
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should remove users when their ttl expires' do
|
50
|
+
id = user.id
|
51
|
+
project.remove_user id
|
52
|
+
project.users.should_not have_key id
|
53
|
+
expect{ user.id }.to raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should not be grouped' do
|
57
|
+
project.should_not be_grouped
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe Cellect do
|
5
|
+
context 'default adapter' do
|
6
|
+
let(:default){ Cellect::Server::Adapters::Default.new }
|
7
|
+
|
8
|
+
it 'should raise a NotImplementedError when using the default adapter' do
|
9
|
+
expect{ default.project_list }.to raise_error NotImplementedError
|
10
|
+
expect{ default.load_data_for(Project.new('test')) }.to raise_error NotImplementedError
|
11
|
+
expect{ default.load_user 'random', 123 }.to raise_error NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return a project given a set of options' do
|
15
|
+
default.project_for('name' => 'a').should be_an_instance_of Project
|
16
|
+
default.project_for('name' => 'b', 'grouped' => true).should be_an_instance_of GroupedProject
|
17
|
+
default.project_for('name' => 'c', 'pairwise' => true).should be_pairwise
|
18
|
+
default.project_for('name' => 'd', 'prioritized' => true).should be_prioritized
|
19
|
+
default.project_for('name' => 'e', 'pairwise' => true, 'prioritized' => true).should be_pairwise
|
20
|
+
default.project_for('name' => 'e', 'pairwise' => true, 'prioritized' => true).should be_prioritized
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Cellect::Server
|
4
|
+
describe User do
|
5
|
+
let(:user){ User.new 1, project_name: 'random' }
|
6
|
+
|
7
|
+
it 'should store seen ids' do
|
8
|
+
user.seen.should be_a DiffSet::RandomSet
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should have a default ttl of 15 minutes' do
|
12
|
+
user.ttl.should == 900 # seconds
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should allow custom ttl' do
|
16
|
+
User.new(2, project_name: 'random', ttl: 123).ttl.should == 123
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should reset the ttl timer on activity' do
|
20
|
+
user.bare_object.should_receive(:restart_ttl_timer).at_least :once
|
21
|
+
user.seen
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should terminate on ttl expiry' do
|
25
|
+
async_project = double
|
26
|
+
Project[user.project_name].should_receive(:async).and_return async_project
|
27
|
+
async_project.should_receive(:remove_user).with user.id
|
28
|
+
user.ttl_expired!
|
29
|
+
user.ttl_timer.should be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|