cellect-server 0.1.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -8
  4. data/cellect-server.gemspec +4 -4
  5. data/cellect.gemspec +4 -3
  6. data/lib/cellect.rb +1 -1
  7. data/lib/cellect/node_set.rb +12 -12
  8. data/lib/cellect/server.rb +4 -4
  9. data/lib/cellect/server/adapters.rb +2 -2
  10. data/lib/cellect/server/adapters/default.rb +6 -6
  11. data/lib/cellect/server/adapters/postgres.rb +5 -5
  12. data/lib/cellect/server/api.rb +9 -9
  13. data/lib/cellect/server/api/helpers.rb +5 -5
  14. data/lib/cellect/server/api/sets.rb +2 -2
  15. data/lib/cellect/server/api/users.rb +5 -5
  16. data/lib/cellect/server/grouped_workflow.rb +10 -10
  17. data/lib/cellect/server/node_set.rb +2 -2
  18. data/lib/cellect/server/user.rb +10 -10
  19. data/lib/cellect/server/workflow.rb +21 -21
  20. data/lib/cellect/version.rb +1 -1
  21. data/spec/server/api/add_seen_spec.rb +2 -2
  22. data/spec/server/api/add_spec.rb +4 -4
  23. data/spec/server/api/remove_spec.rb +4 -4
  24. data/spec/server/api/sample_spec.rb +3 -3
  25. data/spec/server/api/user_load_spec.rb +2 -2
  26. data/spec/server/grouped_workflow_spec.rb +10 -10
  27. data/spec/server/node_set_spec.rb +1 -1
  28. data/spec/server/server_spec.rb +2 -2
  29. data/spec/server/user_spec.rb +5 -5
  30. data/spec/server/workflow_spec.rb +9 -9
  31. data/spec/spec_helper.rb +3 -4
  32. data/spec/support/shared_api_context.rb +2 -2
  33. data/spec/support/shared_examples_for_node_set.rb +3 -3
  34. data/spec/support/shared_examples_for_set.rb +6 -6
  35. data/spec/support/shared_examples_for_workflow.rb +5 -5
  36. data/spec/support/spec_adapter.rb +6 -6
  37. data/spec/support/zk_setup.rb +48 -25
  38. metadata +3 -3
@@ -5,9 +5,9 @@ module Cellect
5
5
  module Server
6
6
  class NodeSet < Cellect::NodeSet
7
7
  attr_accessor :id
8
-
8
+
9
9
  protected
10
-
10
+
11
11
  # Registers this server instance with ZooKeeper
12
12
  def setup
13
13
  zk.mkdir_p '/nodes'
@@ -3,14 +3,14 @@ module Cellect
3
3
  class User
4
4
  include Celluloid
5
5
  include Celluloid::Logger
6
-
6
+
7
7
  # Gracefully exit when the actor dies
8
8
  trap_exit :workflow_crashed
9
9
  finalizer :cancel_ttl_timer
10
-
10
+
11
11
  attr_accessor :id, :workflow_name, :seen, :state
12
12
  attr_accessor :ttl, :ttl_timer
13
-
13
+
14
14
  # Sets up a new user with an empty seen set, then loads the actual data
15
15
  def initialize(id, workflow_name: nil, ttl: nil)
16
16
  self.id = id
@@ -20,7 +20,7 @@ module Cellect
20
20
  @ttl = ttl
21
21
  load_data
22
22
  end
23
-
23
+
24
24
  # Load the seen subjects for a user and restarts the TTL
25
25
  def load_data
26
26
  data = Cellect::Server.adapter.load_user(workflow_name, id) || []
@@ -30,36 +30,36 @@ module Cellect
30
30
  self.state = :ready
31
31
  restart_ttl_timer
32
32
  end
33
-
33
+
34
34
  # Returns the seen subjects set
35
35
  def seen
36
36
  restart_ttl_timer
37
37
  @seen
38
38
  end
39
-
39
+
40
40
  # (Re)starts the inactivity countdown
41
41
  def restart_ttl_timer
42
42
  self.ttl_timer ||= after(ttl){ ttl_expired! }
43
43
  ttl_timer.reset
44
44
  end
45
-
45
+
46
46
  # Releases the timer
47
47
  def cancel_ttl_timer
48
48
  ttl_timer.cancel if ttl_timer
49
49
  self.ttl_timer = nil
50
50
  end
51
-
51
+
52
52
  # Removes the user from inactivity
53
53
  def ttl_expired!
54
54
  debug "User #{ id } TTL expired"
55
55
  cancel_ttl_timer
56
56
  Workflow[workflow_name].async.remove_user(id)
