dynflow 0.3.0 → 0.4.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/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
|
|