cellect-server 0.1.3 → 1.0.0

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.
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}/