nestene 0.1.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 (87) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +31 -0
  7. data/Rakefile +2 -0
  8. data/TODO.txt +7 -0
  9. data/api.rb +8 -0
  10. data/autons.rb +13 -0
  11. data/bin/nestene.rb +5 -0
  12. data/config.ru +21 -0
  13. data/features/api/api_create_auton.feature +6 -0
  14. data/features/api/api_credentials.feature +11 -0
  15. data/features/api/api_list_of_autons.feature +6 -0
  16. data/features/callback.feature +13 -0
  17. data/features/create_auton.feature +11 -0
  18. data/features/credentials.feature +7 -0
  19. data/features/execute_step.feature +59 -0
  20. data/features/restart.feature +7 -0
  21. data/features/schedule_steps.feature +7 -0
  22. data/features/step_definitions/api/api_create_auton_steps.rb +16 -0
  23. data/features/step_definitions/api/api_credentials_steps.rb +22 -0
  24. data/features/step_definitions/api/api_list_of_autons_steps.rb +8 -0
  25. data/features/step_definitions/callback_steps.rb +75 -0
  26. data/features/step_definitions/create_auton_steps.rb +32 -0
  27. data/features/step_definitions/credentials_steps.rb +24 -0
  28. data/features/step_definitions/execute_step_steps.rb +177 -0
  29. data/features/step_definitions/restart_steps.rb +5 -0
  30. data/features/step_definitions/schedule_steps_steps.rb +29 -0
  31. data/features/step_definitions/time_delayed_steps_steps.rb +103 -0
  32. data/features/step_definitions/ui/create_auton_steps.rb +21 -0
  33. data/features/step_definitions/ui/list_of_autons_steps.rb +18 -0
  34. data/features/step_definitions/ui/schedule_auton_step_steps.rb +64 -0
  35. data/features/step_definitions/ui/show_auton_steps.rb +49 -0
  36. data/features/support/env.rb +36 -0
  37. data/features/time_delayed_steps.feature +28 -0
  38. data/features/ui/create_auton.feature +7 -0
  39. data/features/ui/list_of_autons.feature +7 -0
  40. data/features/ui/schedule_auton_step.feature +12 -0
  41. data/features/ui/show_auton.feature +14 -0
  42. data/lib/nestene/actor/auton_queue.rb +87 -0
  43. data/lib/nestene/actor/auton_storage.rb +50 -0
  44. data/lib/nestene/actor/core.rb +150 -0
  45. data/lib/nestene/actor/delayed_scheduler.rb +76 -0
  46. data/lib/nestene/auton_context.rb +31 -0
  47. data/lib/nestene/auton_execution_queue.rb +43 -0
  48. data/lib/nestene/auton_state.rb +27 -0
  49. data/lib/nestene/callback.rb +7 -0
  50. data/lib/nestene/delayed_method.rb +17 -0
  51. data/lib/nestene/executed_method.rb +29 -0
  52. data/lib/nestene/executing_method.rb +23 -0
  53. data/lib/nestene/execution_error.rb +39 -0
  54. data/lib/nestene/scheduled_method.rb +21 -0
  55. data/lib/nestene/storage.rb +72 -0
  56. data/lib/nestene/ui/app.rb +103 -0
  57. data/lib/nestene/ui/public/app/application.js +75 -0
  58. data/lib/nestene/ui/public/vendor/angular-ui-sortable/sortable.min.js +1 -0
  59. data/lib/nestene/ui/public/vendor/angular.min.js +248 -0
  60. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.css +347 -0
  61. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.min.css +7 -0
  62. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.css +5785 -0
  63. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.min.css +7 -0
  64. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  65. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
  66. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  67. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  68. data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.js +1951 -0
  69. data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.min.js +6 -0
  70. data/lib/nestene/ui/public/vendor/jquery-ui.min.js +13 -0
  71. data/lib/nestene/ui/public/vendor/jquery.bootstrap-growl.min.js +1 -0
  72. data/lib/nestene/ui/public/vendor/jquery.min.js +5 -0
  73. data/lib/nestene/ui/public/vendor/json-editor/directives.js +245 -0
  74. data/lib/nestene/ui/public/vendor/json-editor/styles.css +165 -0
  75. data/lib/nestene/ui/views/auton.haml +42 -0
  76. data/lib/nestene/ui/views/auton_form.haml +8 -0
  77. data/lib/nestene/ui/views/index.haml +35 -0
  78. data/lib/nestene/ui/views/layout.haml +40 -0
  79. data/lib/nestene/ui/views/schedule_step.haml +14 -0
  80. data/lib/nestene/version.rb +3 -0
  81. data/lib/nestene.rb +50 -0
  82. data/nestene.gemspec +39 -0
  83. data/spec/nestene/auton_execution_queue_spec.rb +78 -0
  84. data/spec/nestene/nestene_spec.rb +137 -0
  85. data/spec/nestene/storage_spec.rb +125 -0
  86. data/spec/spec_helper.rb +2 -0
  87. metadata +373 -0
