dynflow 1.3.0 → 1.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.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +3 -4
  4. data/Dockerfile +9 -0
  5. data/Gemfile +6 -0
  6. data/Rakefile +1 -0
  7. data/doc/pages/Gemfile +1 -0
  8. data/doc/pages/Rakefile +1 -0
  9. data/doc/pages/plugins/alert_block.rb +1 -0
  10. data/doc/pages/plugins/div_tag.rb +1 -0
  11. data/doc/pages/plugins/graphviz.rb +1 -0
  12. data/doc/pages/plugins/plantuml.rb +1 -0
  13. data/doc/pages/plugins/play.rb +1 -0
  14. data/doc/pages/plugins/tags.rb +1 -0
  15. data/doc/pages/plugins/toc.rb +1 -0
  16. data/docker-compose.yml +41 -0
  17. data/dynflow.gemspec +1 -0
  18. data/examples/clock_benchmark.rb +1 -0
  19. data/examples/example_helper.rb +19 -2
  20. data/examples/future_execution.rb +2 -1
  21. data/examples/memory_limit_watcher.rb +1 -0
  22. data/examples/orchestrate.rb +4 -5
  23. data/examples/orchestrate_evented.rb +3 -2
  24. data/examples/remote_executor.rb +68 -0
  25. data/examples/singletons.rb +4 -3
  26. data/examples/sub_plan_concurrency_control.rb +2 -1
  27. data/examples/sub_plans.rb +3 -2
  28. data/examples/termination.rb +1 -0
  29. data/lib/dynflow.rb +20 -0
  30. data/lib/dynflow/action.rb +28 -3
  31. data/lib/dynflow/action/cancellable.rb +1 -0
  32. data/lib/dynflow/action/format.rb +1 -0
  33. data/lib/dynflow/action/missing.rb +1 -0
  34. data/lib/dynflow/action/polling.rb +3 -1
  35. data/lib/dynflow/action/progress.rb +1 -0
  36. data/lib/dynflow/action/rescue.rb +1 -0
  37. data/lib/dynflow/action/singleton.rb +1 -0
  38. data/lib/dynflow/action/suspended.rb +9 -2
  39. data/lib/dynflow/action/timeouts.rb +2 -1
  40. data/lib/dynflow/action/with_bulk_sub_plans.rb +2 -1
  41. data/lib/dynflow/action/with_polling_sub_plans.rb +7 -5
  42. data/lib/dynflow/action/with_sub_plans.rb +1 -0
  43. data/lib/dynflow/active_job/queue_adapter.rb +1 -0
  44. data/lib/dynflow/actor.rb +13 -5
  45. data/lib/dynflow/actors.rb +1 -0
  46. data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
  47. data/lib/dynflow/clock.rb +27 -47
  48. data/lib/dynflow/config.rb +11 -2
  49. data/lib/dynflow/connectors.rb +1 -0
  50. data/lib/dynflow/connectors/abstract.rb +1 -0
  51. data/lib/dynflow/connectors/database.rb +1 -0
  52. data/lib/dynflow/connectors/direct.rb +1 -0
  53. data/lib/dynflow/coordinator.rb +1 -0
  54. data/lib/dynflow/coordinator_adapters.rb +1 -0
  55. data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
  56. data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
  57. data/lib/dynflow/dead_letter_silencer.rb +2 -0
  58. data/lib/dynflow/debug/telemetry/persistence.rb +1 -0
  59. data/lib/dynflow/delayed_executors.rb +1 -0
  60. data/lib/dynflow/delayed_executors/abstract.rb +1 -0
  61. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -0
  62. data/lib/dynflow/delayed_executors/polling.rb +1 -0
  63. data/lib/dynflow/delayed_plan.rb +1 -0
  64. data/lib/dynflow/director.rb +80 -15
  65. data/lib/dynflow/director/execution_plan_manager.rb +17 -3
  66. data/lib/dynflow/director/flow_manager.rb +1 -0
  67. data/lib/dynflow/director/{work_queue.rb → queue_hash.rb} +9 -8
  68. data/lib/dynflow/director/running_steps_manager.rb +55 -18
  69. data/lib/dynflow/director/sequence_cursor.rb +1 -0
  70. data/lib/dynflow/director/sequential_manager.rb +12 -2
  71. data/lib/dynflow/dispatcher.rb +4 -2
  72. data/lib/dynflow/dispatcher/abstract.rb +1 -0
  73. data/lib/dynflow/dispatcher/client_dispatcher.rb +6 -4
  74. data/lib/dynflow/dispatcher/executor_dispatcher.rb +13 -1
  75. data/lib/dynflow/errors.rb +1 -0
  76. data/lib/dynflow/execution_history.rb +1 -0
  77. data/lib/dynflow/execution_plan.rb +3 -2
  78. data/lib/dynflow/execution_plan/dependency_graph.rb +1 -0
  79. data/lib/dynflow/execution_plan/hooks.rb +1 -0
  80. data/lib/dynflow/execution_plan/output_reference.rb +2 -1
  81. data/lib/dynflow/execution_plan/steps.rb +1 -0
  82. data/lib/dynflow/execution_plan/steps/abstract.rb +10 -5
  83. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +2 -0
  84. data/lib/dynflow/execution_plan/steps/error.rb +1 -0
  85. data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -0
  86. data/lib/dynflow/execution_plan/steps/plan_step.rb +1 -0
  87. data/lib/dynflow/execution_plan/steps/run_step.rb +1 -0
  88. data/lib/dynflow/executors.rb +1 -1
  89. data/lib/dynflow/executors/abstract/core.rb +132 -0
  90. data/lib/dynflow/executors/parallel.rb +24 -11
  91. data/lib/dynflow/executors/parallel/core.rb +10 -91
  92. data/lib/dynflow/executors/parallel/pool.rb +4 -2
  93. data/lib/dynflow/executors/parallel/worker.rb +2 -1
  94. data/lib/dynflow/executors/sidekiq/core.rb +121 -0
  95. data/lib/dynflow/executors/sidekiq/internal_job_base.rb +24 -0
  96. data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +60 -0
  97. data/lib/dynflow/executors/sidekiq/redis_locking.rb +69 -0
  98. data/lib/dynflow/executors/sidekiq/serialization.rb +33 -0
  99. data/lib/dynflow/executors/sidekiq/worker_jobs.rb +42 -0
  100. data/lib/dynflow/flows.rb +1 -0
  101. data/lib/dynflow/flows/abstract.rb +1 -0
  102. data/lib/dynflow/flows/abstract_composed.rb +1 -0
  103. data/lib/dynflow/flows/atom.rb +1 -0
  104. data/lib/dynflow/flows/concurrence.rb +1 -0
  105. data/lib/dynflow/flows/sequence.rb +1 -0
  106. data/lib/dynflow/logger_adapters.rb +1 -0
  107. data/lib/dynflow/logger_adapters/abstract.rb +1 -0
  108. data/lib/dynflow/logger_adapters/delegator.rb +1 -0
  109. data/lib/dynflow/logger_adapters/formatters.rb +1 -0
  110. data/lib/dynflow/logger_adapters/formatters/abstract.rb +1 -0
  111. data/lib/dynflow/logger_adapters/formatters/exception.rb +1 -0
  112. data/lib/dynflow/logger_adapters/simple.rb +1 -0
  113. data/lib/dynflow/middleware.rb +1 -0
  114. data/lib/dynflow/middleware/common/singleton.rb +1 -0
  115. data/lib/dynflow/middleware/common/transaction.rb +1 -0
  116. data/lib/dynflow/middleware/register.rb +1 -0
  117. data/lib/dynflow/middleware/resolver.rb +1 -0
  118. data/lib/dynflow/middleware/stack.rb +1 -0
  119. data/lib/dynflow/middleware/world.rb +1 -0
  120. data/lib/dynflow/persistence.rb +3 -2
  121. data/lib/dynflow/persistence_adapters.rb +1 -0
  122. data/lib/dynflow/persistence_adapters/abstract.rb +1 -0
  123. data/lib/dynflow/persistence_adapters/sequel.rb +10 -7
  124. data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +1 -0
  125. data/lib/dynflow/persistence_adapters/sequel_migrations/002_incremental_progress.rb +1 -0
  126. data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +1 -0
  127. data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +1 -0
  128. data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +1 -0
  129. data/lib/dynflow/persistence_adapters/sequel_migrations/006_fix_data_length.rb +1 -0
  130. data/lib/dynflow/persistence_adapters/sequel_migrations/007_future_execution.rb +1 -0
  131. data/lib/dynflow/persistence_adapters/sequel_migrations/008_rename_scheduled_plans_to_delayed_plans.rb +1 -0
  132. data/lib/dynflow/persistence_adapters/sequel_migrations/009_fix_mysql_data_length.rb +1 -0
  133. data/lib/dynflow/persistence_adapters/sequel_migrations/010_add_execution_plans_label.rb +1 -0
  134. data/lib/dynflow/persistence_adapters/sequel_migrations/011_placeholder.rb +1 -0
  135. data/lib/dynflow/persistence_adapters/sequel_migrations/012_add_delayed_plans_serialized_args.rb +1 -0
  136. data/lib/dynflow/persistence_adapters/sequel_migrations/013_add_action_columns.rb +1 -0
  137. data/lib/dynflow/persistence_adapters/sequel_migrations/014_add_step_columns.rb +1 -0
  138. data/lib/dynflow/persistence_adapters/sequel_migrations/015_add_execution_plan_columns.rb +1 -0
  139. data/lib/dynflow/persistence_adapters/sequel_migrations/016_add_step_queue.rb +1 -0
  140. data/lib/dynflow/persistence_adapters/sequel_migrations/017_add_delayed_plan_frozen.rb +1 -0
  141. data/lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb +1 -0
  142. data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +48 -0
  143. data/lib/dynflow/rails.rb +1 -0
  144. data/lib/dynflow/rails/configuration.rb +6 -3
  145. data/lib/dynflow/rails/daemon.rb +1 -0
  146. data/lib/dynflow/round_robin.rb +1 -0
  147. data/lib/dynflow/semaphores.rb +1 -0
  148. data/lib/dynflow/semaphores/abstract.rb +1 -0
  149. data/lib/dynflow/semaphores/aggregating.rb +1 -0
  150. data/lib/dynflow/semaphores/dummy.rb +1 -0
  151. data/lib/dynflow/semaphores/stateful.rb +1 -0
  152. data/lib/dynflow/serializable.rb +13 -4
  153. data/lib/dynflow/serializer.rb +24 -0
  154. data/lib/dynflow/serializers.rb +1 -0
  155. data/lib/dynflow/serializers/abstract.rb +1 -0
  156. data/lib/dynflow/serializers/noop.rb +1 -0
  157. data/lib/dynflow/stateful.rb +1 -0
  158. data/lib/dynflow/telemetry.rb +1 -0
  159. data/lib/dynflow/telemetry_adapters/abstract.rb +1 -0
  160. data/lib/dynflow/telemetry_adapters/dummy.rb +1 -0
  161. data/lib/dynflow/telemetry_adapters/statsd.rb +1 -0
  162. data/lib/dynflow/testing.rb +1 -0
  163. data/lib/dynflow/testing/assertions.rb +6 -5
  164. data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
  165. data/lib/dynflow/testing/dummy_executor.rb +19 -2
  166. data/lib/dynflow/testing/dummy_planned_action.rb +1 -0
  167. data/lib/dynflow/testing/dummy_step.rb +3 -1
  168. data/lib/dynflow/testing/dummy_world.rb +9 -0
  169. data/lib/dynflow/testing/factories.rb +6 -1
  170. data/lib/dynflow/testing/in_thread_executor.rb +22 -3
  171. data/lib/dynflow/testing/in_thread_world.rb +9 -0
  172. data/lib/dynflow/testing/managed_clock.rb +1 -0
  173. data/lib/dynflow/testing/mimic.rb +1 -0
  174. data/lib/dynflow/throttle_limiter.rb +1 -0
  175. data/lib/dynflow/transaction_adapters.rb +1 -0
  176. data/lib/dynflow/transaction_adapters/abstract.rb +1 -0
  177. data/lib/dynflow/transaction_adapters/active_record.rb +1 -0
  178. data/lib/dynflow/transaction_adapters/none.rb +1 -0
  179. data/lib/dynflow/utils.rb +1 -0
  180. data/lib/dynflow/utils/indifferent_hash.rb +1 -0
  181. data/lib/dynflow/utils/priority_queue.rb +1 -0
  182. data/lib/dynflow/version.rb +2 -1
  183. data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -0
  184. data/lib/dynflow/web.rb +1 -0
  185. data/lib/dynflow/web/console.rb +1 -0
  186. data/lib/dynflow/web/console_helpers.rb +1 -0
  187. data/lib/dynflow/web/filtering_helpers.rb +1 -0
  188. data/lib/dynflow/web/world_helpers.rb +1 -0
  189. data/lib/dynflow/web_console.rb +1 -0
  190. data/lib/dynflow/world.rb +11 -1
  191. data/lib/dynflow/world/invalidation.rb +7 -1
  192. data/test/abnormal_states_recovery_test.rb +41 -40
  193. data/test/action_test.rb +160 -110
  194. data/test/activejob_adapter_test.rb +1 -0
  195. data/test/batch_sub_tasks_test.rb +12 -11
  196. data/test/clock_test.rb +2 -1
  197. data/test/concurrency_control_test.rb +20 -19
  198. data/test/coordinator_test.rb +20 -21
  199. data/test/daemon_test.rb +2 -1
  200. data/test/dead_letter_silencer_test.rb +9 -7
  201. data/test/dispatcher_test.rb +2 -1
  202. data/test/execution_plan_cleaner_test.rb +13 -12
  203. data/test/execution_plan_hooks_test.rb +3 -2
  204. data/test/execution_plan_test.rb +33 -32
  205. data/test/executor_test.rb +533 -489
  206. data/test/future_execution_test.rb +45 -44
  207. data/test/memory_cosumption_watcher_test.rb +5 -4
  208. data/test/middleware_test.rb +55 -54
  209. data/test/persistence_test.rb +56 -53
  210. data/test/rescue_test.rb +36 -35
  211. data/test/round_robin_test.rb +13 -12
  212. data/test/semaphores_test.rb +31 -30
  213. data/test/support/code_workflow_example.rb +1 -0
  214. data/test/support/dummy_example.rb +14 -1
  215. data/test/support/middleware_example.rb +2 -1
  216. data/test/support/rails/config/environment.rb +1 -0
  217. data/test/support/rescue_example.rb +1 -0
  218. data/test/support/test_execution_log.rb +1 -0
  219. data/test/test_helper.rb +18 -17
  220. data/test/testing_test.rb +45 -44
  221. data/test/utils_test.rb +18 -17
  222. data/test/web_console_test.rb +1 -0
  223. data/test/world_test.rb +7 -6
  224. metadata +13 -4
  225. data/lib/dynflow/executors/abstract.rb +0 -40
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Semaphores
3
4
  require 'dynflow/semaphores/abstract'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Semaphores
