bumbleworks-api 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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +25 -0
  8. data/Rakefile +9 -0
  9. data/bumbleworks-api.gemspec +32 -0
  10. data/config.ru +13 -0
  11. data/lib/bumbleworks/api.rb +15 -0
  12. data/lib/bumbleworks/api/config/application.rb +10 -0
  13. data/lib/bumbleworks/api/config/routes.rb +34 -0
  14. data/lib/bumbleworks/api/controllers/application_controller.rb +6 -0
  15. data/lib/bumbleworks/api/controllers/entities_controller.rb +25 -0
  16. data/lib/bumbleworks/api/controllers/errors_controller.rb +32 -0
  17. data/lib/bumbleworks/api/controllers/expressions_controller.rb +29 -0
  18. data/lib/bumbleworks/api/controllers/processes_controller.rb +29 -0
  19. data/lib/bumbleworks/api/controllers/tasks_controller.rb +43 -0
  20. data/lib/bumbleworks/api/controllers/trackers_controller.rb +13 -0
  21. data/lib/bumbleworks/api/controllers/workers_controller.rb +51 -0
  22. data/lib/bumbleworks/api/lib/presenter.rb +32 -0
  23. data/lib/bumbleworks/api/lib/presenters/entity_class_presenter.rb +19 -0
  24. data/lib/bumbleworks/api/lib/presenters/entity_presenter.rb +13 -0
  25. data/lib/bumbleworks/api/lib/presenters/error_presenter.rb +15 -0
  26. data/lib/bumbleworks/api/lib/presenters/expression_presenter.rb +17 -0
  27. data/lib/bumbleworks/api/lib/presenters/process_presenter.rb +32 -0
  28. data/lib/bumbleworks/api/lib/presenters/task_presenter.rb +15 -0
  29. data/lib/bumbleworks/api/lib/presenters/tracker_presenter.rb +16 -0
  30. data/lib/bumbleworks/api/lib/presenters/worker_presenter.rb +30 -0
  31. data/lib/bumbleworks/api/lib/time_support.rb +23 -0
  32. data/lib/bumbleworks/api/version.rb +5 -0
  33. data/playground_setup.rb +30 -0
  34. data/spec/controllers/entities_controller_spec.rb +32 -0
  35. data/spec/controllers/errors_controller_spec.rb +42 -0
  36. data/spec/controllers/expressions_controller_spec.rb +40 -0
  37. data/spec/controllers/processes_controller_spec.rb +50 -0
  38. data/spec/controllers/tasks_controller_spec.rb +82 -0
  39. data/spec/controllers/trackers_controller_spec.rb +25 -0
  40. data/spec/controllers/workers_controller_spec.rb +113 -0
  41. data/spec/fixtures/bumbleworks_config.rb +10 -0
  42. data/spec/fixtures/entities/mock_entity.rb +38 -0
  43. data/spec/fixtures/entities/widget.rb +9 -0
  44. data/spec/fixtures/entities/widgety_fidget.rb +5 -0
  45. data/spec/fixtures/participants.rb +3 -0
  46. data/spec/fixtures/participants/naughty_participant.rb +16 -0
  47. data/spec/fixtures/processes/error_process.rb +3 -0
  48. data/spec/fixtures/processes/task_process.rb +9 -0
  49. data/spec/fixtures/processes/waiting_process.rb +8 -0
  50. data/spec/lib/presenter_spec.rb +29 -0
  51. data/spec/lib/presenters/entity_class_presenter_spec.rb +17 -0
  52. data/spec/lib/presenters/entity_presenter_spec.rb +15 -0
  53. data/spec/lib/presenters/error_presenter_spec.rb +18 -0
  54. data/spec/lib/presenters/expression_presenter_spec.rb +33 -0
  55. data/spec/lib/presenters/process_presenter_spec.rb +42 -0
  56. data/spec/lib/presenters/task_presenter_spec.rb +18 -0
  57. data/spec/lib/presenters/tracker_presenter_spec.rb +18 -0
  58. data/spec/lib/presenters/worker_presenter_spec.rb +30 -0
  59. data/spec/lib/time_support_spec.rb +42 -0
  60. data/spec/spec_helper.rb +39 -0
  61. data/spec/support/api_helper.rb +23 -0
  62. data/spec/support/process_helpers.rb +11 -0
  63. metadata +261 -0