57
57
  end
58
-
58
+
59
59
  def ttl
60
60
  @ttl || 60 * 15 # 15 minutes
61
61
  end
62
-
62
+
63
63
  # Handle errors and let the actor die
64
64
  def workflow_crashed(actor, reason)
65
65
  cancel_ttl_timer
@@ -2,33 +2,33 @@ module Cellect
2
2
  module Server
3
3
  class Workflow
4
4
  include Celluloid
5
-
5
+
6
6
  attr_accessor :name, :users, :subjects, :state
7
7
  attr_accessor :pairwise, :prioritized
8
-
8
+
9
9
  # Look up and/or load a workflow
10
10
  def self.[](name)
11
11
  Cellect::Server.adapter.load_workflows(name) unless Actor[name]
12
12
  Actor[name].actors.first
13
13
  end
14
-
14
+
15
15
  # Load a workflow
16
16
  def self.[]=(name, opts)
17
17
  Actor[name] = supervise name, pairwise: opts['pairwise'], prioritized: opts['prioritized']
18
18
  end
19
-
19
+
20
20
  # The names of all workflows currently loaded
21
21
  def self.names
22
22
  actor_names = Celluloid.actor_system.registry.names.collect &:to_s
23
23
  workflow_actors = actor_names.select{ |key| key =~ /^workflow_/ }
24
24
  workflow_actors.collect{ |name| name.sub(/^workflow_/, '').to_sym }
25
25
  end
26
-
26
+
27
27
  # All currently loaded workflows
28
28
  def self.all
29
29
  names.collect{ |name| Workflow[name] }
30
30
  end
31
-
31
+
32
32
  # Sets up a new workflow and starts the data loading
33
33
  def initialize(name, pairwise: false, prioritized: false)
34
34
  self.name = name
@@ -38,7 +38,7 @@ module Cellect
38
38
  self.subjects = set_klass.new
39
39
  load_data
40
40
  end
41
-
41
+
42
42
  # Loads subjects from the adapter
43
43
  def load_data
44
44
  self.state = :initializing
@@ -48,31 +48,31 @@ module Cellect
48
48
  end
49
49
  self.state = :ready
50
50
  end
51
-
51
+
52
52
  # Look up and/or load a user
53
53
  def user(id)
54
54
  self.users[id] ||= User.supervise id, workflow_name: name
55
55
  users[id].actors.first
56
56
  end
57
-
57
+
58
58
  # Get unseen subjects for a user
59
59
  def unseen_for(user_id, limit: 5)
60
60
  subjects.subtract user(user_id).seen, limit
61
61
  end
62
-
62
+
63
63
  # Add subjects to a users seen set
64
64
  def add_seen_for(user_id, *subject_ids)
65
65
  [subject_ids].flatten.compact.each do |subject_id|
66
66
  user(user_id).seen.add subject_id
67
67
  end
68
68
  end
69
-
69
+
70
70
  # Unload a user
71
71
  def remove_user(user_id)
72
72
  removed = self.users.delete user_id
73
73
  removed.terminate if removed
74
74
  end
75
-
75
+
76
76
  # Get a sample of subjects for a user
77
77
  #
78
78
  # Accepts a hash in the form:
@@ -87,7 +87,7 @@ module Cellect
87
87
  subjects.sample opts[:limit]
88
88
  end
89
89
  end
90
-
90
+
91
91
  # Adds or updates a subject
92
92
  #
93
93
  # Accepts a hash in the form:
@@ -102,7 +102,7 @@ module Cellect
102
102
  subjects.add opts[:subject_id]
103
103
  end
104
104
  end
105
-
105
+
106
106
  # Removes a subject
107
107
  #
108
108
  # Accepts a hash in the form:
@@ -112,23 +112,23 @@ module Cellect
112
112
  def remove(opts = { })
113
113
  subjects.remove opts[:subject_id]
114
114
  end
115
-
115
+
116
116
  def pairwise?
117
117
  !!pairwise
118
118
  end
119
-
119
+
120
120
  def prioritized?
121
121
  !!prioritized
122
122
  end
123
-
123
+
124
124
  def grouped?
125
125
  false
126
126
  end
127
-
127
+
128
128
  def ready?
129
129
  state == :ready
130
130
  end
131
-
131
+
132
132
  # Provide a lookup for matching sets to workflow criteria
133
133
  SET_KLASS = {
134
134
  # priority, pairwise
@@ -137,12 +137,12 @@ module Cellect
137
137
  [ true, false ] => DiffSet::PrioritySet,
138
138
  [ true, true ] => DiffSet::PairwisePrioritySet
139
139
  }