3
4
  class Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Semaphores
3
4
  class Aggregating < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Semaphores
3
4
  class Dummy < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Semaphores
3
4
  class Stateful < Abstract
@@ -1,5 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require 'date'
1
3
  module Dynflow
2
4
  class Serializable
5
+ TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
6
+ LEGACY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
7
+
3
8
  def self.from_hash(hash, *args)
4
9
  check_class_key_present hash
5
10
  constantize(hash[:class]).new_from_hash(hash, *args)
@@ -58,15 +63,19 @@ module Dynflow
58
63
  def self.string_to_time(string)
59
64
  return if string.nil?
60
65
  return string if string.is_a?(Time)
61
- _, year, month, day, hour, min, sec =
62
- */(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/.match(string)
63
- Time.utc(year.to_i, month.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i)
66
+ time = begin
67
+ DateTime.strptime(string, TIME_FORMAT)
68
+ rescue ArgumentError => _
69
+ DateTime.strptime(string, LEGACY_TIME_FORMAT)
70
+ end
71
+
72
+ time.to_time.utc
64
73
  end
65
74
 
66
75
  def time_to_str(time)
67
76
  return if time.nil?
68
77
  Type! time, Time
69
- time.utc.strftime '%Y-%m-%d %H:%M:%S'
78
+ time.utc.strftime(TIME_FORMAT)
70
79
  end
71
80
 
72
81
  def self.hash_to_error(hash)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'algebrick/serializer'
2
3
 
3
4
  module Dynflow
@@ -10,6 +11,24 @@ module Dynflow
10
11
  ARBITRARY_TYPE_KEY = :class
11
12
  MARSHAL_KEY = :marshaled
12
13
 
14
+ def load(data, options = {})
15
+ case data
16
+ when ::Array
17
+ data.collect { |v| load(v) }
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def dump(object, options = {})
24
+ case object
25
+ when ::Array
26
+ object.collect { |v| dump(v) }
27
+ else
28
+ super
29
+ end
30
+ end
31
+
13
32
  protected
14
33
 
15
34
  def parse_other(other, options = {})
@@ -19,6 +38,9 @@ module Dynflow
19
38
  end
20
39
 
21
40
  if (type_name = other[ARBITRARY_TYPE_KEY] || other[ARBITRARY_TYPE_KEY.to_s])
41
+ if type_name == 'Time' && ( time_str = other['value'] )
42
+ return Serializable.send(:string_to_time, time_str)
43
+ end
22
44
  type = Utils.constantize(type_name) rescue nil
23
45
  if type && type.respond_to?(:from_hash)
24
46
  return type.from_hash other
@@ -35,6 +57,8 @@ module Dynflow
35
57
  object.to_h
36
58
  when object.respond_to?(:to_hash)
37
59
  object.to_hash
60
+ when object.is_a?(Time) && !options[:marshaled_time]
61
+ { ARBITRARY_TYPE_KEY => 'Time', 'value' => object.utc.strftime(Serializable::TIME_FORMAT) }
38
62
  else
39
63
  { ARBITRARY_TYPE_KEY => object.class.to_s,
40
64
  MARSHAL_KEY => Base64.strict_encode64(Marshal.dump(object)) }
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Serializers
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Serializers
3
4
  # @abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Serializers
3
4
  class Noop < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Stateful
3
4
  def self.included(base)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'dynflow/telemetry_adapters/abstract'
2
3
  require 'dynflow/telemetry_adapters/dummy'
3
4
  require 'dynflow/telemetry_adapters/statsd'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TelemetryAdapters
3
4
  class Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TelemetryAdapters
3
4
  # Telemetry adapter which does not evaluate blocks passed to {#with_instance}.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TelemetryAdapters
3
4
  class StatsD < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  extend Algebrick::TypeCheck
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  module Assertions
@@ -48,8 +49,8 @@ module Dynflow
48
49
  def assert_run_phase(action, input = nil, &block)
49
50
  Match! action.phase, Action::Plan
50
51
  Match! action.state, :success
51
- action.execution_plan.planned_run_steps.must_include action
52
- action.input.must_equal Utils.stringify_keys(input) if input
52
+ _(action.execution_plan.planned_run_steps).must_include action
53
+ _(action.input).must_equal Utils.stringify_keys(input) if input
53
54
  block.call action.input if block
54
55
  end
55
56
 
@@ -57,21 +58,21 @@ module Dynflow
57
58
  def refute_run_phase(action)
58
59
  Match! action.phase, Action::Plan
59
60
  Match! action.state, :success
60
- action.execution_plan.planned_run_steps.wont_include action
61
+ _(action.execution_plan.planned_run_steps).wont_include action
61
62
  end
62
63
 
63
64
  # assert that +action+ has finalize-phase planned
64
65
  def assert_finalize_phase(action)
65
66
  Match! action.phase, Action::Plan
66
67
  Match! action.state, :success
67
- action.execution_plan.planned_finalize_steps.must_include action
68
+ _(action.execution_plan.planned_finalize_steps).must_include action
68
69
  end
69
70
 
70
71
  # refute that +action+ has finalize-phase planned
71
72
  def refute_finalize_phase(action)
72
73
  Match! action.phase, Action::Plan
73
74
  Match! action.state, :success
74
- action.execution_plan.planned_finalize_steps.wont_include action
75
+ _(action.execution_plan.planned_finalize_steps).wont_include action
75
76
  end
76
77
 
77
78
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class DummyExecutionPlan
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class DummyExecutor
4
- attr_reader :world
5
+ attr_reader :world, :events_to_process
5
6
 
6
7
  def initialize(world)
7
8
  @world = world
@@ -12,6 +13,22 @@ module Dynflow
12
13
  @events_to_process << [execution_plan_id, step_id, event, future]
13
14
  end
14
15
 
16
+ def delayed_event(director_event)
17
+ @events_to_process << [director_event.execution_plan_id, director_event.step_id, director_event.event, director_event.result]
18
+ end
19
+
20
+ def plan_events(delayed_events)
21
+ delayed_events.each do |event|
22
+ world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time)
23
+ end
24
+ end
25
+
26
+ def execute(action, event = nil)
27
+ action.execute event
28
+ plan_events(action.delayed_events.dup)
29
+ action.delayed_events.clear
30
+ end
31
+
15
32
  # returns true if some event was processed.
