nestene 0.1.0

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