dynflow 0.1.0 → 0.2.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 (133) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +0 -10
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +99 -37
  6. data/Rakefile +2 -6
  7. data/doc/images/logo.png +0 -0
  8. data/dynflow.gemspec +10 -1
  9. data/examples/generate_work_for_daemon.rb +24 -0
  10. data/examples/orchestrate.rb +121 -0
  11. data/examples/run_daemon.rb +17 -0
  12. data/examples/web_console.rb +29 -0
  13. data/lib/dynflow.rb +27 -6
  14. data/lib/dynflow/action.rb +185 -77
  15. data/lib/dynflow/action/cancellable_polling.rb +18 -0
  16. data/lib/dynflow/action/finalize_phase.rb +18 -0
  17. data/lib/dynflow/action/flow_phase.rb +44 -0
  18. data/lib/dynflow/action/format.rb +46 -0
  19. data/lib/dynflow/action/missing.rb +26 -0
  20. data/lib/dynflow/action/plan_phase.rb +85 -0
  21. data/lib/dynflow/action/polling.rb +49 -0
  22. data/lib/dynflow/action/presenter.rb +51 -0
  23. data/lib/dynflow/action/progress.rb +62 -0
  24. data/lib/dynflow/action/run_phase.rb +43 -0
  25. data/lib/dynflow/action/suspended.rb +21 -0
  26. data/lib/dynflow/clock.rb +133 -0
  27. data/lib/dynflow/daemon.rb +29 -0
  28. data/lib/dynflow/execution_plan.rb +285 -33
  29. data/lib/dynflow/execution_plan/dependency_graph.rb +29 -0
  30. data/lib/dynflow/execution_plan/output_reference.rb +52 -0
  31. data/lib/dynflow/execution_plan/steps.rb +12 -0
  32. data/lib/dynflow/execution_plan/steps/abstract.rb +121 -0
  33. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +52 -0
  34. data/lib/dynflow/execution_plan/steps/error.rb +33 -0
  35. data/lib/dynflow/execution_plan/steps/finalize_step.rb +23 -0
  36. data/lib/dynflow/execution_plan/steps/plan_step.rb +81 -0
  37. data/lib/dynflow/execution_plan/steps/run_step.rb +21 -0
  38. data/lib/dynflow/executors.rb +9 -0
  39. data/lib/dynflow/executors/abstract.rb +32 -0
  40. data/lib/dynflow/executors/parallel.rb +88 -0
  41. data/lib/dynflow/executors/parallel/core.rb +119 -0
  42. data/lib/dynflow/executors/parallel/execution_plan_manager.rb +120 -0
  43. data/lib/dynflow/executors/parallel/flow_manager.rb +48 -0
  44. data/lib/dynflow/executors/parallel/pool.rb +102 -0
  45. data/lib/dynflow/executors/parallel/running_steps_manager.rb +63 -0
  46. data/lib/dynflow/executors/parallel/sequence_cursor.rb +97 -0
  47. data/lib/dynflow/executors/parallel/sequential_manager.rb +81 -0
  48. data/lib/dynflow/executors/parallel/work_queue.rb +44 -0
  49. data/lib/dynflow/executors/parallel/worker.rb +30 -0
  50. data/lib/dynflow/executors/remote_via_socket.rb +38 -0
  51. data/lib/dynflow/executors/remote_via_socket/core.rb +150 -0
  52. data/lib/dynflow/flows.rb +13 -0
  53. data/lib/dynflow/flows/abstract.rb +36 -0
  54. data/lib/dynflow/flows/abstract_composed.rb +104 -0
  55. data/lib/dynflow/flows/atom.rb +36 -0
  56. data/lib/dynflow/flows/concurrence.rb +28 -0
  57. data/lib/dynflow/flows/sequence.rb +13 -0
  58. data/lib/dynflow/future.rb +173 -0
  59. data/lib/dynflow/listeners.rb +7 -0
  60. data/lib/dynflow/listeners/abstract.rb +13 -0
  61. data/lib/dynflow/listeners/serialization.rb +41 -0
  62. data/lib/dynflow/listeners/socket.rb +88 -0
  63. data/lib/dynflow/logger_adapters.rb +8 -0
  64. data/lib/dynflow/logger_adapters/abstract.rb +30 -0
  65. data/lib/dynflow/logger_adapters/delegator.rb +13 -0
  66. data/lib/dynflow/logger_adapters/formatters.rb +8 -0
  67. data/lib/dynflow/logger_adapters/formatters/abstract.rb +33 -0
  68. data/lib/dynflow/logger_adapters/formatters/exception.rb +15 -0
  69. data/lib/dynflow/logger_adapters/simple.rb +59 -0
  70. data/lib/dynflow/micro_actor.rb +102 -0
  71. data/lib/dynflow/persistence.rb +53 -0
  72. data/lib/dynflow/persistence_adapters.rb +6 -0
  73. data/lib/dynflow/persistence_adapters/abstract.rb +56 -0
  74. data/lib/dynflow/persistence_adapters/sequel.rb +160 -0
  75. data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +52 -0
  76. data/lib/dynflow/serializable.rb +66 -0
  77. data/lib/dynflow/simple_world.rb +18 -0
  78. data/lib/dynflow/stateful.rb +40 -0
  79. data/lib/dynflow/testing.rb +32 -0
  80. data/lib/dynflow/testing/assertions.rb +64 -0
  81. data/lib/dynflow/testing/dummy_execution_plan.rb +40 -0
  82. data/lib/dynflow/testing/dummy_executor.rb +29 -0
  83. data/lib/dynflow/testing/dummy_planned_action.rb +18 -0
  84. data/lib/dynflow/testing/dummy_step.rb +19 -0
  85. data/lib/dynflow/testing/dummy_world.rb +33 -0
  86. data/lib/dynflow/testing/factories.rb +83 -0
  87. data/lib/dynflow/testing/managed_clock.rb +23 -0
  88. data/lib/dynflow/testing/mimic.rb +38 -0
  89. data/lib/dynflow/transaction_adapters.rb +9 -0
  90. data/lib/dynflow/transaction_adapters/abstract.rb +26 -0
  91. data/lib/dynflow/transaction_adapters/active_record.rb +27 -0
  92. data/lib/dynflow/transaction_adapters/none.rb +12 -0
  93. data/lib/dynflow/version.rb +1 -1
  94. data/lib/dynflow/web_console.rb +277 -0
  95. data/lib/dynflow/world.rb +168 -0
  96. data/test/action_test.rb +89 -11
  97. data/test/clock_test.rb +59 -0
  98. data/test/code_workflow_example.rb +382 -0
  99. data/test/execution_plan_test.rb +195 -64
  100. data/test/executor_test.rb +692 -0
  101. data/test/persistance_adapters_test.rb +173 -0
  102. data/test/test_helper.rb +316 -1
  103. data/test/testing_test.rb +148 -0
  104. data/test/web_console_test.rb +38 -0
  105. data/web/assets/javascripts/application.js +25 -0
  106. data/web/assets/stylesheets/application.css +101 -0
  107. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +1109 -0
  108. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +9 -0
  109. data/web/assets/vendor/bootstrap/css/bootstrap.css +6167 -0
  110. data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -0
  111. data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
  112. data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
  113. data/web/assets/vendor/bootstrap/js/bootstrap.js +2280 -0
  114. data/web/assets/vendor/bootstrap/js/bootstrap.min.js +6 -0
  115. data/web/assets/vendor/google-code-prettify/lang-basic.js +3 -0
  116. data/web/assets/vendor/google-code-prettify/prettify.css +1 -0
  117. data/web/assets/vendor/google-code-prettify/prettify.js +30 -0
  118. data/web/assets/vendor/google-code-prettify/run_prettify.js +34 -0
  119. data/web/assets/vendor/jquery/jquery.js +9807 -0
  120. data/web/views/flow.erb +19 -0
  121. data/web/views/flow_step.erb +31 -0
  122. data/web/views/index.erb +39 -0
  123. data/web/views/layout.erb +20 -0
  124. data/web/views/plan_step.erb +11 -0
  125. data/web/views/show.erb +54 -0
  126. metadata +250 -11
  127. data/examples/events.rb +0 -71
  128. data/examples/workflow.rb +0 -140
  129. data/lib/dynflow/bus.rb +0 -168
  130. data/lib/dynflow/dispatcher.rb +0 -36
  131. data/lib/dynflow/logger.rb +0 -34
  132. data/lib/dynflow/step.rb +0 -234
  133. data/test/bus_test.rb +0 -150