16
33
  def progress
17
34
  events = @events_to_process.dup
@@ -21,7 +38,7 @@ module Dynflow
21
38
  if event && world.action.state != :suspended
22
39
  return false
23
40
  end
24
- world.action.execute event
41
+ execute(world.action, event)
25
42
  end
26
43
  end
27
44
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class DummyPlannedAction
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class DummyStep
@@ -12,7 +13,8 @@ module Dynflow
12
13
  @id = Testing.get_id
13
14
  end
14
15
 
15
- def save
16
+ def save(_options = {})
17
+ 1
16
18
  end
17
19
  end
18
20
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class DummyWorld
@@ -34,6 +35,14 @@ module Dynflow
34
35
  executor.event execution_plan_id, step_id, event, future
35
36
  end
36
37
 
38
+ def plan_event(execution_plan_id, step_id, event, time, accepted = Concurrent::Promises.resolvable_future)
39
+ if time.nil? || time < Time.now
40
+ event(execution_plan_id, step_id, event, accepted)
41
+ else
42
+ clock.ping(executor, time, Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, accepted], :delayed_event)
43
+ end
44
+ end
45
+
37
46
  def persistence
38
47
  nil
39
48
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  module Factories
@@ -47,6 +48,10 @@ module Dynflow
47
48
  plan_action create_action(action_class), *args, &block