@@ -0,0 +1,25 @@
1
+ describe Bumbleworks::Api::TrackersController do
2
+ before(:each) do
3
+ Bumbleworks.launch!('waiting_process')
4
+ wait_until { Bumbleworks::Tracker.count == 5 }
5
+ end
6
+
7
+ describe "#index" do
8
+ it "returns all existing trackers" do
9
+ get "/trackers"
10
+ expect(last_response.body).to eq(
11
+ json_presentation_of(Bumbleworks::Tracker.all, :as => 'Tracker')
12
+ )
13
+ end
14
+ end
15
+
16
+ describe "#show" do
17
+ it "returns requested instance" do
18
+ tracker = Bumbleworks::Tracker.all.first
19
+ get "/trackers/#{tracker.id}"
20
+ expect(last_response.body).to eq(
21
+ json_presentation_of(tracker, :as => 'Tracker')
22
+ )
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,113 @@
1
+ describe Bumbleworks::Api::WorkersController do
2
+ before(:each) do
3
+ Bumbleworks.start_worker!
4
+ allow(Time).to receive(:now).and_return(Time.now)
5
+ end
6
+
7
+ describe "#index" do
8
+ it "returns all workers" do
9
+ allow(Time).to receive(:now).and_return(Time.now)
10
+ workers = Bumbleworks::Worker::Info.all
11
+ expect(Bumbleworks::Worker).to receive(:refresh_worker_info)
12
+ get "/workers"
13
+ expect(last_response.body).to eq(
14
+ json_presentation_of(workers, :as => 'Worker')
15
+ )
16
+ end
17
+ end
18
+
19
+ describe "#show" do
20
+ it "returns requested worker" do
21
+ worker = Bumbleworks::Worker::Info.first
22
+ expect(Bumbleworks::Worker).to receive(:refresh_worker_info)
23
+ get "/workers/#{worker.id}"
24
+ expect(last_response.body).to eq(
25
+ json_presentation_of(worker, :as => 'Worker')
26
+ )
27
+ end
28
+ end
29
+
30
+ describe "#change_state" do
31
+ let(:worker) { Bumbleworks::Worker::Info.first }
32
+ before(:each) do
33
+ allow(Bumbleworks::Worker::Info).to receive(:[]).
34
+ with(worker.id).and_return(worker)
35
+ end
36
+
37
+ it "sends shutdown to given worker" do
38
+ expect(worker).to receive(:shutdown)
39
+ put "/workers/#{worker.id}/command/shutdown"
40
+ expect(last_response.body).to eq(
41
+ { "status" => "shutdown sent" }.to_json
42
+ )
43
+ end
44
+
45
+ it "sends pause to given worker" do
46
+ expect(worker).to receive(:pause)
47
+ put "/workers/#{worker.id}/command/pause"
48
+ expect(last_response.body).to eq(
49
+ { "status" => "pause sent" }.to_json
50
+ )
51
+ end
52
+
53
+ it "sends unpause to given worker" do
54
+ expect(worker).to receive(:unpause)
55
+ put "/workers/#{worker.id}/command/unpause"
56
+ expect(last_response.body).to eq(
57
+ { "status" => "unpause sent" }.to_json
58
+ )
59
+ end
60
+
61
+ it "returns 404 if unknown command is sent" do
62
+ put "/workers/#{worker.id}/command/dance"
63
+ expect(last_response.body).to eq(
64
+ { :error => "not_found" }.to_json
65
+ )
66
+ expect(last_response.status).to eq(404)
67
+ end
68
+ end
69
+
70
+ describe "#change_global_state" do
71
+ it "sends shutdown to all workers" do
72
+ expect(Bumbleworks::Worker).to receive(:shutdown_all)
73
+ put "/workers/command/shutdown"
74
+ expect(last_response.body).to eq(
75
+ { "status" => "shutdown sent" }.to_json
76
+ )
77
+ end
78
+
79
+ it "sends pause to all workers" do
80
+ expect(Bumbleworks::Worker).to receive(:pause_all)
81
+ put "/workers/command/pause"
82
+ expect(last_response.body).to eq(
83
+ { "status" => "pause sent" }.to_json
84
+ )
85
+ end
86
+
87
+ it "sends unpause to all workers" do
88
+ expect(Bumbleworks::Worker).to receive(:unpause_all)
89
+ put "/workers/command/unpause"
90
+ expect(last_response.body).to eq(
91
+ { "status" => "unpause sent" }.to_json
92
+ )
93
+ end
94
+
95
+ it "returns 404 if unknown command is sent" do
96
+ put "/workers/command/dance"
97
+ expect(last_response.body).to eq(
98
+ { :error => "not_found" }.to_json
99
+ )
100
+ expect(last_response.status).to eq(404)
101
+ end
102
+ end
103
+
104
+ describe "#prune_stopped_workers" do
105
+ it "prunes stale worker info" do
106
+ expect(Bumbleworks::Worker::Info).to receive(:purge_stale_worker_info)
107
+ delete "/workers/prune"
108
+ expect(last_response.body).to eq(
109
+ { :status => "pruned_stale_worker_info" }.to_json
110
+ )
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'entities/widget'
2
+ require_relative 'entities/widgety_fidget'
3
+
4
+ Bumbleworks.configure do |c|
5
+ c.storage = {}
6
+ c.root = File.dirname(__FILE__)
7
+ end
8
+
9
+ Bumbleworks.bootstrap!
10
+ Bumbleworks.initialize!
@@ -0,0 +1,38 @@
1
+ class MockEntity
2
+ class << self
3
+ def collection
4
+ @collection ||= {}
5
+ end
6
+
7
+ def all
8
+ collection.values
9
+ end
10
+
11
+ def truncate!
12
+ @collection = {}
13
+ end
14
+
15
+ def first_by_identifier(identifier)
16
+ collection[identifier.to_i]
17
+ end
18
+
19
+ def count
20
+ all.count
21
+ end
22
+ end
23
+
24
+ def initialize(identifier)
25
+ @identifier = identifier
26
+ self.class.collection[identifier] = self
27
+ end
28
+
29
+ def id
30
+ @identifier
31
+ end
32
+
33
+ def update(hsh)
34
+ hsh.each do |key, value|
35
+ self.send(:"#{key}=", value)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'mock_entity'
2
+
3
+ class Widget < MockEntity
4
+ include Bumbleworks::Entity
5
+
6
+ process :task_process
7
+
8
+ attr_accessor :task_process_identifier
9
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'mock_entity'
2
+
3
+ class WidgetyFidget < MockEntity
4
+ include Bumbleworks::Entity
5
+ end
@@ -0,0 +1,3 @@
1
+ Bumbleworks.register_participants do
2
+ fall_apart NaughtyParticipant
3
+ end
@@ -0,0 +1,16 @@
1
+ class NaughtyParticipant
2
+ class StupidError < StandardError; end
3
+ include Bumbleworks::LocalParticipant
4
+
5
+ class << self
6
+ attr_accessor :naughty_is_ok
7
+ end
8
+
9
+ def on_workitem
10
+ if self.class.naughty_is_ok
11
+ reply
12
+ else
13
+ raise StupidError, 'Oh crumb.'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ Bumbleworks.define_process do
2
+ fall_apart
3
+ end
@@ -0,0 +1,9 @@
1
+ Bumbleworks.define_process do
2
+ concurrence do
3
+ admin :task => 'do_something'
4
+ peon :task => 'do_something_dumb'
5
+ end
6
+ fall_apart
7
+ peon :task => 'meekly_do_another_thing'
8
+ admin :task => 'confront'
9
+ end
@@ -0,0 +1,8 @@
1
+ Bumbleworks.define_process do
2
+ concurrence do
3
+ wait_for_event :the_event, :where => :entities_match
4
+ await :left_tag => 'local_event'
5
+ await :error => /bad/
6
+ await :participant => %w[goose bunnies]
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ describe Bumbleworks::Api::Presenter do
2
+ describe ".from_array" do
3
+ it "maps each object to its presentation, passing in_collection arg" do
4
+ allow(described_class).to receive(:new).
5
+ with(:a, :in_collection => true).
6
+ and_return(:a_p)
7
+ allow(described_class).to receive(:new).
8
+ with(:b, :in_collection => true).
9
+ and_return(:b_p)
10
+ expect(described_class.from_array([:a, :b])).to eq(
11
+ [:a_p, :b_p]
12
+ )
13
+ end
14
+ end
15
+
16
+ describe ".present" do
17
+ it "instantiates single presenter if not given array" do
18
+ allow(described_class).to receive(:new).with(:a).and_return(:a_p)
19
+ expect(described_class.present(:a)).to eq(:a_p)
20
+ end
21
+
22
+ it "returns result of .from_array if given array" do
23
+ allow(described_class).to receive(:from_array).
24
+ with([:a, :b]).
25
+ and_return(:presented_array)
26
+ expect(described_class.present([:a, :b])).to eq(:presented_array)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ describe Bumbleworks::Api::EntityClassPresenter do
2
+ let(:presented) { Widget }
3
+ subject { described_class.new(presented) }
4
+
5
+ describe "#to_hash" do
6
+ it "returns hash representation of entity class" do
7
+ allow(presented).to receive(:count).and_return(10)
8
+ expect(subject.to_hash).to eq({
9
+ :class => 'Widget',
10
+ :count => 10,
11
+ :registered_processes => [
12
+ { :name => :task_process, :attribute => :task_process_identifier }
13
+ ]
14
+ })
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ describe Bumbleworks::Api::EntityPresenter do
2
+ let(:presented) { Widget.new(42) }
3
+ subject { described_class.new(presented) }
4
+
5
+ describe "#to_hash" do
6
+ it "returns hash representation of entity class" do
7
+ allow(presented).to receive(:processes).and_return([:a, :b])
8
+ expect(subject.to_hash).to eq({
9
+ :identifier => 42,
10
+ :name => "Widget 42",
11
+ :process_count => 2
12
+ })
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ describe Bumbleworks::Api::ErrorPresenter do
2
+ let(:presented) { Bumbleworks.errors.first }
3
+ subject { described_class.new(presented) }
4
+
5
+ describe "#to_hash" do
6
+ it "returns hash representation of error" do
7
+ process = Bumbleworks.launch!('error_process')
8
+ wait_until { Bumbleworks.errors.count == 1 }
9
+ expect(subject.to_hash).to eq({
10
+ :process_id => process.id,
11
+ :expression_id => presented.expression.expid,
12
+ :error_class_name => "NaughtyParticipant::StupidError",
13
+ :message => "Oh crumb.",
14
+ :backtrace => presented.backtrace
15
+ })
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ describe Bumbleworks::Api::ExpressionPresenter do
2
+ let(:process) { Bumbleworks.launch!('task_process') }
3
+ let(:presented) { process.expression_at_position('0_0_1') }
4
+
5
+ subject { described_class.new(presented) }
6
+
7
+ describe "#to_hash" do
8
+ it "returns hash representation of expression" do
9
+ wait_until { process.tasks.count == 2 }
10
+ expect(subject.to_hash).to eq({
11
+ :expression_id => presented.expid,
12
+ :process_id => presented.process.id,
13
+ :tree => presented.tree
14
+ })
15
+ end
16
+
17
+ context "with error" do
18
+ let(:process) { Bumbleworks.launch!('error_process') }
19
+ let(:presented) { process.expression_at_position('0_0') }
20
+
21
+ it "includes presented error" do
22
+ wait_until { process.reload.errors.count == 1 }
23
+ error = Bumbleworks.errors.first
24
+ expect(subject.to_hash).to eq({
25
+ :expression_id => presented.expid,
26
+ :process_id => presented.process.id,
27
+ :tree => presented.tree,
28
+ :error => Bumbleworks::Api::ErrorPresenter.present(error).to_hash
29
+ })
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ describe Bumbleworks::Api::ProcessPresenter do
2
+ let(:presented) { Bumbleworks.launch!('task_process', :entity => widget) }
3
+ let(:widget) { Widget.new(41) }
4
+
5
+ describe "#to_hash" do
6
+ context "not in collection" do
7
+ subject { described_class.new(presented) }
8
+
9
+ it "returns full hash representation of process" do
10
+ wait_until { presented.tasks.count == 2 }
11
+ expect(subject.to_hash).to eq({
12
+ :id => presented.id,
13
+ :definition_name => "task_process",
14
+ :subscribed_events => [],
15
+ :entity => Bumbleworks::Api::EntityPresenter.present(widget).to_hash,
16
+ :entity_name => widget.to_s,
17
+ :original_tree => presented.original_tree
18
+ })
19
+ end
20
+
21
+ it "doesn't include entity if process has none" do
22
+ wait_until { presented.tasks.count == 2 }
23
+ allow(presented).to receive(:entity).and_return(nil)
24
+ expect(subject.to_hash.keys).not_to include(:entity)
25
+ end
26
+ end
27
+
28
+ context "in collection" do
29
+ subject { described_class.new(presented, :in_collection => true) }
30
+
31
+ it "returns minimal hash representation of process" do
32
+ wait_until { presented.tasks.count == 2 }
33
+ expect(subject.to_hash).to eq({
34
+ :id => presented.id,
35
+ :definition_name => "task_process",
36
+ :entity_name => widget.to_s,
37
+ :subscribed_events => []
38
+ })
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ describe Bumbleworks::Api::TaskPresenter do
2
+ let(:process) { Bumbleworks.launch!('task_process') }
3
+ let(:presented) { process.tasks.next_available }
4
+ subject { described_class.new(presented) }
5
+
6
+ describe "#to_hash" do
7
+ it "returns hash representation of entity class" do
8
+ presented.claim('oscar')
9
+ expect(subject.to_hash).to eq({
10
+ :id => presented.id,
11
+ :name => presented.to_s,
12
+ :claimant => 'oscar',
13
+ :process_id => presented.wfid,
14
+ :role => presented.role
15
+ })
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ describe Bumbleworks::Api::TrackerPresenter do
2
+ let(:process) { Bumbleworks.launch!('waiting_process') }
3
+ let(:presented) { Bumbleworks::Tracker.all.first }
4
+ subject { described_class.new(presented) }
5
+
6
+ describe "#to_hash" do
7
+ it "returns hash representation of entity class" do
8
+ expect(subject.to_hash).to eq({
9
+ :id => presented.id,
10
+ :waiting_expression => presented.waiting_expression,
11
+ :original_hash => presented.original_hash,
12
+ :action => presented.action,
13
+ :conditions => presented.conditions,
14
+ :process_id => presented.wfid
15
+ })
16
+ end
17
+ end
18
+ end