data/examples/events.rb DELETED
@@ -1,71 +0,0 @@
1
- # Shows how Dynflow can be used for events architecture: actions are
2
- # subscribed to an event. When the event is triggered all the
3
- # subscribed actions are preformed.
4
-
5
- $:.unshift(File.expand_path('../../lib', __FILE__))
6
-
7
- require 'dynflow'
8
- require 'pp'
9
-
10
- # this is an event that can be triggered.
11
- # it has an input format so that the interface is given
12
- # TODO: the validations are turned off right now
13
- class Click < Dynflow::Action
14
- input_format do
15
- param :x, Integer
16
- param :y, Integer
17
- end
18
- end
19
-
20
- # SayHello subscibes to the event: it's run when the event is triggered
21
- class SayHello < Dynflow::Action
22
-
23
- def self.subscribe
24
- Click
25
- end
26
-
27
- def run
28
- puts "Hello World"
29
- end
30
- end
31
-
32
- # we can subscribe more actions to an event
33
- class SayPosition < Dynflow::Action
34
-
35
- def self.subscribe
36
- Click
37
- end
38
-
39
- def run
40
- puts "your position is [#{input['x']} - #{input['y']}]"
41
- end
42
-
43
- end
44
-
45
- # we can even subscribe to an action that is subscribed to an event
46
- class SayGoodbye < Dynflow::Action
47
-
48
- def self.subscribe
49
- SayPosition
50
- end
51
-
52
- def run
53
- puts "Good Bye"
54
- end
55
- end
56
-
57
- Click.trigger('x' => 5, 'y' => 4)
58
- # gives us:
59
- # Hello World
60
- # your position is [5 - 4]
61
- # Good Bye
62
-
63
- pp Click.plan('x' => 5, 'y' => 4).actions
64
- # returns the execution plan for the event (nothing is triggered):
65
- # [
66
- # since the event is action as well, it could have a run method
67
- # [Click: {"x"=>5, "y"=>4} ~> {},
68
- # SayHello: {"x"=>5, "y"=>4} ~> {},
69
- # SayPosition: {"x"=>5, "y"=>4} ~> {},
70
- # SayGoodbye: {"x"=>5, "y"=>4} ~> {}]
71
- # ]
data/examples/workflow.rb DELETED
@@ -1,140 +0,0 @@
1
- # Shows how Dynflow can be used for dynamic workflow definition
2
- # and execution.
3
- # In a planning phase of an action, a sub-action can be planned as
4
- # well.
5
-
6
-
7
- $:.unshift(File.expand_path('../../lib', __FILE__))
8
-
9
- require 'dynflow'
10
- require 'pp'
11
-
12
- class Article < Struct.new(:title, :body, :color); end
13
-
14
- class Publish < Dynflow::Action
15
- input_format do
16
- param :title, Integer
17
- param :body, Integer
18
- end
19
-
20
- # plan can take arbitrary arguments. The args are passed from the
21
- # trigger method.
22
- def plan(article)
23
- # we can explicitly plan a subaction
24
- plan_self 'title' => article.title, 'body' => article.body
25
- plan_action Review, article
26
- end
27
-
28
- def run
29
- puts 'Starting'
30
- end
31
-
32
- # after all actions are run, there is a finishing phase. All the
33
- # actions with +finished+ action defined are called, passing all the
34
- # performed actions (with inputs and outputs)
35
- def finalize(outputs)
36
- printer_action = outputs.find { |o| o.is_a? Print }
37
- puts "Printer says '#{printer_action.output['message']}'"
38
- end
39
- end
40
-
41
- class Review < Dynflow::Action
42
-
43
- # the actions can provide an output for the finalizing phase
44
- output_format do
45
- param :rating, Integer
46
- end
47
-
48
- # the plan method takes the same arguments as the parent action
49
- def plan(article)
50
- # in the input attribute the input for the parent action is
51
- # available
52
- plan_self input
53
- end
54
-
55
- # if no plan method given, the input is the same as the action that
56
- # triggered it
57
- def run
58
- puts "Reviewing #{input['title']}"
59
- raise "Too Short" if input['body'].size < 6
60
- output['rating'] = input['body'].size
61
- end
62
-
63
- def finalize(outputs)
64
- # +input+ and +output+ attributes are available in the finalizing
65
- # phase as well.
66
- puts "The rating was #{output['rating']}"
67
- end
68
-
69
- end
70
-
71
- class Print < Dynflow::Action
72
-
73
- input_format do
74
- param :title, Integer
75
- param :body, Integer
76
- param :color, :boolean
77
- end
78
-
79
- output_format do
80
- param :message, String
81
- end
82
-
83
- # if needed, we can subscribe to an action instead of explicitly
84
- # specifying it in the plan method. Suitable for plugin architecture.
85
- def self.subscribe
86
- Review # sucessful review means we can print
87
- end
88
-
89
- def plan(article)
90
- plan_self input.merge('color' => article.color)
91
- end
92
-
93
- def run
94
- if input['color']
95
- puts "Printing in color"
96
- else
97
- puts "Printing blank&white"
98
- end
99
- output['message'] = "Here you are"
100
- end
101
- end
102
-
103
- short_article = Article.new('Short', 'Short', false)
104
- long_article = Article.new('Long', 'This is long', false)
105
- colorful_article = Article.new('Long Color', 'This is long in color', true)
106
-
107
- pp Publish.plan(short_article).actions
108
- # the expanded workflow is:
109
- # [
110
- # Publish: {"title"=>"Short", "body"=>"Short"} ~> {},
111
- # Review: {"title"=>"Short", "body"=>"Short"} ~> {},
112
- # Print: {"title"=>"Short", "body"=>"Short", "color"=>false} ~> {}
113
- # ]
114
-
115
- begin
116
- Publish.trigger(short_article)
117
- rescue => e
118
- puts e.message
119
- end
120
- # Produces:
121
- # Starting
122
- # Reviewing Short
123
- # Too Short
124
-
125
- Publish.trigger(long_article)
126
- # Produces:
127
- # Starting
128
- # Reviewing Long
129
- # Printing blank&white
130
- # Printer says 'Here you are'
131
- # The rating was 12
132
-
133
-
134
- Publish.trigger(colorful_article)
135
- # Produces:
136
- # Starting
137
- # Reviewing Long Color
138
- # Printing in color
139
- # Printer says 'Here you are'
140
- # The rating was 21
data/lib/dynflow/bus.rb DELETED
@@ -1,168 +0,0 @@
1
- require 'active_support/inflector'
2
- require 'forwardable'
3
- module Dynflow
4
- class Bus
5
-
6
- class << self
7
- extend Forwardable
8
-
9
- def_delegators :impl, :trigger, :resume, :skip, :preview_execution_plan,
10
- :persisted_plans, :persisted_plan, :persisted_step
11
-
12
- def impl
13
- @impl ||= Bus.new
14
- end
15
- attr_writer :impl
16
- end
17
-
18
- # Entry point for running an action
19
- def trigger(action_class, *args)
20
- execution_plan = nil
21
- in_transaction_if_possible do
22
- execution_plan = prepare_execution_plan(action_class, *args)
23
- rollback_transaction if execution_plan.status == 'error'
24
- end
25
- persist_plan_if_possible(execution_plan)
26
- unless execution_plan.status == 'error'
27
- execute(execution_plan)
28
- end
29
- return execution_plan
30
- end
31
-
32
- def prepare_execution_plan(action_class, *args)
33
- action_class.plan(*args)
34
- end
35
-
36
- # execution and finalizaition. Usable for resuming paused plan
37
- # as well as starting from scratch
38
- def execute(execution_plan)
39
- run_execution_plan(execution_plan)
40
- in_transaction_if_possible do
41
- unless self.finalize(execution_plan)
42
- rollback_transaction
43
- end
44
- end
45
- execution_plan.persist(true)
46
- end
47
-
48
- alias_method :resume, :execute
49
-
50
- def skip(step)
51
- step.status = 'skipped'
52
- step.persist
53
- end
54
-
55
- # return true if everyting worked fine
56
- def finalize(execution_plan)
57
- success = true
58
- if execution_plan.run_steps.any? { |action| ['pending', 'error'].include?(action.status) }
59
- success = false
60
- else
61
- execution_plan.finalize_steps.each(&:replace_references!)
62
- execution_plan.finalize_steps.each do |step|
63
- break unless success
64
- next if %w[skipped].include?(step.status)
65
-
66
- success = step.catch_errors do
67
- step.action.finalize(execution_plan.run_steps)
68
- end
69
- end
70
- end
71
-
72
- if success
73
- execution_plan.status = 'finished'
74
- else
75
- execution_plan.status = 'paused'
76
- end
77
- return success
78
- end
79
-
80
- # return true if the run phase finished successfully
81
- def run_execution_plan(execution_plan)
82
- success = true
83
- execution_plan.run_steps.map do |step|
84
- next step if !success || %w[skipped success].include?(step.status)
85
- step.persist_before_run
86
- success = step.catch_errors do
87
- step.output = {}
88
- step.action.run
89
- end
90
- step.persist_after_run
91
- step
92
- end
93
- return success
94
- end
95
-
96
- def transaction_driver
97
- nil
98
- end
99
-
100
- def in_transaction_if_possible
101
- if transaction_driver
102
- ret = nil
103
- transaction_driver.transaction do
104
- ret = yield
105
- end
106
- return ret
107
- else
108
- return yield
109
- end
110
- end
111
-
112
- def rollback_transaction
113
- transaction_driver.rollback if transaction_driver
114
- end
115
-
116
-
117
- def persistence_driver
118
- nil
119
- end
120
-
121
- def persist_plan_if_possible(execution_plan)
122
- if persistence_driver
123
- persistence_driver.persist(execution_plan)
124
- end
125
- end
126
-
127
- def persisted_plans(status = nil)
128
- if persistence_driver
129
- persistence_driver.persisted_plans(status)
130
- else
131
- []
132
- end
133
- end
134
-
135
- def persisted_plan(persistence_id)
136
- if persistence_driver
137
- persistence_driver.persisted_plan(persistence_id)
138
- end
139
- end
140
-
141
- def persisted_step(persistence_id)
142
- if persistence_driver
143
- persistence_driver.persisted_step(persistence_id)
144
- end
145
- end
146
-
147
- # performs the planning phase of an action, but rollbacks any db
148
- # changes done in this phase. Returns the resulting execution
149
- # plan. Suitable for debugging.
150
- def preview_execution_plan(action_class, *args)
151
- unless transaction_driver
152
- raise "Bus doesn't know how to run in transaction"
153
- end
154
-
155
- execution_plan = nil
156
- transaction_driver.transaction do
157
- execution_plan = prepare_execution_plan(action_class, *args)
158
- transaction_driver.rollback
159
- end
160
- return execution_plan
161
- end
162
-
163
- def logger
164
- @logger ||= Dynflow::Logger.new(self.class)
165
- end
166
-
167
- end
168
- end
@@ -1,36 +0,0 @@
1
- module Dynflow
2
- class Dispatcher
3
- class << self
4
- def finalizers
5
- @finalizers ||= Hash.new { |h, k| h[k] = [] }
6
- end
7
-
8
- def subscribed_actions(action)
9
- Action.actions.find_all do |sub_action|
10
- case sub_action.subscribe
11
- when Hash
12
- sub_action.subscribe.keys.include?(action.class)
13
- when Array
14
- sub_action.subscribe.include?(action.class)
15
- else
16
- sub_action.subscribe == action.class
17
- end
18
- end
19
- end
20
-
21
- def execution_plan_for(action, *plan_args)
22
- ordered_actions = subscribed_actions(action).sort_by(&:name)
23
-
24
- execution_plan = ExecutionPlan.new
25
- ordered_actions.each do |action_class|
26
- sub_action_plan = action_class.plan(*plan_args) do |sub_action|
27
- sub_action.input = action.input
28
- sub_action.from_subscription = true
29
- end
30
- execution_plan.concat(sub_action_plan)
31
- end
32
- return execution_plan
33
- end
34
- end
35
- end
36
- end
@@ -1,34 +0,0 @@
1
- require 'forwardable'
2
- require 'logger'
3
-
4
- module Dynflow
5
- class Logger
6
- extend Forwardable
7
-
8
- def_delegators :@impl, :debug, :info, :warn; :error
9
-
10
- class DummyLogger < ::Logger
11
- def initialize(identifier)
12
- super(nil)
13
- end
14
- end
15
-
16
-
17
- def initialize(identifier, impl = nil)
18
- @impl = self.class.logger_class.new(identifier)
19
- end
20
-
21
- class << self
22
-
23
- def logger_class
24
- unless @logger_class
25
- @logger_class ||= DummyLogger
26
- end
27
- return @logger_class
28
- end
29
-
30
- attr_writer :logger_class
31
- end
32
-
33
- end
34
- end