48
49
  end
49
50
 
51
+ def plan_events(world, delayed_events)
52
+ delayed_events.each { |event| world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time) }
53
+ end
54
+
50
55
  # @return [Action::RunPhase]
51
56
  def run_action(plan_action, event = nil, &stubbing)
52
57
  Match! plan_action.phase, Action::Plan, Action::Run
@@ -70,7 +75,7 @@ module Dynflow
70
75
  run_action.world.action ||= run_action
71
76
  run_action.world.clock.clear
72
77
  stubbing.call run_action if stubbing
73
- run_action.execute event
78
+ run_action.world.executor.execute(run_action, event)
74
79
  raise run_action.error if run_action.error
75
80
  run_action
76
81
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
- class InThreadExecutor < Dynflow::Executors::Abstract
4
+ class InThreadExecutor
4
5
  def initialize(world)
5
6
  @world = world
6
7
  @director = Director.new(@world)
@@ -20,25 +21,43 @@ module Dynflow
20
21
  end
21
22
  end
22
23
 
24
+ def plan_events(delayed_events)
25
+ delayed_events.each do |event|
26
+ @world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time)
27
+ end
28
+ end
29
+
23
30
  def handle_work(work_item)
24
31
  work_item.execute
32
+ step = work_item.step if work_item.is_a?(Director::StepWorkItem)
33
+ plan_events(step && step.delayed_events) if step && step.delayed_events
25
34
  @director.work_finished(work_item)