140
-
140
+
141
141
  # Looks up the set class
142
142
  def set_klass
143
143
  SET_KLASS[[prioritized, pairwise]]
144
144
  end
145
-
145
+
146
146
  # General information about this workflow
147
147
  def status
148
148
  {
@@ -1,3 +1,3 @@
1
1
  module Cellect
2
- VERSION = '0.1.3'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module Cellect::Server
4
4
  describe API do
5
5
  include_context 'API'
6
-
6
+
7
7
  { 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
8
8
  SET_TYPES.shuffle.each do |set_type|
9
9
  context "#{ grouping_type } #{ set_type }" do
@@ -11,7 +11,7 @@ module Cellect::Server
11
11
  let(:workflow){ Workflow[workflow_type] }
12
12
  let(:user){ workflow.user 123 }
13
13
  before(:each){ pass_until workflow, is: :ready }
14
-
14
+
15
15
  it 'should add seen subjects' do
16
16
  async_workflow = double
17
17
  expect(workflow).to receive(:async).and_return async_workflow
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module Cellect::Server
4
4
  describe API do
5
5
  include_context 'API'
6
-
6
+
7
7
  { 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
8
8
  SET_TYPES.shuffle.each do |set_type|
9
9
  context "#{ grouping_type } #{ set_type }" do
@@ -11,14 +11,14 @@ module Cellect::Server
11
11
  let(:workflow){ Workflow[workflow_type] }
12
12
  let(:user){ workflow.user 123 }
13
13
  before(:each){ pass_until workflow, is: :ready }
14
-
14
+
15
15
  let(:opts) do
16
16
  { subject_id: 123 }.tap do |h|
17
17
  h[:priority] = 456.0 if workflow.prioritized?
18
18
  h[:group_id] = 1 if workflow.grouped?
19
19
  end
20
20
  end
21
-
21
+
22
22
  it 'should add subjects' do
23
23
  if workflow.grouped? && workflow.prioritized?
24
24
  expect(workflow).to receive(:add).with subject_id: 123, group_id: 1, priority: 456.0
@@ -29,7 +29,7 @@ module Cellect::Server
29
29
  else
30
30
  expect(workflow).to receive(:add).with subject_id: 123, group_id: nil, priority: nil
31
31
  end
32
-
32
+
33
33
  put "/workflows/#{ workflow_type }/add", opts
34
34
  expect(last_response.status).to eq 200
35
35
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module Cellect::Server
4
4
  describe API do
5
5
  include_context 'API'
6
-
6
+
7
7
  { 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
8
8
  SET_TYPES.shuffle.each do |set_type|
9
9
  context "#{ grouping_type } #{ set_type }" do
@@ -11,20 +11,20 @@ module Cellect::Server
11
11
  let(:workflow){ Workflow[workflow_type] }
12
12
  let(:user){ workflow.user 123 }
13
13
  before(:each){ pass_until workflow, is: :ready }
14
-
14
+
15
15
  let(:opts) do
16
16
  { subject_id: 123 }.tap do |h|
17
17
  h[:group_id] = 1 if workflow.grouped?
18
18
  end
19
19
  end
20
-
20
+
21
21
  it 'should remove subjects' do
22
22
  if workflow.grouped?
23
23
  expect(workflow).to receive(:remove).with subject_id: 123, group_id: 1, priority: nil
24
24
  else
25
25
  expect(workflow).to receive(:remove).with subject_id: 123, group_id: nil, priority: nil
26
26
  end
27
-
27
+
28
28
  put "/workflows/#{ workflow_type }/remove", opts
29
29
  expect(last_response.status).to eq 200
30
30
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module Cellect::Server
4
4
  describe API do
5
5
  include_context 'API'
6
-
6
+
7
7
  { 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
8
8
  SET_TYPES.shuffle.each do |set_type|
9
9
  context "#{ grouping_type } #{ set_type }" do
@@ -11,14 +11,14 @@ module Cellect::Server
11
11
  let(:workflow){ Workflow[workflow_type] }
12
12
  let(:user){ workflow.user 123 }
13
13
  before(:each){ pass_until workflow, is: :ready }
14
-
14
+
15
15
  it 'should sample without a user, limit, or group' do
16
16
  expect(workflow).to receive(:sample).with(limit: 5, user_id: nil, group_id: nil).and_call_original
17
17
  get "/workflows/#{ workflow_type }"
18
18
  expect(last_response.status).to eq 200
19
19
  expect(json).to be_a Array
20
20
  end
21
-
21
+
22
22
  shoulda = grouping ? 'limit, group, and user' : 'limit and user'
23
23
  it "should sample with a #{ shoulda }" do
24
24
  group_id = grouping ? 1 : nil
@@ -3,14 +3,14 @@ require 'spec_helper'
3
3
  module Cellect::Server
4
4
  describe API do
5
5
  include_context 'API'
6
-
6
+
7
7
  { 'Ungrouped' => nil, 'Grouped' => 'grouped' }.each_pair do |grouping_type, grouping|
8
8
  SET_TYPES.shuffle.each do |set_type|
9
9
  context "#{ grouping_type } #{ set_type }" do
10
10
  let(:workflow_type){ [grouping, set_type].compact.join '_' }
11
11
  let(:workflow){ Workflow[workflow_type] }
12
12
  before(:each){ pass_until workflow, is: :ready }
13
-
13
+
14
14
  it 'should load users' do
15
15
  async_workflow = double
16
16
  expect(workflow).to receive(:async).and_return async_workflow
@@ -9,49 +9,49 @@ module Cellect::Server
9
9
  let(:user){ workflow.user 123 }
10
10
  let(:set_klass){ workflow.prioritized? ? DiffSet::PrioritySet : DiffSet::RandomSet }
11
11
  before(:each){ pass_until workflow, is: :ready }
12
-
12
+
13
13
  it 'should provide unseen from a random group for users' do
14
14
  workflow.groups = { }
15
15
  workflow.groups[1] = set_klass.new
16
16
  expect(workflow.groups[1]).to receive(:subtract).with user.seen, 3
17
17
  workflow.unseen_for 123, limit: 3
18
18
  end
19
-
19
+
20
20
  it 'should provide unseen from a specific group for users' do
21
21
  3.times{ |i| workflow.groups[i] = set_klass.new }
22
22
  expect(workflow.group(1)).to receive(:subtract).with user.seen, 3
23
23
  workflow.unseen_for 123, group_id: 1, limit: 3
24
24
  end
25
-
25
+
26
26
  it 'should sample subjects from a random group without a user' do
27
27
  workflow.groups = { }
28
28
  workflow.groups[1] = set_klass.new
29
29
  expect(workflow.group(1)).to receive(:sample).with 3
30
30
  workflow.sample limit: 3
31
31
  end
32
-
32
+
33
33
  it 'should sample subjects from a specific group without a user' do
34
34
  3.times{ |i| workflow.groups[i] = set_klass.new }
35
35
  expect(workflow.group(1)).to receive(:sample).with 3
36
36
  workflow.sample group_id: 1, limit: 3
37
37
  end
38
-
38
+
39
39
  it 'should sample subjects from a random group for a user' do
40
40
  workflow.groups = { }
41
41
  workflow.groups[1] = set_klass.new
42
42
  expect(workflow.groups[1]).to receive(:subtract).with user.seen, 3
43
43
  workflow.sample user_id: 123, limit: 3
44
44
  end
45
-
45
+
46
46
  it 'should sample subjects from a specific group for a user' do
47
47
  3.times{ |i| workflow.groups[i] = set_klass.new }
48
48
  expect(workflow.group(1)).to receive(:subtract).with user.seen, 3
49
49
  workflow.sample user_id: 123, group_id: 1, limit: 3
50
50
  end
51
-
51
+
52
52
  it 'should add subjects' do
53
53
  workflow.groups[1] = set_klass.new
54
-
54
+
55
55
  if workflow.prioritized?
56
56
  expect(workflow.groups[1]).to receive(:add).with 123, 456
57
57
  workflow.add subject_id: 123, group_id: 1, priority: 456
@@ -60,13 +60,13 @@ module Cellect::Server
60
60
  workflow.add subject_id: 123, group_id: 1
61
61
  end
62
62
  end
63
-
63
+
64
64
  it 'should remove subjects' do
65
65
  workflow.groups[1] = set_klass.new
66
66
  expect(workflow.groups[1]).to receive(:remove).with 123
67
67
  workflow.remove subject_id: 123, group_id: 1
68
68
  end
69
-
69
+
70
70
  it 'should be grouped' do
71
71
  expect(workflow).to be_grouped
72
72
  end
@@ -4,7 +4,7 @@ module Cellect::Server
4
4
  describe NodeSet do
5
5
  it_behaves_like 'node set'
6
6
  let(:node_set){ Cellect::Server.node_set.actors.first }
7
-
7
+
8
8
  it 'should register this node' do
9
9
  expect(node_set.id).to eq 'node0000000000'
10
10
  expect(node_set.zk.get('/nodes/node0000000000').first).to match /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/