cellect-server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|