26
35
  end
27
36
 
28
37
  def event(execution_plan_id, step_id, event, future = Concurrent::Promises.resolvable_future)
29
- event = (Director::Event[execution_plan_id, step_id, event, future])
38
+ event = (Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, future])
30
39
  @director.handle_event(event).each do |work_item|
31
40
  @work_items << work_item
32
41
  end
33
42
  future
34
43
  end
35
44
 
45
+ def delayed_event(director_event)
46
+ @director.handle_event(director_event).each do |work_item|
47
+ @work_items << work_item
48
+ end
49
+ director_event.result
50
+ end
51
+
36
52
  def clock_tick
37
53
  @world.clock.progress_all([:periodic_check_inbox])
38
54
  end
39
55
 
40
56
  def feed_queue(work_items)
41
- work_items.each { |work_item| @work_items.push(work_item) }
57
+ work_items.each do |work_item|
58
+ work_item.world = @world
59
+ @work_items.push(work_item)
60
+ end
42
61
  end
43
62
 
44
63
  def terminate(future = Concurrent::Promises.resolvable_future)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class InThreadWorld < Dynflow::World
@@ -60,6 +61,14 @@ module Dynflow
60
61
  def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future)
61
62
  @executor.event(execution_plan_id, step_id, event, done)
62
63
  end
64
+
65
+ def plan_event(execution_plan_id, step_id, event, time, done = Concurrent::Promises.resolvable_future)
66
+ if time.nil? || time < Time.now
67
+ event(execution_plan_id, step_id, event, done)
68
+ else
69
+ clock.ping(executor, time, Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, done], :delayed_event)
70
+ end
71
+ end
63
72
  end
64
73
  end
65
74
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
  class ManagedClock
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Testing
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  class ThrottleLimiter
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TransactionAdapters
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TransactionAdapters
3
4
  class Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TransactionAdapters
3
4
  class ActiveRecord < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module TransactionAdapters
3
4
  class None < Abstract
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Utils
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Utils
3
4
  # Heavily inpired by ActiveSupport::HashWithIndifferentAccess,