@@ -0,0 +1,29 @@
1
+ Given(/^I have an Auton that has two steps, first step scheduling the next step$/) do
2
+ class ScheduleSecondStepAuton
3
+ include StructureMapper::Hash
4
+
5
+ def first
6
+ context.schedule_step(:second)
7
+ end
8
+
9
+ def second
10
+ 'ok'
11
+ end
12
+
13
+ attr_accessor :context
14
+
15
+ attribute foo: Fixnum
16
+ end
17
+ @auton_type="ScheduleSecondStepAuton"
18
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type)
19
+ end
20
+
21
+ When(/^I schedule the first step that returns the uuid of the second scheduled step$/) do
22
+ @step_execution_id = Celluloid::Actor[:nestene_core].schedule_step @auton_id, :first
23
+ @step_execution_id = Celluloid::Actor[:nestene_core].wait_for_execution_result(@auton_id, @step_execution_id)
24
+ end
25
+
26
+ When(/^I wait for the second step to finish$/) do
27
+ @execution_result = Celluloid::Actor[:nestene_core].wait_for_execution_result(@auton_id, @step_execution_id)
28
+ end
29
+
@@ -0,0 +1,103 @@
1
+ Given(/^an Auton that schedules an one off delayed step in (\d+)\.(\d+) second$/) do |arg1, arg2|
2
+ class TimeDelayedStepSchedulingAuton
3
+ include StructureMapper::Hash
4
+
5
+ def first
6
+ context.schedule_delayed_step(0.1, :second)
7
+ end
8
+
9
+ def second
10
+ 'ok'
11
+ end
12
+
13
+ attr_accessor :context
14
+
15
+ attribute foo: Fixnum
16
+ end
17
+ @auton_type="TimeDelayedStepSchedulingAuton"
18
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type)
19
+ end
20
+
21
+ When(/^I execute the step that schedules the delayed step$/) do
22
+ @start_time = Time.now
23
+ @step_execution_id = Celluloid::Actor[:nestene_core].schedule_step @auton_id, :first
24
+ @second_step_execution_id = Celluloid::Actor[:nestene_core].wait_for_execution_result(@auton_id, @step_execution_id)
25
+ end
26
+
27
+ Then(/^the elapsed time between first and sencond step should be at least (\d+\.\d+) secods$/) do |seconds_string|
28
+ raise @execution_exception if @execution_exception
29
+ expect(@execution_result).to eq('ok')
30
+ expect(Time.now - @start_time).to be >= seconds_string.to_f
31
+ end
32
+
33
+ When(/^I cancel the time delayed step$/) do
34
+ Celluloid::Actor[:nestene_core].cancel_delayed_step @auton_id, @second_step_execution_id
35
+ end
36
+
37
+ When(/^I wait for (\d+\.\d+) seconds$/) do |seconds|
38
+ sleep seconds.to_f
39
+ end
40
+
41
+ Then(/^the scheduled step should not be executed$/) do
42
+ expect(Celluloid::Actor[:nestene_core].get_state(@auton_id).queue.executed.size).to eq(1)
43
+ end
44
+
45
+
46
+ Given(/^an Auton that schedules an repeating every (\d+\.\d+) seconds delayed step in (\d+\.\d+) second$/) do |every, delay|
47
+ class RepeatingTimeDelayedStepdSchedulingAuton
48
+ include StructureMapper::Hash
49
+
50
+ def first
51
+ context.schedule_repeating_delayed_step(0.1, 0.1, :second)
52
+ end
53
+
54
+ def second
55
+ 'ok'
56
+ end
57
+
58
+ attr_accessor :context
59
+
60
+ attribute foo: Fixnum
61
+ end
62
+ @auton_type="RepeatingTimeDelayedStepdSchedulingAuton"
63
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type)
64
+ end
65
+
66
+ Then(/^the scheduled step should be executed twice$/) do
67
+ expect(Celluloid::Actor[:nestene_core].get_state(@auton_id).queue.executed.map{|m| m.name}).to eq([:first,:second,:second])
68
+ end
69
+
70
+
71
+ Given(/^an Auton that schedules an one off delayed step in (\d+\.\d+) seconds and has a failing step$/) do |delay|
72
+ class TimeDelayedAndFailingStepSchedulingAuton
73
+ include StructureMapper::Hash
74
+
75
+ def first
76
+ context.schedule_delayed_step(0.1, :second)
77
+ end
78
+
79
+ def fail
80
+ raise "fail!"
81
+ end
82
+
83
+ def second
84
+ 'ok'
85
+ end
86
+
87
+ attr_accessor :context
88
+
89
+ attribute foo: Fixnum
90
+ end
91
+ @auton_type="TimeDelayedAndFailingStepSchedulingAuton"
92
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type)
93
+ end
94
+
95
+ When(/^I execute the failing step$/) do
96
+ @step_execution_id = Celluloid::Actor[:nestene_core].schedule_step @auton_id, :fail
97
+ expect {Celluloid::Actor[:nestene_core].wait_for_execution_result(@auton_id, @step_execution_id)}.to raise_error
98
+ end
99
+
100
+ Then(/^the delayed step should not be executed$/) do
101
+ expect(Celluloid::Actor[:nestene_core].get_state(@auton_id).queue.executed.map{|m| m.name}).to eq([:first,:fail])
102
+ end
103
+
@@ -0,0 +1,21 @@
1
+ Given(/^there is a simple Auton implementation$/) do
2
+ class EmptyAuton
3
+ include StructureMapper::Hash
4
+
5
+ attribute foo: String
6
+ end
7
+ @auton_type="EmptyAuton"
8
+ end
9
+
10
+ When(/^I create that simple Auton$/) do
11
+ visit '/'
12
+ click_on 'Create Auton'
13
+ fill_in 'auton_type', with: @auton_type
14
+ click_on 'create'
15
+ end
16
+
17
+ Then(/^the list of Autons should contain the auton id$/) do
18
+ get '/autons.json'
19
+ expect(last_response).to be_ok
20
+ expect(JSON.parse(last_response.body)).not_to be_empty
21
+ end
@@ -0,0 +1,18 @@
1
+ Given(/^I have one started auton$/) do
2
+ class EmptyAuton
3
+ include StructureMapper::Hash
4
+
5
+ attribute foo: String
6
+ end
7
+ @auton_type="EmptyAuton"
8
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type, 'empty_auton')
9
+ end
10
+
11
+ When(/^I visit the index page$/) do
12
+ visit '/'
13
+ end
14
+
15
+ Then(/^I should see the id of that auton$/) do
16
+ expect(page).to have_content 'empty_auton'
17
+ end
18
+
@@ -0,0 +1,64 @@
1
+ Given(/^I have a running auton with one defined step without parameters$/) do
2
+ class EmptyAuton
3
+ include StructureMapper::Hash
4
+
5
+ def initialize
6
+ end
7
+
8
+ def start
9
+ self.started = true
10
+ end
11
+
12
+ attribute started: Boolean
13
+ end
14
+
15
+ @auton_type="EmptyAuton"
16
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type, 'empty_auton')
17
+ end
18
+
19
+ When(/^I schedule the step$/) do
20
+ visit '/auton/%s' % @auton_id
21
+ click_on 'schedule_step'
22
+ select 'start'
23
+ click_on 'schedule'
24
+ @step_execution_id = page.response_headers['X-AUTON-STEP-ID']
25
+ raise 'could not find step id' unless @step_execution_id
26
+ end
27
+
28
+
29
+ Given(/^I have a running auton with one defined step with two parameters$/) do
30
+ class EmptyAutonWithParams
31
+ include StructureMapper::Hash
32
+
33
+ def initialize
34
+ end
35
+
36
+ def start p1, p2
37
+ self.p1 = p1
38
+ self.p2 = p2
39
+ end
40
+
41
+ attribute p1: String
42
+ attribute p2: String
43
+ end
44
+
45
+ @auton_type="EmptyAutonWithParams"
46
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton(@auton_type)
47
+ end
48
+
49
+ When(/^I schedule the step with parameters$/) do
50
+ visit '/auton/%s' % @auton_id
51
+ click_on 'schedule_step'
52
+ select 'start'
53
+ click_on 'schedule'
54
+ # select 'start'
55
+ fill_in 'parameters[0]', with: 'p1'
56
+ fill_in 'parameters[1]', with: 'p1'
57
+ click_on 'schedule'
58
+ @step_execution_id = page.response_headers['X-AUTON-STEP-ID']
59
+ raise 'could not find step id' unless @step_execution_id
60
+ end
61
+
62
+ Then(/^the executed steps should include the step with the parameters$/) do
63
+ expect(Celluloid::Actor["storage:%s" % @auton_id].get.queue.executed.first.uuid).to eq(@step_execution_id)
64
+ end
@@ -0,0 +1,49 @@
1
+ Given(/^I have one running Auton$/) do
2
+ class SomeCoolAuton
3
+ include StructureMapper::Hash
4
+
5
+ def start
6
+ end
7
+
8
+ attribute foo: String
9
+ end
10
+ @auton_id = Celluloid::Actor[:nestene_core].create_auton "SomeCoolAuton"
11
+ end
12
+
13
+ When(/^I view the Auton$/) do
14
+ visit '/'
15
+ click_on @auton_id
16
+ end
17
+
18
+ Then(/^I should see Auton's id/) do
19
+ expect(page).to have_content @auton_id
20
+ end
21
+
22
+
23
+ Then(/^I should see Auton's status$/) do
24
+ expect(page.find('#auton_state').text).to eq('ready')
25
+ end
26
+
27
+ Then(/^I should see Auton's state$/) do
28
+ expect(page).to have_css('#current_state')
29
+ end
30
+
31
+ Then(/^I should see Auton's queue$/) do
32
+ expect(page).to have_css('#queue')
33
+ end
34
+
35
+ Then(/^I should see Auton's executed steps$/) do
36
+ expect(page).to have_css('#executed')
37
+ end
38
+
39
+ Then(/^I should see Auton's current step$/) do
40
+ expect(page).to have_css('#executing')
41
+ end
42
+
43
+ Then(/^I should see Auton's scheduled steps$/) do
44
+ expect(page).to have_css('#to_execute')
45
+ end
46
+
47
+ Then(/^I should see Auton's delayed steps$/) do
48
+ pending # express the regexp above with the code you wish you had
49
+ end
@@ -0,0 +1,36 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ require "rack/test"
3
+ require 'nestene'
4
+ require 'capybara/cucumber'
5
+ require 'capybara/poltergeist'
6
+ require 'pry'
7
+ Capybara.javascript_driver = :poltergeist
8
+
9
+ Capybara.app = Nestene::Ui::App
10
+
11
+ Capybara.register_driver :rack_test do |app|
12
+ Capybara::RackTest::Driver.new app, \
13
+ redirect_limit: 15,
14
+ follow_redirects: false,
15
+ respect_data_method: true
16
+ end
17
+
18
+ Before do
19
+ Celluloid.shutdown
20
+ Celluloid.boot
21
+ @storage = Nestene::MemoryStorage.new
22
+ Nestene::start_nestene(@storage)
23
+ end
24
+
25
+ class MyWorld
26
+ include Rack::Test::Methods
27
+ def app
28
+ Nestene::Ui::App
29
+ end
30
+ end
31
+
32
+ World do
33
+ MyWorld.new
34
+ end
35
+
36
+
@@ -0,0 +1,28 @@
1
+ Feature: time delayed steps
2
+
3
+ Scenario: scheduling one off time delayed step
4
+ Given an Auton that schedules an one off delayed step in 0.1 second
5
+ When I execute the step that schedules the delayed step
6
+ And I wait for the second step to execute
7
+ Then the elapsed time between first and sencond step should be at least 0.1 secods
8
+
9
+ Scenario: un-scheduling one off time delayed step
10
+ Given an Auton that schedules an one off delayed step in 0.1 second
11
+ When I execute the step that schedules the delayed step
12
+ And I cancel the time delayed step
13
+ And I wait for 0.15 seconds
14
+ Then the scheduled step should not be executed
15
+
16
+ Scenario: scheduling repeating delayed step
17
+ Given an Auton that schedules an repeating every 0.1 seconds delayed step in 0.1 second
18
+ When I execute the step that schedules the delayed step
19
+ And I wait for 0.21 seconds
20
+ Then the scheduled step should be executed twice
21
+
22
+ Scenario: suspending one off delayed step when auton is in error state
23
+ Given an Auton that schedules an one off delayed step in 0.1 seconds and has a failing step
24
+ When I execute the step that schedules the delayed step
25
+ When I execute the failing step
26
+ And I wait for 0.11 seconds
27
+ Then the delayed step should not be executed
28
+
@@ -0,0 +1,7 @@
1
+ Feature: create auton
2
+
3
+ @javascript
4
+ Scenario: create auton
5
+ Given there is a simple Auton implementation
6
+ When I create that simple Auton
7
+ Then the list of Autons should contain the auton id
@@ -0,0 +1,7 @@
1
+ Feature: List of autons
2
+
3
+ @javascript
4
+ Scenario: Listing Running Autons
5
+ Given I have one started auton
6
+ When I visit the index page
7
+ Then I should see the id of that auton
@@ -0,0 +1,12 @@
1
+ Feature: schedule auton step
2
+
3
+ Scenario: schedule steps without parameters
4
+ Given I have a running auton with one defined step without parameters
5
+ When I schedule the step
6
+ And I wait for the step to execute
7
+
8
+ Scenario: schedule steps with parameters
9
+ Given I have a running auton with one defined step with two parameters
10
+ When I schedule the step with parameters
11
+ And I wait for the step to execute
12
+ Then the executed steps should include the step with the parameters
@@ -0,0 +1,14 @@
1
+ Feature: show auton
2
+
3
+ @javascript
4
+ Scenario: show running Auton
5
+ Given I have one running Auton
6
+ When I view the Auton
7
+ Then I should see Auton's id
8
+ And I should see Auton's status
9
+ And I should see Auton's state
10
+ And I should see Auton's queue
11
+ And I should see Auton's executed steps
12
+ And I should see Auton's current step
13
+ And I should see Auton's scheduled steps
14
+ # And I should see Auton's delayed steps
@@ -0,0 +1,87 @@
1
+ require 'celluloid'
2
+
3
+ module Nestene
4
+ module Actor
5
+ class AutonQueue
6
+ include Celluloid
7
+ include Celluloid::Notifications
8
+
9
+ def initialize
10
+ subscribe('state_update', :execute_next_step)
11
+ end
12
+
13
+
14
+ def execute_next_step topic, auton_id, state
15
+ executing = nil
16
+ type = nil
17
+ ser = nil
18
+ running = false
19
+
20
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
21
+ running = state.state == :ready && !state.queue.to_execute.empty?
22
+ if running
23
+ nx = state.queue.to_execute.shift
24
+ executing = ExecutingMethod.new(nx)
25
+ state.queue.currently_executing = executing
26
+ type = state.type
27
+ ser = state.serialized
28
+ end
29
+ end
30
+
31
+ return unless running
32
+
33
+ instance = Nestene::class_from_string(type).from_structure(ser)
34
+
35
+ if instance.public_methods.include?(:context=)
36
+ instance.public_send(:context=, AutonContext.new(auton_id))
37
+ end
38
+
39
+ future = Celluloid::Future.new do
40
+ instance.method(executing.name).call(*executing.parameters)
41
+ end
42
+
43
+ result = exception = nil
44
+
45
+ begin
46
+ result = future.value
47
+ rescue Exception => e
48
+ exception = e
49
+ end
50
+
51
+ executed = ExecutedMethod.new(executing)
52
+
53
+ executed.result = result
54
+
55
+ if exception
56
+ executed.error = exception
57
+ end
58
+
59
+ if executed.callback
60
+ if exception
61
+ Celluloid::Actor[:nestene_core].async.schedule_step executed.callback.auton_id, ("%s_error" % executed.callback.name.to_s), [exception.class.name, exception.message]
62
+ else
63
+ Celluloid::Actor[:nestene_core].async.schedule_step executed.callback.auton_id, executed.callback.name, result
64
+ end
65
+ end
66
+
67
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
68
+ state.queue.currently_executing = nil
69
+ state.serialized = instance.to_structure
70
+ if exception
71
+ if instance.methods.include?(:handle_exception) && executing.name != :handle_exception
72
+ method = ScheduledMethod.new
73
+ method.name = :handle_exception
74
+ method.parameters = [exception.class.name, exception.message, executing.name, executing.parameters]
75
+ method.uuid = SecureRandom.uuid
76
+ state.queue.to_execute.unshift method
77
+ else
78
+ state.queue.failed = true
79
+ end
80
+ end
81
+ state.queue.executed << executed
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,50 @@
1
+ require 'celluloid'
2
+
3
+ module Nestene
4
+ module Actor
5
+ class AutonStorage
6
+ include Celluloid
7
+ include Celluloid::Notifications
8
+
9
+ exclusive :create, :get, :update
10
+
11
+ execute_block_on_receiver :update
12
+
13
+ def initialize(auton_id, storage)
14
+ @auton_id = auton_id
15
+ @storage = storage
16
+ state = get
17
+ publish('state_update', @auton_id, state) if state
18
+ end
19
+
20
+ def create type
21
+ state = AutonState.new
22
+ state.type = type
23
+ instance = Nestene::class_from_string(type).new
24
+ state.serialized = instance.to_structure
25
+ @storage.store(@auton_id,state.to_structure)
26
+ publish('state_update', @auton_id, state)
27
+ rescue Exception => e
28
+ abort e
29
+ end
30
+
31
+ def get
32
+ structure = @storage.load(@auton_id)
33
+ structure ? AutonState.from_structure(structure) : nil
34
+ end
35
+
36
+ def update &block
37
+ before = @storage.load(@auton_id)
38
+ state = AutonState.from_structure(@storage.load(@auton_id))
39
+ result = block.call(state)
40
+ after = state.to_structure
41
+ @storage.store(@auton_id,after)
42
+ publish('state_update', @auton_id, state) if before != after
43
+ result
44
+ rescue Exception => e
45
+ abort e
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,150 @@
1
+ require 'celluloid'
2
+
3
+ module Nestene
4
+ module Actor
5
+ class Core
6
+
7
+ include Celluloid
8
+ include Celluloid::Notifications
9
+
10
+ def initialize(storage)
11
+ subscribe('state_update', :notify_waiters)
12
+ @execution_futures={}
13
+ @storage = storage
14
+ storage.list.each do |auton_id|
15
+ Celluloid::Actor["storage:%s" % auton_id] = AutonStorage.new(auton_id, @storage)
16
+ end
17
+ end
18
+
19
+ def auton_names
20
+ @storage.list
21
+ end
22
+
23
+ def get_credentials
24
+ @storage.load('__credentials__') || {}
25
+ end
26
+
27
+ def set_credentials credentials
28
+ @storage.store('__credentials__',credentials)
29
+ end
30
+
31
+ def create_auton type, auton_id=SecureRandom.uuid
32
+ storage = AutonStorage.new(auton_id, @storage)
33
+ Celluloid::Actor["storage:%s" % auton_id] = storage
34
+ storage.create(type)
35
+ auton_id
36
+ rescue Exception => e
37
+ abort e
38
+ end
39
+
40
+ def get_state auton_id
41
+ Celluloid::Actor["storage:%s" % auton_id].get
42
+ rescue Exception => e
43
+ abort e
44
+ end
45
+
46
+ def schedule_step auton_id, step_name, parameters=[], callback_auton_id=nil, callback_method=nil
47
+ step_id = SecureRandom.uuid
48
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
49
+ method = ScheduledMethod.new
50
+ method.name = step_name
51
+ method.parameters = parameters
52
+ method.uuid = step_id
53
+ if callback_auton_id && callback_method
54
+ method.callback = Callback.new
55
+ method.callback.name = callback_method
56
+ method.callback.auton_id = callback_auton_id
57
+ end
58
+ state.queue.to_execute << method
59
+ end
60
+ step_id
61
+ end
62
+
63
+ def cancel_delayed_step auton_id, step_id
64
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
65
+ delayed = state.queue.remove_delayed_method step_id
66
+ end
67
+ end
68
+
69
+ def schedule_delayed_step auton_id, delay , name, parameters=[]
70
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
71
+ delayed = state.queue.add_delayed_method name, parameters, delay
72
+ delayed.uuid
73
+ end
74
+ end
75
+
76
+
77
+ def schedule_repeating_delayed_step auton_id, every, delay, name, parameters=[]
78
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
79
+ delayed = state.queue.add_delayed_method name, parameters, delay, every
80
+ delayed.uuid
81
+ end
82
+ end
83
+
84
+ def repeat_step auton_id, method_uuid
85
+ step_id = SecureRandom.uuid
86
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
87
+
88
+ original_step = state.queue.executed.find{|m| m.uuid == method_uuid}
89
+
90
+ method = ScheduledMethod.new
91
+ method.name = original_step.name
92
+ method.parameters = original_step.parameters
93
+ method.uuid = step_id
94
+ state.queue.to_execute.unshift method
95
+ state.queue.failed = false
96
+ end
97
+ step_id
98
+ end
99
+
100
+ def resume auton_id
101
+ Celluloid::Actor["storage:%s" % auton_id].update do |state|
102
+ state.queue.failed = false
103
+ end
104
+ end
105
+
106
+
107
+ def wait_for_execution_result auton_id, method_uuid
108
+
109
+ future = nil
110
+
111
+ executed = nil
112
+
113
+
114
+ exclusive do
115
+
116
+ state = Celluloid::Actor["storage:%s" % auton_id].get
117
+
118
+ unless @execution_futures.has_key?(auton_id)
119
+ @execution_futures[auton_id] = Celluloid::Future.new
120
+ end
121
+
122
+ future = @execution_futures[auton_id]
123
+
124
+ executed = state ? state.queue.executed.find{|m| m.uuid == method_uuid} : nil
125
+
126
+ end
127
+
128
+ until executed
129
+
130
+ executed = future.value
131
+ state = Celluloid::Actor["storage:%s" % auton_id].get
132
+ executed = state.queue.executed.find{|m| m.uuid == method_uuid}
133
+ end
134
+
135
+ if executed.error
136
+ abort executed.error
137
+ else
138
+ return executed.result
139
+ end
140
+
141
+ end
142
+
143
+ def notify_waiters topic, auton_id, state
144
+ future = @execution_futures.delete(auton_id)
145
+ future.signal(SelfValue.new) if future
146
+ end
147
+
148
+ end
149
+ end
150
+ end