dynflow 0.1.0 → 0.2.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.
- data/.gitignore +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +0 -10
- data/MIT-LICENSE +1 -1
- data/README.md +99 -37
- data/Rakefile +2 -6
- data/doc/images/logo.png +0 -0
- data/dynflow.gemspec +10 -1
- data/examples/generate_work_for_daemon.rb +24 -0
- data/examples/orchestrate.rb +121 -0
- data/examples/run_daemon.rb +17 -0
- data/examples/web_console.rb +29 -0
- data/lib/dynflow.rb +27 -6
- data/lib/dynflow/action.rb +185 -77
- data/lib/dynflow/action/cancellable_polling.rb +18 -0
- data/lib/dynflow/action/finalize_phase.rb +18 -0
- data/lib/dynflow/action/flow_phase.rb +44 -0
- data/lib/dynflow/action/format.rb +46 -0
- data/lib/dynflow/action/missing.rb +26 -0
- data/lib/dynflow/action/plan_phase.rb +85 -0
- data/lib/dynflow/action/polling.rb +49 -0
- data/lib/dynflow/action/presenter.rb +51 -0
- data/lib/dynflow/action/progress.rb +62 -0
- data/lib/dynflow/action/run_phase.rb +43 -0
- data/lib/dynflow/action/suspended.rb +21 -0
- data/lib/dynflow/clock.rb +133 -0
- data/lib/dynflow/daemon.rb +29 -0
- data/lib/dynflow/execution_plan.rb +285 -33
- data/lib/dynflow/execution_plan/dependency_graph.rb +29 -0
- data/lib/dynflow/execution_plan/output_reference.rb +52 -0
- data/lib/dynflow/execution_plan/steps.rb +12 -0
- data/lib/dynflow/execution_plan/steps/abstract.rb +121 -0
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +52 -0
- data/lib/dynflow/execution_plan/steps/error.rb +33 -0
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +23 -0
- data/lib/dynflow/execution_plan/steps/plan_step.rb +81 -0
- data/lib/dynflow/execution_plan/steps/run_step.rb +21 -0
- data/lib/dynflow/executors.rb +9 -0
- data/lib/dynflow/executors/abstract.rb +32 -0
- data/lib/dynflow/executors/parallel.rb +88 -0
- data/lib/dynflow/executors/parallel/core.rb +119 -0
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +120 -0
- data/lib/dynflow/executors/parallel/flow_manager.rb +48 -0
- data/lib/dynflow/executors/parallel/pool.rb +102 -0
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +63 -0
- data/lib/dynflow/executors/parallel/sequence_cursor.rb +97 -0
- data/lib/dynflow/executors/parallel/sequential_manager.rb +81 -0
- data/lib/dynflow/executors/parallel/work_queue.rb +44 -0
- data/lib/dynflow/executors/parallel/worker.rb +30 -0
- data/lib/dynflow/executors/remote_via_socket.rb +38 -0
- data/lib/dynflow/executors/remote_via_socket/core.rb +150 -0
- data/lib/dynflow/flows.rb +13 -0
- data/lib/dynflow/flows/abstract.rb +36 -0
- data/lib/dynflow/flows/abstract_composed.rb +104 -0
- data/lib/dynflow/flows/atom.rb +36 -0
- data/lib/dynflow/flows/concurrence.rb +28 -0
- data/lib/dynflow/flows/sequence.rb +13 -0
- data/lib/dynflow/future.rb +173 -0
- data/lib/dynflow/listeners.rb +7 -0
- data/lib/dynflow/listeners/abstract.rb +13 -0
- data/lib/dynflow/listeners/serialization.rb +41 -0
- data/lib/dynflow/listeners/socket.rb +88 -0
- data/lib/dynflow/logger_adapters.rb +8 -0
- data/lib/dynflow/logger_adapters/abstract.rb +30 -0
- data/lib/dynflow/logger_adapters/delegator.rb +13 -0
- data/lib/dynflow/logger_adapters/formatters.rb +8 -0
- data/lib/dynflow/logger_adapters/formatters/abstract.rb +33 -0
- data/lib/dynflow/logger_adapters/formatters/exception.rb +15 -0
- data/lib/dynflow/logger_adapters/simple.rb +59 -0
- data/lib/dynflow/micro_actor.rb +102 -0
- data/lib/dynflow/persistence.rb +53 -0
- data/lib/dynflow/persistence_adapters.rb +6 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +56 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +160 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +52 -0
- data/lib/dynflow/serializable.rb +66 -0
- data/lib/dynflow/simple_world.rb +18 -0
- data/lib/dynflow/stateful.rb +40 -0
- data/lib/dynflow/testing.rb +32 -0
- data/lib/dynflow/testing/assertions.rb +64 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +40 -0
- data/lib/dynflow/testing/dummy_executor.rb +29 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +18 -0
- data/lib/dynflow/testing/dummy_step.rb +19 -0
- data/lib/dynflow/testing/dummy_world.rb +33 -0
- data/lib/dynflow/testing/factories.rb +83 -0
- data/lib/dynflow/testing/managed_clock.rb +23 -0
- data/lib/dynflow/testing/mimic.rb +38 -0
- data/lib/dynflow/transaction_adapters.rb +9 -0
- data/lib/dynflow/transaction_adapters/abstract.rb +26 -0
- data/lib/dynflow/transaction_adapters/active_record.rb +27 -0
- data/lib/dynflow/transaction_adapters/none.rb +12 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +277 -0
- data/lib/dynflow/world.rb +168 -0
- data/test/action_test.rb +89 -11
- data/test/clock_test.rb +59 -0
- data/test/code_workflow_example.rb +382 -0
- data/test/execution_plan_test.rb +195 -64
- data/test/executor_test.rb +692 -0
- data/test/persistance_adapters_test.rb +173 -0
- data/test/test_helper.rb +316 -1
- data/test/testing_test.rb +148 -0
- data/test/web_console_test.rb +38 -0
- data/web/assets/javascripts/application.js +25 -0
- data/web/assets/stylesheets/application.css +101 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +1109 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +6167 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +2280 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +6 -0
- data/web/assets/vendor/google-code-prettify/lang-basic.js +3 -0
- data/web/assets/vendor/google-code-prettify/prettify.css +1 -0
- data/web/assets/vendor/google-code-prettify/prettify.js +30 -0
- data/web/assets/vendor/google-code-prettify/run_prettify.js +34 -0
- data/web/assets/vendor/jquery/jquery.js +9807 -0
- data/web/views/flow.erb +19 -0
- data/web/views/flow_step.erb +31 -0
- data/web/views/index.erb +39 -0
- data/web/views/layout.erb +20 -0
- data/web/views/plan_step.erb +11 -0
- data/web/views/show.erb +54 -0
- metadata +250 -11
- data/examples/events.rb +0 -71
- data/examples/workflow.rb +0 -140
- data/lib/dynflow/bus.rb +0 -168
- data/lib/dynflow/dispatcher.rb +0 -36
- data/lib/dynflow/logger.rb +0 -34
- data/lib/dynflow/step.rb +0 -234
- data/test/bus_test.rb +0 -150
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Demo for Dynflow web console
|
|
2
|
+
# usage: ruby web_console.rb
|
|
3
|
+
|
|
4
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
|
5
|
+
|
|
6
|
+
require 'dynflow'
|
|
7
|
+
require_relative 'orchestrate'
|
|
8
|
+
|
|
9
|
+
world = Dynflow::SimpleWorld.new
|
|
10
|
+
|
|
11
|
+
require 'dynflow/web_console'
|
|
12
|
+
dynflow_console = Dynflow::WebConsole.setup do
|
|
13
|
+
set :world, world
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
3.times do
|
|
17
|
+
Thread.new do
|
|
18
|
+
3.times do
|
|
19
|
+
world.trigger(Orchestrate::CreateInfrastructure)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
puts <<MESSAGE
|
|
25
|
+
=============================================
|
|
26
|
+
See the console at http://localhost:4567/
|
|
27
|
+
=============================================
|
|
28
|
+
MESSAGE
|
|
29
|
+
dynflow_console.run!
|
data/lib/dynflow.rb
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
+
require 'apipie-params'
|
|
2
|
+
require 'algebrick'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'set'
|
|
1
5
|
require 'active_support/core_ext/hash/indifferent_access'
|
|
2
|
-
require 'dynflow/logger'
|
|
3
|
-
require 'dynflow/execution_plan'
|
|
4
|
-
require 'dynflow/dispatcher'
|
|
5
|
-
require 'dynflow/bus'
|
|
6
|
-
require 'dynflow/step'
|
|
7
|
-
require 'dynflow/action'
|
|
8
6
|
|
|
7
|
+
# TODO validate in/output, also validate unknown keys
|
|
8
|
+
# TODO performance testing, how many actions will it handle?
|
|
9
|
+
# TODO profiling, find bottlenecks
|
|
9
10
|
module Dynflow
|
|
10
11
|
|
|
12
|
+
class Error < StandardError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
require 'dynflow/future'
|
|
16
|
+
require 'dynflow/micro_actor'
|
|
17
|
+
require 'dynflow/serializable'
|
|
18
|
+
require 'dynflow/clock'
|
|
19
|
+
require 'dynflow/stateful'
|
|
20
|
+
require 'dynflow/transaction_adapters'
|
|
21
|
+
require 'dynflow/persistence'
|
|
22
|
+
require 'dynflow/action'
|
|
23
|
+
require 'dynflow/flows'
|
|
24
|
+
require 'dynflow/execution_plan'
|
|
25
|
+
require 'dynflow/listeners'
|
|
26
|
+
require 'dynflow/executors'
|
|
27
|
+
require 'dynflow/logger_adapters'
|
|
28
|
+
require 'dynflow/world'
|
|
29
|
+
require 'dynflow/simple_world'
|
|
30
|
+
require 'dynflow/daemon'
|
|
31
|
+
|
|
11
32
|
end
|
data/lib/dynflow/action.rb
CHANGED
|
@@ -1,130 +1,238 @@
|
|
|
1
|
+
require 'active_support/inflector'
|
|
2
|
+
|
|
1
3
|
module Dynflow
|
|
2
|
-
class Action
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
attr_accessor :execution_plan, :from_subscription, :input, :output
|
|
5
|
+
# TODO unify phases into one class, check what can be called in what phase at runtime
|
|
6
|
+
class Action < Serializable
|
|
7
|
+
include Algebrick::TypeCheck
|
|
8
|
+
include Algebrick::Matching
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
require 'dynflow/action/format'
|
|
11
|
+
extend Format
|
|
12
|
+
|
|
13
|
+
require 'dynflow/action/progress'
|
|
14
|
+
include Progress
|
|
15
|
+
|
|
16
|
+
require 'dynflow/action/suspended'
|
|
17
|
+
require 'dynflow/action/missing'
|
|
18
|
+
|
|
19
|
+
require 'dynflow/action/plan_phase'
|
|
20
|
+
require 'dynflow/action/flow_phase'
|
|
21
|
+
require 'dynflow/action/run_phase'
|
|
22
|
+
require 'dynflow/action/finalize_phase'
|
|
23
|
+
|
|
24
|
+
require 'dynflow/action/presenter'
|
|
25
|
+
require 'dynflow/action/polling'
|
|
26
|
+
require 'dynflow/action/cancellable_polling'
|
|
27
|
+
|
|
28
|
+
# Override this to extend the phase classes
|
|
29
|
+
def self.phase_modules
|
|
30
|
+
{ plan_phase: [PlanPhase],
|
|
31
|
+
run_phase: [RunPhase],
|
|
32
|
+
finalize_phase: [FinalizePhase],
|
|
33
|
+
presenter: [Presenter] }.freeze
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
phase_modules.each do |phase_name, _|
|
|
37
|
+
define_singleton_method phase_name do
|
|
38
|
+
instance_variable_get :"@#{phase_name}" or
|
|
39
|
+
instance_variable_set :"@#{phase_name}", __send__("create_#{phase_name}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
define_singleton_method "create_#{phase_name}" do
|
|
43
|
+
generate_phase(*phase_modules[phase_name])
|
|
44
|
+
end
|
|
12
45
|
end
|
|
13
46
|
|
|
14
|
-
def self.
|
|
15
|
-
|
|
47
|
+
def self.generate_phase(*modules)
|
|
48
|
+
Class.new(self) { modules.each { |m| include m } }
|
|
16
49
|
end
|
|
17
50
|
|
|
51
|
+
def self.phase?
|
|
52
|
+
[PlanPhase, RunPhase, FinalizePhase, Presenter].any? { |phase| self < phase }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.all_children
|
|
56
|
+
#noinspection RubyArgCount
|
|
57
|
+
children.
|
|
58
|
+
inject(children) { |children, child| children + child.all_children }.
|
|
59
|
+
select { |ch| !ch.phase? }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# FIND define subscriptions in world independent on action's classes,
|
|
63
|
+
# limited only by in/output formats
|
|
64
|
+
# @return [nil, Class] a child of Action
|
|
18
65
|
def self.subscribe
|
|
19
66
|
nil
|
|
20
67
|
end
|
|
21
68
|
|
|
22
|
-
def self.
|
|
23
|
-
|
|
69
|
+
def self.attr_indifferent_access_hash(*names)
|
|
70
|
+
attr_reader(*names)
|
|
71
|
+
names.each do |name|
|
|
72
|
+
define_method("#{name}=") { |v| indifferent_access_hash_variable_set name, v }
|
|
73
|
+
end
|
|
24
74
|
end
|
|
25
75
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
76
|
+
def indifferent_access_hash_variable_set(name, value)
|
|
77
|
+
Type! value, Hash
|
|
78
|
+
instance_variable_set :"@#{name}", value.with_indifferent_access
|
|
79
|
+
end
|
|
29
80
|
|
|
30
|
-
|
|
31
|
-
|
|
81
|
+
def self.from_hash(hash, phase, *args)
|
|
82
|
+
check_class_key_present hash
|
|
83
|
+
raise ArgumentError, "unknown phase '#{phase}'" unless [:plan_phase, :run_phase, :finalize_phase].include? phase
|
|
84
|
+
Action.constantize(hash[:class]).send(phase).new_from_hash(hash, *args)
|
|
32
85
|
end
|
|
33
86
|
|
|
87
|
+
attr_reader :world, :execution_plan_id, :id, :plan_step_id, :run_step_id, :finalize_step_id
|
|
34
88
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
89
|
+
def initialize(attributes, world)
|
|
90
|
+
raise "It's not expected to initialize this class directly, use phases." unless self.class.phase?
|
|
91
|
+
|
|
92
|
+
Type! attributes, Hash
|
|
39
93
|
|
|
40
|
-
|
|
41
|
-
|
|
94
|
+
@world = Type! world, World
|
|
95
|
+
@step = Type! attributes[:step], ExecutionPlan::Steps::Abstract
|
|
96
|
+
@execution_plan_id = attributes[:execution_plan_id] || raise(ArgumentError, 'missing execution_plan_id')
|
|
97
|
+
@id = attributes[:id] || raise(ArgumentError, 'missing id')
|
|
98
|
+
@plan_step_id = attributes[:plan_step_id]
|
|
99
|
+
@run_step_id = attributes[:run_step_id]
|
|
100
|
+
@finalize_step_id = attributes[:finalize_step_id]
|
|
42
101
|
end
|
|
43
102
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@input_format_block = block
|
|
49
|
-
elsif @input_format_block
|
|
50
|
-
@input_format ||= Apipie::Params::Description.define(&@input_format_block)
|
|
103
|
+
def self.action_class
|
|
104
|
+
# superclass because we run this from the phases of action class
|
|
105
|
+
if phase?
|
|
106
|
+
superclass
|
|
51
107
|
else
|
|
52
|
-
|
|
108
|
+
self
|
|
53
109
|
end
|
|
54
110
|
end
|
|
55
111
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@output_format_block = block
|
|
61
|
-
elsif @output_format_block
|
|
62
|
-
@output_format ||= Apipie::Params::Description.define(&@output_format_block)
|
|
63
|
-
else
|
|
64
|
-
nil
|
|
65
|
-
end
|
|
112
|
+
def self.constantize(action_name)
|
|
113
|
+
action_name.constantize
|
|
114
|
+
rescue NameError
|
|
115
|
+
Action::Missing.generate(action_name)
|
|
66
116
|
end
|
|
67
117
|
|
|
68
|
-
def
|
|
69
|
-
|
|
118
|
+
def action_logger
|
|
119
|
+
world.action_logger
|
|
70
120
|
end
|
|
71
121
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
122
|
+
def action_class
|
|
123
|
+
self.class.action_class
|
|
124
|
+
end
|
|
75
125
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
126
|
+
def to_hash
|
|
127
|
+
recursive_to_hash class: action_class.name,
|
|
128
|
+
execution_plan_id: execution_plan_id,
|
|
129
|
+
id: id,
|
|
130
|
+
plan_step_id: plan_step_id,
|
|
131
|
+
run_step_id: run_step_id,
|
|
132
|
+
finalize_step_id: finalize_step_id
|
|
133
|
+
end
|
|
81
134
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
135
|
+
# @api private
|
|
136
|
+
# @return [Array<Fixnum>] - ids of steps referenced from action
|
|
137
|
+
def required_step_ids(value = self.input)
|
|
138
|
+
ret = case value
|
|
139
|
+
when Hash
|
|
140
|
+
value.values.map { |val| required_step_ids(val) }
|
|
141
|
+
when Array
|
|
142
|
+
value.map { |val| required_step_ids(val) }
|
|
143
|
+
when ExecutionPlan::OutputReference
|
|
144
|
+
value.step_id
|
|
145
|
+
else
|
|
146
|
+
# no reference hidden in this arg
|
|
147
|
+
end
|
|
148
|
+
return Array(ret).flatten.compact
|
|
149
|
+
end
|
|
87
150
|
|
|
88
|
-
|
|
151
|
+
def state
|
|
152
|
+
@step.state
|
|
89
153
|
end
|
|
90
154
|
|
|
91
|
-
|
|
92
|
-
|
|
155
|
+
def error
|
|
156
|
+
@step.error
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
protected
|
|
160
|
+
|
|
161
|
+
def state=(state)
|
|
162
|
+
@world.logger.debug "step #{execution_plan_id}:#{@step.id} #{self.state} >> #{state}"
|
|
163
|
+
@step.state = state
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def save_state
|
|
167
|
+
@step.save
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @override
|
|
93
171
|
def plan(*args)
|
|
94
|
-
if
|
|
172
|
+
if trigger
|
|
95
173
|
# if the action is triggered by subscription, by default use the
|
|
96
|
-
# input of parent action
|
|
97
|
-
|
|
174
|
+
# input of parent action.
|
|
175
|
+
# should be replaced by referencing the input from input format
|
|
176
|
+
plan_self(input.merge(trigger.input))
|
|
98
177
|
else
|
|
99
178
|
# in this case, the action was triggered by plan_action. Use
|
|
100
179
|
# the argument specified there.
|
|
101
|
-
plan_self(args
|
|
180
|
+
plan_self(*args)
|
|
102
181
|
end
|
|
182
|
+
self
|
|
103
183
|
end
|
|
104
184
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
@execution_plan << self
|
|
185
|
+
def self.new_from_hash(hash, world)
|
|
186
|
+
new(hash, world)
|
|
108
187
|
end
|
|
109
188
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
ERRORING = Object.new
|
|
192
|
+
|
|
193
|
+
# DSL to terminate action execution and set it to error
|
|
194
|
+
def error!(error)
|
|
195
|
+
set_error(error)
|
|
196
|
+
throw ERRORING
|
|
115
197
|
end
|
|
116
198
|
|
|
117
|
-
def
|
|
118
|
-
|
|
199
|
+
def with_error_handling(&block)
|
|
200
|
+
raise "wrong state #{self.state}" unless self.state == :running
|
|
201
|
+
|
|
202
|
+
begin
|
|
203
|
+
catch(ERRORING) { block.call }
|
|
204
|
+
rescue Exception => error
|
|
205
|
+
set_error(error)
|
|
206
|
+
# reraise low-level exceptions
|
|
207
|
+
raise error unless Type? error, StandardError, ScriptError
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
case self.state
|
|
211
|
+
when :running
|
|
212
|
+
self.state = :success
|
|
213
|
+
when :suspended, :error
|
|
214
|
+
else
|
|
215
|
+
raise "wrong state #{self.state}"
|
|
216
|
+
end
|
|
119
217
|
end
|
|
120
218
|
|
|
121
|
-
def
|
|
122
|
-
|
|
219
|
+
def set_error(error)
|
|
220
|
+
Type! error, Exception, String
|
|
221
|
+
action_logger.error error
|
|
222
|
+
self.state = :error
|
|
223
|
+
@step.error = if error.is_a?(String)
|
|
224
|
+
ExecutionPlan::Steps::Error.new(nil, error, nil)
|
|
225
|
+
else
|
|
226
|
+
ExecutionPlan::Steps::Error.new(error.class.name, error.message, error.backtrace)
|
|
227
|
+
end
|
|
123
228
|
end
|
|
124
229
|
|
|
125
|
-
def
|
|
126
|
-
|
|
230
|
+
def self.inherited(child)
|
|
231
|
+
children << child
|
|
127
232
|
end
|
|
128
233
|
|
|
234
|
+
def self.children
|
|
235
|
+
@children ||= []
|
|
236
|
+
end
|
|
129
237
|
end
|
|
130
238
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Action::CancellablePolling
|
|
3
|
+
include Action::Polling
|
|
4
|
+
Cancel = Algebrick.atom
|
|
5
|
+
|
|
6
|
+
def run(event = nil)
|
|
7
|
+
if Cancel === event
|
|
8
|
+
self.external_task = cancel_external_task
|
|
9
|
+
else
|
|
10
|
+
super event
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def cancel_external_task
|
|
15
|
+
NotImplementedError
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Action::FinalizePhase
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.send(:include, Action::FlowPhase)
|
|
6
|
+
base.send(:attr_reader, :output)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def execute
|
|
10
|
+
self.state = :running
|
|
11
|
+
save_state
|
|
12
|
+
with_error_handling do
|
|
13
|
+
finalize
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Action::FlowPhase
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend(ClassMethods)
|
|
6
|
+
base.send(:attr_reader, :input)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(attributes, world)
|
|
10
|
+
super attributes, world
|
|
11
|
+
|
|
12
|
+
indifferent_access_hash_variable_set :input, deserialize_references(attributes[:input])
|
|
13
|
+
indifferent_access_hash_variable_set :output, attributes[:output] || {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_hash
|
|
17
|
+
super.merge recursive_to_hash(input: input,
|
|
18
|
+
output: output)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def deserialize_references(value)
|
|
22
|
+
case value
|
|
23
|
+
when Hash
|
|
24
|
+
if value[:class] == "Dynflow::ExecutionPlan::OutputReference"
|
|
25
|
+
ExecutionPlan::OutputReference.new_from_hash(value)
|
|
26
|
+
else
|
|
27
|
+
value.reduce(HashWithIndifferentAccess.new) do |h, (key, val)|
|
|
28
|
+
h.update(key => deserialize_references(val))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
when Array
|
|
32
|
+
value.map { |val| deserialize_references(val) }
|
|
33
|
+
else
|
|
34
|
+
value
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module ClassMethods
|
|
39
|
+
def new_from_hash(hash, step, world)
|
|
40
|
+
new(hash.merge(step: step), world)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|