dynflow 0.8.3 → 0.8.4
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.
- checksums.yaml +8 -8
- data/doc/pages/source/documentation/index.md +14 -14
- data/dynflow.gemspec +0 -1
- data/examples/future_execution.rb +7 -7
- data/lib/dynflow.rb +3 -3
- data/lib/dynflow/action.rb +7 -9
- data/lib/dynflow/action/with_sub_plans.rb +9 -3
- data/lib/dynflow/config.rb +2 -2
- data/lib/dynflow/coordinator.rb +3 -3
- data/lib/dynflow/delayed_executors.rb +9 -0
- data/lib/dynflow/{schedulers → delayed_executors}/abstract.rb +3 -3
- data/lib/dynflow/{schedulers → delayed_executors}/abstract_core.rb +7 -7
- data/lib/dynflow/{schedulers → delayed_executors}/polling.rb +6 -6
- data/lib/dynflow/{scheduled_plan.rb → delayed_plan.rb} +4 -4
- data/lib/dynflow/execution_plan.rb +10 -10
- data/lib/dynflow/execution_plan/output_reference.rb +2 -2
- data/lib/dynflow/execution_plan/steps/error.rb +1 -1
- data/lib/dynflow/execution_plan/steps/plan_step.rb +2 -2
- data/lib/dynflow/middleware.rb +1 -1
- data/lib/dynflow/middleware/stack.rb +1 -1
- data/lib/dynflow/middleware/world.rb +1 -1
- data/lib/dynflow/persistence.rb +10 -10
- data/lib/dynflow/persistence_adapters/abstract.rb +4 -4
- data/lib/dynflow/persistence_adapters/sequel.rb +17 -17
- data/lib/dynflow/persistence_adapters/sequel_migrations/008_rename_scheduled_plans_to_delayed_plans.rb +5 -0
- data/lib/dynflow/serializable.rb +1 -1
- data/lib/dynflow/serializer.rb +1 -1
- data/lib/dynflow/testing/assertions.rb +1 -1
- data/lib/dynflow/utils.rb +205 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web/console_helpers.rb +1 -1
- data/lib/dynflow/web/filtering_helpers.rb +3 -3
- data/lib/dynflow/world.rb +16 -16
- data/test/action_test.rb +4 -2
- data/test/future_execution_test.rb +32 -32
- data/test/middleware_test.rb +5 -5
- data/test/persistence_test.rb +3 -3
- data/test/support/dummy_example.rb +2 -2
- data/test/support/middleware_example.rb +5 -5
- data/test/test_helper.rb +1 -1
- data/test/testing_test.rb +1 -1
- data/web/views/show.erb +3 -3
- metadata +9 -21
- data/lib/dynflow/schedulers.rb +0 -9
@@ -6,7 +6,7 @@ module Dynflow
|
|
6
6
|
def self.dereference(object, persistence)
|
7
7
|
case object
|
8
8
|
when Hash
|
9
|
-
object.reduce(
|
9
|
+
object.reduce(Utils.indifferent_hash({})) do |h, (key, val)|
|
10
10
|
h.update(key => dereference(val, persistence))
|
11
11
|
end
|
12
12
|
when Array
|
@@ -25,7 +25,7 @@ module Dynflow
|
|
25
25
|
if value[:class] == self.to_s
|
26
26
|
new_from_hash(value)
|
27
27
|
else
|
28
|
-
value.reduce(
|
28
|
+
value.reduce(Utils.indifferent_hash({})) do |h, (key, val)|
|
29
29
|
h.update(key => deserialize(val))
|
30
30
|
end
|
31
31
|
end
|
@@ -35,8 +35,8 @@ module Dynflow
|
|
35
35
|
super.merge recursive_to_hash(:children => children)
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
@action.
|
38
|
+
def delay(delay_options, args)
|
39
|
+
@action.execute_delay(delay_options, *args)
|
40
40
|
@action.serializer
|
41
41
|
ensure
|
42
42
|
save
|
data/lib/dynflow/middleware.rb
CHANGED
@@ -14,7 +14,7 @@ module Dynflow
|
|
14
14
|
@middleware_class = Child! middleware_class, Middleware
|
15
15
|
@middleware = middleware_class.new self
|
16
16
|
@action = Type! action, Dynflow::Action, NilClass
|
17
|
-
@method = Match! method, :
|
17
|
+
@method = Match! method, :delay, :plan, :run, :finalize, :plan_phase, :finalize_phase
|
18
18
|
@next_stack = Type! next_stack, Middleware::Stack, Proc
|
19
19
|
end
|
20
20
|
|
@@ -14,7 +14,7 @@ module Dynflow
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def execute(method, action_or_class, *args, &block)
|
17
|
-
Match! method, :
|
17
|
+
Match! method, :delay, :plan, :run, :finalize, :plan_phase, :finalize_phase
|
18
18
|
if Child? action_or_class, Dynflow::Action
|
19
19
|
action = nil
|
20
20
|
action_class = action_or_class
|
data/lib/dynflow/persistence.rb
CHANGED
@@ -49,24 +49,24 @@ module Dynflow
|
|
49
49
|
adapter.save_execution_plan(execution_plan.id, execution_plan.to_hash)
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
53
|
-
adapter.
|
54
|
-
|
52
|
+
def find_past_delayed_plans(time)
|
53
|
+
adapter.find_past_delayed_plans(time).map do |plan|
|
54
|
+
DelayedPlan.new_from_hash(@world, plan)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
adapter.
|
58
|
+
def delete_delayed_plans(filters, batch_size = 1000)
|
59
|
+
adapter.delete_delayed_plans(filters, batch_size)
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
63
|
-
adapter.
|
62
|
+
def save_delayed_plan(delayed_plan)
|
63
|
+
adapter.save_delayed_plan(delayed_plan.execution_plan_uuid, delayed_plan.to_hash)
|
64
64
|
end
|
65
65
|
|
66
|
-
def
|
67
|
-
hash = adapter.
|
66
|
+
def load_delayed_plan(execution_plan_id)
|
67
|
+
hash = adapter.load_delayed_plan(execution_plan_id)
|
68
68
|
return nil unless hash
|
69
|
-
|
69
|
+
DelayedPlan.new_from_hash(@world, hash)
|
70
70
|
end
|
71
71
|
|
72
72
|
def load_step(execution_plan_id, step_id, world)
|
@@ -55,19 +55,19 @@ module Dynflow
|
|
55
55
|
raise NotImplementedError
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def find_past_delayed_plans(options = {})
|
59
59
|
raise NotImplementedError
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
62
|
+
def delete_delayed_plans(filters, batch_size = 1000)
|
63
63
|
raise NotImplementedError
|
64
64
|
end
|
65
65
|
|
66
|
-
def
|
66
|
+
def load_delayed_plan(execution_plan_id)
|
67
67
|
raise NotImplementedError
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
70
|
+
def save_delayed_plan(execution_plan_id, value)
|
71
71
|
raise NotImplementedError
|
72
72
|
end
|
73
73
|
|
@@ -32,7 +32,7 @@ module Dynflow
|
|
32
32
|
step: %w(state started_at ended_at real_time execution_time action_id progress_done progress_weight),
|
33
33
|
envelope: %w(receiver_id),
|
34
34
|
coordinator_record: %w(id owner_id class),
|
35
|
-
|
35
|
+
delayed: %w(execution_plan_uuid start_at start_before args_serializer)}
|
36
36
|
|
37
37
|
def initialize(config)
|
38
38
|
config = config.dup
|
@@ -63,7 +63,7 @@ module Dynflow
|
|
63
63
|
filter(:execution_plan, table(:execution_plan), filters).each_slice(batch_size) do |plans|
|
64
64
|
uuids = plans.map { |p| p.fetch(:uuid) }
|
65
65
|
@db.transaction do
|
66
|
-
table(:
|
66
|
+
table(:delayed).where(execution_plan_uuid: uuids).delete
|
67
67
|
table(:step).where(execution_plan_uuid: uuids).delete
|
68
68
|
table(:action).where(execution_plan_uuid: uuids).delete
|
69
69
|
count += table(:execution_plan).where(uuid: uuids).delete
|
@@ -80,33 +80,33 @@ module Dynflow
|
|
80
80
|
save :execution_plan, { uuid: execution_plan_id }, value
|
81
81
|
end
|
82
82
|
|
83
|
-
def
|
83
|
+
def delete_delayed_plans(filters, batch_size = 1000)
|
84
84
|
count = 0
|
85
|
-
filter(:
|
85
|
+
filter(:delayed, table(:delayed), filters).each_slice(batch_size) do |plans|
|
86
86
|
uuids = plans.map { |p| p.fetch(:execution_plan_uuid) }
|
87
87
|
@db.transaction do
|
88
|
-
count += table(:
|
88
|
+
count += table(:delayed).where(execution_plan_uuid: uuids).delete
|
89
89
|
end
|
90
90
|
end
|
91
91
|
count
|
92
92
|
end
|
93
93
|
|
94
|
-
def
|
95
|
-
table(:
|
94
|
+
def find_past_delayed_plans(time)
|
95
|
+
table(:delayed)
|
96
96
|
.where('start_at <= ?', time)
|
97
97
|
.order_by(:start_at)
|
98
98
|
.all
|
99
99
|
.map { |plan| load_data(plan) }
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
103
|
-
load :
|
102
|
+
def load_delayed_plan(execution_plan_id)
|
103
|
+
load :delayed, execution_plan_uuid: execution_plan_id
|
104
104
|
rescue KeyError
|
105
105
|
return nil
|
106
106
|
end
|
107
107
|
|
108
|
-
def
|
109
|
-
save :
|
108
|
+
def save_delayed_plan(execution_plan_id, value)
|
109
|
+
save :delayed, { execution_plan_uuid: execution_plan_id }, value
|
110
110
|
end
|
111
111
|
|
112
112
|
def load_step(execution_plan_id, step_id)
|
@@ -195,7 +195,7 @@ module Dynflow
|
|
195
195
|
step: :dynflow_steps,
|
196
196
|
envelope: :dynflow_envelopes,
|
197
197
|
coordinator_record: :dynflow_coordinator_records,
|
198
|
-
|
198
|
+
delayed: :dynflow_delayed_plans }
|
199
199
|
|
200
200
|
def table(which)
|
201
201
|
db[TABLES.fetch(which)]
|
@@ -243,7 +243,7 @@ module Dynflow
|
|
243
243
|
|
244
244
|
def load(what, condition)
|
245
245
|
table = table(what)
|
246
|
-
if (record = with_retry { table.first(
|
246
|
+
if (record = with_retry { table.first(Utils.symbolize_keys(condition)) } )
|
247
247
|
load_data(record)
|
248
248
|
else
|
249
249
|
raise KeyError, "searching: #{what} by: #{condition.inspect}"
|
@@ -251,16 +251,16 @@ module Dynflow
|
|
251
251
|
end
|
252
252
|
|
253
253
|
def load_data(record)
|
254
|
-
|
254
|
+
Utils.indifferent_hash(MultiJson.load(record[:data]))
|
255
255
|
end
|
256
256
|
|
257
257
|
def delete(what, condition)
|
258
|
-
table(what).where(
|
258
|
+
table(what).where(Utils.symbolize_keys(condition)).delete
|
259
259
|
end
|
260
260
|
|
261
261
|
def extract_metadata(what, value)
|
262
262
|
meta_keys = META_DATA.fetch(what)
|
263
|
-
value = value
|
263
|
+
value = Utils.indifferent_hash(value)
|
264
264
|
meta_keys.inject({}) { |h, k| h.update k.to_sym => value[k] }
|
265
265
|
end
|
266
266
|
|
@@ -312,7 +312,7 @@ module Dynflow
|
|
312
312
|
raise ArgumentError, "unkown columns: #{unknown.inspect}"
|
313
313
|
end
|
314
314
|
|
315
|
-
data_set.where
|
315
|
+
data_set.where Utils.symbolize_keys(filters)
|
316
316
|
end
|
317
317
|
|
318
318
|
def with_retry
|
data/lib/dynflow/serializable.rb
CHANGED
data/lib/dynflow/serializer.rb
CHANGED
@@ -19,7 +19,7 @@ module Dynflow
|
|
19
19
|
end
|
20
20
|
|
21
21
|
if (type_name = other[ARBITRARY_TYPE_KEY] || other[ARBITRARY_TYPE_KEY.to_s])
|
22
|
-
type =
|
22
|
+
type = Utils.constantize(type_name) rescue nil
|
23
23
|
if type && type.respond_to?(:from_hash)
|
24
24
|
return type.from_hash other
|
25
25
|
end
|
@@ -49,7 +49,7 @@ module Dynflow
|
|
49
49
|
Match! action.phase, Action::Plan
|
50
50
|
Match! action.state, :success
|
51
51
|
action.execution_plan.planned_run_steps.must_include action
|
52
|
-
action.input.must_equal
|
52
|
+
action.input.must_equal Utils.stringify_keys(input) if input
|
53
53
|
block.call action.input if block
|
54
54
|
end
|
55
55
|
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
def self.symbolize_keys(hash)
|
5
|
+
return hash.symbolize_keys if hash.respond_to?(:symbolize_keys)
|
6
|
+
hash.reduce({}) do |new_hash, (key, value)|
|
7
|
+
new_hash.update(key.to_sym => value)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.stringify_keys(hash)
|
12
|
+
return hash.stringify_keys if hash.respond_to?(:stringify_keys)
|
13
|
+
hash.reduce({}) do |new_hash, (key, value)|
|
14
|
+
new_hash.update(key.to_s => value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Inspired by ActiveSupport::Inflector
|
19
|
+
def self.constantize(string)
|
20
|
+
return string.constantize if string.respond_to?(:constantize)
|
21
|
+
|
22
|
+
names = string.split('::')
|
23
|
+
|
24
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
25
|
+
Object.const_get(string) if names.empty?
|
26
|
+
|
27
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
28
|
+
names.shift if names.size > 1 && names.first.empty?
|
29
|
+
|
30
|
+
names.inject(Object) do |constant, name|
|
31
|
+
if constant == Object
|
32
|
+
constant.const_get(name)
|
33
|
+
else
|
34
|
+
candidate = constant.const_get(name)
|
35
|
+
next candidate if constant.const_defined?(name, false)
|
36
|
+
next candidate unless Object.const_defined?(name)
|
37
|
+
|
38
|
+
# Go down the ancestors to check if it is owned directly. The check
|
39
|
+
# stops when we reach Object or the end of ancestors tree.
|
40
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
41
|
+
break const if ancestor == Object
|
42
|
+
break ancestor if ancestor.const_defined?(name, false)
|
43
|
+
const
|
44
|
+
end
|
45
|
+
|
46
|
+
# owner is in Object, so raise
|
47
|
+
constant.const_get(name, false)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.indifferent_hash(hash)
|
53
|
+
if defined? ::HashWithIndifferentAccess
|
54
|
+
# the users already have it: lets give them what they are used to
|
55
|
+
::HashWithIndifferentAccess.new(hash)
|
56
|
+
else
|
57
|
+
if hash.is_a? IndifferentHash
|
58
|
+
return hash
|
59
|
+
else
|
60
|
+
IndifferentHash.new(hash)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Heaviliy inpired by ActiveSupport::HashWithIndifferentAccess,
|
66
|
+
# reasons we don't want to use the original implementation:
|
67
|
+
# 1. we don't want any core_ext extensions
|
68
|
+
# 2. some users are not happy about seeing the ActiveSupport as
|
69
|
+
# our depednency
|
70
|
+
class IndifferentHash < Hash
|
71
|
+
def initialize(constructor = {})
|
72
|
+
if constructor.respond_to?(:to_hash)
|
73
|
+
super()
|
74
|
+
update(constructor)
|
75
|
+
else
|
76
|
+
super(constructor)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def default(key = nil)
|
81
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
82
|
+
self[key]
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.[](*args)
|
89
|
+
new.merge!(Hash[*args])
|
90
|
+
end
|
91
|
+
|
92
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
93
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
94
|
+
|
95
|
+
def []=(key, value)
|
96
|
+
regular_writer(convert_key(key), convert_value(value, for: :assignment))
|
97
|
+
end
|
98
|
+
|
99
|
+
alias_method :store, :[]=
|
100
|
+
|
101
|
+
def update(other_hash)
|
102
|
+
if other_hash.is_a? IndifferentHash
|
103
|
+
super(other_hash)
|
104
|
+
else
|
105
|
+
other_hash.to_hash.each_pair do |key, value|
|
106
|
+
if block_given? && key?(key)
|
107
|
+
value = yield(convert_key(key), self[key], value)
|
108
|
+
end
|
109
|
+
regular_writer(convert_key(key), convert_value(value))
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
alias_method :merge!, :update
|
116
|
+
|
117
|
+
def key?(key)
|
118
|
+
super(convert_key(key))
|
119
|
+
end
|
120
|
+
|
121
|
+
alias_method :include?, :key?
|
122
|
+
alias_method :has_key?, :key?
|
123
|
+
alias_method :member?, :key?
|
124
|
+
|
125
|
+
def fetch(key, *extras)
|
126
|
+
super(convert_key(key), *extras)
|
127
|
+
end
|
128
|
+
|
129
|
+
def values_at(*indices)
|
130
|
+
indices.collect { |key| self[convert_key(key)] }
|
131
|
+
end
|
132
|
+
|
133
|
+
def dup
|
134
|
+
self.class.new(self).tap do |new_hash|
|
135
|
+
new_hash.default = default
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def merge(hash, &block)
|
140
|
+
self.dup.update(hash, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def reverse_merge(other_hash)
|
144
|
+
super(self.class.new_from_hash_copying_default(other_hash))
|
145
|
+
end
|
146
|
+
|
147
|
+
def reverse_merge!(other_hash)
|
148
|
+
replace(reverse_merge( other_hash ))
|
149
|
+
end
|
150
|
+
|
151
|
+
def replace(other_hash)
|
152
|
+
super(self.class.new_from_hash_copying_default(other_hash))
|
153
|
+
end
|
154
|
+
|
155
|
+
def delete(key)
|
156
|
+
super(convert_key(key))
|
157
|
+
end
|
158
|
+
|
159
|
+
def stringify_keys!; self end
|
160
|
+
def deep_stringify_keys!; self end
|
161
|
+
def stringify_keys; dup end
|
162
|
+
def deep_stringify_keys; dup end
|
163
|
+
def to_options!; self end
|
164
|
+
|
165
|
+
def select(*args, &block)
|
166
|
+
dup.tap { |hash| hash.select!(*args, &block) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def reject(*args, &block)
|
170
|
+
dup.tap { |hash| hash.reject!(*args, &block) }
|
171
|
+
end
|
172
|
+
|
173
|
+
# Convert to a regular hash with string keys.
|
174
|
+
def to_hash
|
175
|
+
_new_hash = Hash.new(default)
|
176
|
+
each do |key, value|
|
177
|
+
_new_hash[key] = convert_value(value, for: :to_hash)
|
178
|
+
end
|
179
|
+
_new_hash
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
def convert_key(key)
|
184
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
185
|
+
end
|
186
|
+
|
187
|
+
def convert_value(value, options = {})
|
188
|
+
if value.is_a? Hash
|
189
|
+
if options[:for] == :to_hash
|
190
|
+
value.to_hash
|
191
|
+
else
|
192
|
+
Utils.indifferent_hash(value)
|
193
|
+
end
|
194
|
+
elsif value.is_a?(Array)
|
195
|
+
unless options[:for] == :assignment
|
196
|
+
value = value.dup
|
197
|
+
end
|
198
|
+
value.map! { |e| convert_value(e, options) }
|
199
|
+
else
|
200
|
+
value
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|