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