dynflow 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dynflow.rb +2 -1
- data/lib/dynflow/action.rb +271 -101
- data/lib/dynflow/action/format.rb +4 -4
- data/lib/dynflow/action/progress.rb +8 -8
- data/lib/dynflow/execution_plan.rb +14 -15
- data/lib/dynflow/execution_plan/output_reference.rb +56 -23
- data/lib/dynflow/execution_plan/steps/abstract.rb +1 -3
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +0 -17
- data/lib/dynflow/execution_plan/steps/error.rb +35 -11
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -1
- data/lib/dynflow/execution_plan/steps/plan_step.rb +8 -3
- data/lib/dynflow/execution_plan/steps/run_step.rb +1 -1
- data/lib/dynflow/listeners/socket.rb +0 -1
- data/lib/dynflow/middleware.rb +0 -1
- data/lib/dynflow/middleware/world.rb +1 -1
- data/lib/dynflow/persistence.rb +2 -5
- data/lib/dynflow/serializable.rb +24 -11
- data/lib/dynflow/testing/assertions.rb +5 -5
- data/lib/dynflow/testing/dummy_execution_plan.rb +1 -1
- data/lib/dynflow/testing/dummy_planned_action.rb +2 -1
- data/lib/dynflow/testing/dummy_world.rb +4 -0
- data/lib/dynflow/testing/factories.rb +18 -10
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +35 -21
- data/test/action_test.rb +50 -76
- data/test/clock_test.rb +4 -4
- data/test/execution_plan_test.rb +1 -1
- data/test/executor_test.rb +3 -2
- data/test/remote_via_socket_test.rb +11 -9
- data/test/support/code_workflow_example.rb +2 -3
- data/test/test_helper.rb +1 -1
- data/test/testing_test.rb +3 -3
- metadata +2 -8
- data/lib/dynflow/action/finalize_phase.rb +0 -20
- data/lib/dynflow/action/flow_phase.rb +0 -44
- data/lib/dynflow/action/plan_phase.rb +0 -87
- data/lib/dynflow/action/presenter.rb +0 -51
- data/lib/dynflow/action/run_phase.rb +0 -45
- data/lib/dynflow/middleware/action.rb +0 -9
@@ -14,12 +14,12 @@ module Dynflow
|
|
14
14
|
when !block && @input_format_block
|
15
15
|
return @input_format ||= Apipie::Params::Description.define(&@input_format_block)
|
16
16
|
when block && @input_format_block
|
17
|
-
raise "The input_format has already been defined in #{self.
|
17
|
+
raise "The input_format has already been defined in #{self.class}"
|
18
18
|
when !block && !@input_format_block
|
19
19
|
if superclass.respond_to? :input_format
|
20
20
|
superclass.input_format
|
21
21
|
else
|
22
|
-
raise "The input_format has not been defined yet in #{self.
|
22
|
+
raise "The input_format has not been defined yet in #{self.class}"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -31,12 +31,12 @@ module Dynflow
|
|
31
31
|
when !block && @output_format_block
|
32
32
|
return @output_format ||= Apipie::Params::Description.define(&@output_format_block)
|
33
33
|
when block && @output_format_block
|
34
|
-
raise "The output_format has already been defined in #{self.
|
34
|
+
raise "The output_format has already been defined in #{self.class}"
|
35
35
|
when !block && !@output_format_block
|
36
36
|
if superclass.respond_to? :output_format
|
37
37
|
superclass.output_format
|
38
38
|
else
|
39
|
-
raise "The output_format has not been defined yet in #{self.
|
39
|
+
raise "The output_format has not been defined yet in #{self.class}"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -33,13 +33,13 @@ module Dynflow
|
|
33
33
|
when :success, :skipped
|
34
34
|
1
|
35
35
|
when :running, :suspended
|
36
|
-
case
|
37
|
-
when Action::
|
36
|
+
case phase
|
37
|
+
when Action::Run
|
38
38
|
run_progress
|
39
|
-
when Action::
|
39
|
+
when Action::Finalize
|
40
40
|
finalize_progress
|
41
41
|
else
|
42
|
-
raise
|
42
|
+
raise 'Calculating progress for this phase is not supported'
|
43
43
|
end
|
44
44
|
else
|
45
45
|
0
|
@@ -47,13 +47,13 @@ module Dynflow
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def progress_weight
|
50
|
-
case
|
51
|
-
when Action::
|
50
|
+
case phase
|
51
|
+
when Action::Run
|
52
52
|
run_progress_weight
|
53
|
-
when Action::
|
53
|
+
when Action::Finalize
|
54
54
|
finalize_progress_weight
|
55
55
|
else
|
56
|
-
raise
|
56
|
+
raise 'Calculating progress for this phase is not supported'
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -96,6 +96,10 @@ module Dynflow
|
|
96
96
|
result == :error
|
97
97
|
end
|
98
98
|
|
99
|
+
def errors
|
100
|
+
steps.values.map(&:error).compact
|
101
|
+
end
|
102
|
+
|
99
103
|
def generate_action_id
|
100
104
|
@last_action_id ||= 0
|
101
105
|
@last_action_id += 1
|
@@ -149,7 +153,7 @@ module Dynflow
|
|
149
153
|
# FIND maybe move to persistence to let adapter to do it effectively?
|
150
154
|
# @return [Array<Steps::Abstract>]
|
151
155
|
def steps_to_skip(step)
|
152
|
-
dependent_steps =
|
156
|
+
dependent_steps = steps.values.find_all do |s|
|
153
157
|
next if s.is_a? Steps::PlanStep
|
154
158
|
action = persistence.load_action(s)
|
155
159
|
action.required_step_ids.include?(step.id)
|
@@ -199,14 +203,14 @@ module Dynflow
|
|
199
203
|
end
|
200
204
|
|
201
205
|
def add_run_step(action)
|
202
|
-
add_step(Steps::RunStep, action.
|
206
|
+
add_step(Steps::RunStep, action.class, action.id).tap do |step|
|
203
207
|
@dependency_graph.add_dependencies(step, action)
|
204
208
|
current_run_flow.add_and_resolve(@dependency_graph, Flows::Atom.new(step.id))
|
205
209
|
end
|
206
210
|
end
|
207
211
|
|
208
212
|
def add_finalize_step(action)
|
209
|
-
add_step(Steps::FinalizeStep, action.
|
213
|
+
add_step(Steps::FinalizeStep, action.class, action.id).tap do |step|
|
210
214
|
finalize_flow << Flows::Atom.new(step.id)
|
211
215
|
end
|
212
216
|
end
|
@@ -264,19 +268,14 @@ module Dynflow
|
|
264
268
|
# @return [Array<Action::Presenter>] presenter of the actions
|
265
269
|
# involved in the plan
|
266
270
|
def actions
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
action_id,
|
275
|
-
involved_steps,
|
276
|
-
all_actions)
|
277
|
-
all_actions << action
|
271
|
+
@actions ||= begin
|
272
|
+
action_ids = steps.values.map(&:action_id).uniq
|
273
|
+
action_ids.map do |action_id|
|
274
|
+
attributes = world.persistence.adapter.load_action(id, action_id)
|
275
|
+
Action.from_hash(attributes.update(phase: Action::Present, execution_plan: self),
|
276
|
+
world)
|
277
|
+
end
|
278
278
|
end
|
279
|
-
return all_actions
|
280
279
|
end
|
281
280
|
|
282
281
|
private
|
@@ -1,51 +1,84 @@
|
|
1
1
|
module Dynflow
|
2
2
|
class ExecutionPlan::OutputReference < Serializable
|
3
|
+
include Algebrick::TypeCheck
|
3
4
|
|
4
|
-
|
5
|
+
# dereferences all OutputReferences in Hash-Array structure
|
6
|
+
def self.dereference(object, persistence)
|
7
|
+
case object
|
8
|
+
when Hash
|
9
|
+
object.reduce(HashWithIndifferentAccess.new) do |h, (key, val)|
|
10
|
+
h.update(key => dereference(val, persistence))
|
11
|
+
end
|
12
|
+
when Array
|
13
|
+
object.map { |val| dereference(val, persistence) }
|
14
|
+
when self
|
15
|
+
object.dereference(persistence)
|
16
|
+
else
|
17
|
+
object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# dereferences all hashes representing OutputReferences in Hash-Array structure
|
22
|
+
def self.deserialize(value)
|
23
|
+
case value
|
24
|
+
when Hash
|
25
|
+
if value[:class] == self.to_s
|
26
|
+
new_from_hash(value)
|
27
|
+
else
|
28
|
+
value.reduce(HashWithIndifferentAccess.new) do |h, (key, val)|
|
29
|
+
h.update(key => deserialize(val))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
when Array
|
33
|
+
value.map { |val| deserialize(val) }
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :execution_plan_id, :step_id, :action_id, :subkeys
|
5
40
|
|
6
|
-
def initialize(step_id, action_id, subkeys = [])
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
41
|
+
def initialize(execution_plan_id, step_id, action_id, subkeys = [])
|
42
|
+
@execution_plan_id = Type! execution_plan_id, String
|
43
|
+
@step_id = Type! step_id, Integer
|
44
|
+
@action_id = Type! action_id, Integer
|
45
|
+
Type! subkeys, Array
|
46
|
+
@subkeys = subkeys.map { |v| Type!(v, String, Symbol).to_s }.freeze
|
10
47
|
end
|
11
48
|
|
12
49
|
def [](subkey)
|
13
|
-
|
50
|
+
self.class.new(execution_plan_id, step_id, action_id, subkeys + [subkey])
|
14
51
|
end
|
15
52
|
|
16
53
|
def to_hash
|
17
|
-
recursive_to_hash class:
|
18
|
-
|
19
|
-
|
20
|
-
|
54
|
+
recursive_to_hash class: self.class.to_s,
|
55
|
+
execution_plan_id: execution_plan_id,
|
56
|
+
step_id: step_id,
|
57
|
+
action_id: action_id,
|
58
|
+
subkeys: subkeys
|
21
59
|
end
|
22
60
|
|
23
61
|
def to_s
|
24
|
-
"Step(#{
|
25
|
-
ret <<
|
62
|
+
"Step(#{step_id}).output".tap do |ret|
|
63
|
+
ret << subkeys.map { |k| "[:#{k}]" }.join('') if subkeys.any?
|
26
64
|
end
|
27
65
|
end
|
28
66
|
|
29
67
|
alias_method :inspect, :to_s
|
30
68
|
|
31
|
-
def dereference(persistence
|
69
|
+
def dereference(persistence)
|
32
70
|
action_data = persistence.adapter.load_action(execution_plan_id, action_id)
|
33
|
-
|
34
|
-
@subkeys.each do |subkey|
|
35
|
-
if deref.respond_to?(:[])
|
36
|
-
deref = deref[subkey]
|
37
|
-
else
|
38
|
-
raise "We were not able to dereference subkey #{@subkeys} from #{self.inspect}"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
return deref
|
71
|
+
@subkeys.reduce(action_data[:output]) { |v, k| v.fetch k }
|
42
72
|
end
|
43
73
|
|
44
74
|
protected
|
45
75
|
|
46
76
|
def self.new_from_hash(hash)
|
47
77
|
check_class_matching hash
|
48
|
-
new(hash
|
78
|
+
new(hash.fetch(:execution_plan_id),
|
79
|
+
hash.fetch(:step_id),
|
80
|
+
hash.fetch(:action_id),
|
81
|
+
hash.fetch(:subkeys))
|
49
82
|
end
|
50
83
|
|
51
84
|
end
|
@@ -31,9 +31,7 @@ module Dynflow
|
|
31
31
|
|
32
32
|
self.state = state.to_sym
|
33
33
|
|
34
|
-
|
35
|
-
raise ArgumentError, 'action_class is not an child of Action' unless action_class < Action
|
36
|
-
raise ArgumentError, 'action_class must not be phase' if action_class.phase?
|
34
|
+
Child! action_class, Action
|
37
35
|
@action_class = action_class
|
38
36
|
|
39
37
|
@action_id = action_id || raise(ArgumentError, 'missing action_id')
|
@@ -5,7 +5,6 @@ module Dynflow
|
|
5
5
|
def execute(*args)
|
6
6
|
return self if [:skipped, :success].include? self.state
|
7
7
|
open_action do |action|
|
8
|
-
action.indifferent_access_hash_variable_set :input, dereference(action.input)
|
9
8
|
with_time_calculation do
|
10
9
|
action.execute(*args)
|
11
10
|
end
|
@@ -16,7 +15,6 @@ module Dynflow
|
|
16
15
|
self.class.from_hash(to_hash, execution_plan_id, world)
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
18
|
def progress
|
21
19
|
action = persistence.load_action(self)
|
22
20
|
[action.progress_done, action.progress_weight]
|
@@ -32,21 +30,6 @@ module Dynflow
|
|
32
30
|
|
33
31
|
return self
|
34
32
|
end
|
35
|
-
|
36
|
-
def dereference(input)
|
37
|
-
case input
|
38
|
-
when Hash
|
39
|
-
input.reduce(HashWithIndifferentAccess.new) do |h, (key, val)|
|
40
|
-
h.update(key => dereference(val))
|
41
|
-
end
|
42
|
-
when Array
|
43
|
-
input.map { |val| dereference(val) }
|
44
|
-
when ExecutionPlan::OutputReference
|
45
|
-
input.dereference(persistence, execution_plan_id)
|
46
|
-
else
|
47
|
-
input
|
48
|
-
end
|
49
|
-
end
|
50
33
|
end
|
51
34
|
end
|
52
35
|
end
|
@@ -1,32 +1,56 @@
|
|
1
1
|
module Dynflow
|
2
2
|
module ExecutionPlan::Steps
|
3
3
|
class Error < Serializable
|
4
|
+
extend Algebrick::Matching
|
5
|
+
include Algebrick::TypeCheck
|
4
6
|
|
5
7
|
attr_reader :exception_class, :message, :backtrace
|
6
8
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def self.new(*args)
|
10
|
+
case args.size
|
11
|
+
when 1
|
12
|
+
match obj = args.first,
|
13
|
+
(on String do
|
14
|
+
super(StandardError, obj, caller, nil)
|
15
|
+
end),
|
16
|
+
(on Exception do
|
17
|
+
super(obj.class, obj.message, obj.backtrace, obj)
|
18
|
+
end)
|
19
|
+
when 3, 4
|
20
|
+
super(*args.values_at(0..3))
|
21
|
+
else
|
22
|
+
raise ArgumentError, "wrong number of arguments #{args}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(exception_class, message, backtrace, exception)
|
27
|
+
@exception_class = Child! exception_class, Exception
|
28
|
+
@message = Type! message, String
|
29
|
+
@backtrace = Type! backtrace, Array
|
30
|
+
@exception = Type! exception, Exception, NilClass
|
11
31
|
end
|
12
32
|
|
13
33
|
def self.new_from_hash(hash)
|
14
|
-
self.new(hash[:exception_class], hash[:message], hash[:backtrace])
|
34
|
+
self.new(hash[:exception_class].constantize, hash[:message], hash[:backtrace], nil)
|
15
35
|
end
|
16
36
|
|
17
37
|
def to_hash
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
38
|
+
recursive_to_hash class: self.class.name,
|
39
|
+
exception_class: exception_class.to_s,
|
40
|
+
message: message,
|
41
|
+
backtrace: backtrace
|
22
42
|
end
|
23
43
|
|
24
44
|
def to_s
|
25
|
-
|
45
|
+
format '%s (%s)\n%s',
|
46
|
+
(@exception || self).message,
|
47
|
+
(@exception ? @exception.class : exception_class),
|
48
|
+
(@exception || self).backtrace
|
26
49
|
end
|
27
50
|
|
28
51
|
def exception
|
29
|
-
|
52
|
+
@exception ||
|
53
|
+
exception_class.exception(message).tap { |e| e.set_backtrace backtrace }
|
30
54
|
end
|
31
55
|
end
|
32
56
|
end
|
@@ -24,7 +24,7 @@ module Dynflow
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def phase
|
27
|
-
|
27
|
+
Action::Plan
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_hash
|
@@ -37,8 +37,13 @@ module Dynflow
|
|
37
37
|
attributes = { execution_plan_id: execution_plan.id,
|
38
38
|
id: action_id,
|
39
39
|
step: self,
|
40
|
-
plan_step_id: self.id
|
41
|
-
|
40
|
+
plan_step_id: self.id,
|
41
|
+
run_step_id: nil,
|
42
|
+
finalize_step_id: nil,
|
43
|
+
phase: phase,
|
44
|
+
execution_plan: execution_plan,
|
45
|
+
trigger: trigger }
|
46
|
+
action = action_class.new(attributes, execution_plan.world)
|
42
47
|
persistence.save_action(execution_plan_id, action)
|
43
48
|
|
44
49
|
with_time_calculation do
|
data/lib/dynflow/middleware.rb
CHANGED
@@ -20,7 +20,7 @@ module Dynflow
|
|
20
20
|
action_class = action_or_class
|
21
21
|
elsif Type? action_or_class, Dynflow::Action
|
22
22
|
action = action_or_class
|
23
|
-
action_class = action.
|
23
|
+
action_class = action.class
|
24
24
|
else
|
25
25
|
Algebrick::TypeCheck.error action_or_class, 'is not instance or child class', Dynflow::Action
|
26
26
|
end
|
data/lib/dynflow/persistence.rb
CHANGED
@@ -14,11 +14,8 @@ module Dynflow
|
|
14
14
|
def load_action(step)
|
15
15
|
attributes = adapter.
|
16
16
|
load_action(step.execution_plan_id, step.action_id).
|
17
|
-
update(step: step)
|
18
|
-
return Action.from_hash(attributes,
|
19
|
-
step.phase,
|
20
|
-
step,
|
21
|
-
step.world)
|
17
|
+
update(step: step, phase: step.phase)
|
18
|
+
return Action.from_hash(attributes, step.world)
|
22
19
|
end
|
23
20
|
|
24
21
|
def save_action(execution_plan_id, action)
|
data/lib/dynflow/serializable.rb
CHANGED
@@ -2,7 +2,7 @@ module Dynflow
|
|
2
2
|
class Serializable
|
3
3
|
def self.from_hash(hash, *args)
|
4
4
|
check_class_key_present hash
|
5
|
-
hash[:class].
|
5
|
+
constantize(hash[:class]).new_from_hash(hash, *args)
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_hash
|
@@ -26,26 +26,39 @@ module Dynflow
|
|
26
26
|
raise ArgumentError, 'missing :class' unless hash[:class]
|
27
27
|
end
|
28
28
|
|
29
|
+
def self.constantize(action_name)
|
30
|
+
action_name.constantize
|
31
|
+
end
|
32
|
+
|
29
33
|
private_class_method :check_class_matching, :check_class_key_present
|
30
34
|
|
31
35
|
private
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
value
|
39
|
-
|
40
|
-
|
37
|
+
# recursively traverses hash-array structure and converts all to hashes
|
38
|
+
# accepts more hashes which are then merged
|
39
|
+
def recursive_to_hash(*values)
|
40
|
+
if values.size == 1
|
41
|
+
value = values.first
|
42
|
+
case value
|
43
|
+
when Numeric, String, Symbol, TrueClass, FalseClass, NilClass
|
44
|
+
value
|
45
|
+
when Array
|
46
|
+
value.map { |v| recursive_to_hash v }
|
47
|
+
when Hash
|
48
|
+
value.inject({}) { |h, (k, v)| h.update k => recursive_to_hash(v) }
|
49
|
+
else
|
50
|
+
value.to_hash
|
51
|
+
end
|
41
52
|
else
|
42
|
-
|
53
|
+
values.all? { |v| Type! v, Hash, NilClass }
|
54
|
+
recursive_to_hash(values.compact.reduce { |h, v| h.merge v })
|
43
55
|
end
|
44
56
|
end
|
45
57
|
|
46
58
|
def self.string_to_time(string)
|
47
59
|
return if string.nil?
|
48
|
-
_, year, month, day, hour, min, sec =
|
60
|
+
_, year, month, day, hour, min, sec =
|
61
|
+
*/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/.match(string)
|
49
62
|
Time.new(year.to_i, month.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i)
|
50
63
|
end
|
51
64
|
|