taskinator 0.0.18 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile.lock +28 -28
  4. data/README.md +29 -0
  5. data/Rakefile +5 -0
  6. data/bin/console +5 -0
  7. data/lib/taskinator/create_process_worker.rb +5 -2
  8. data/lib/taskinator/definition/builder.rb +12 -7
  9. data/lib/taskinator/definition.rb +36 -28
  10. data/lib/taskinator/executor.rb +4 -4
  11. data/lib/taskinator/job_worker.rb +5 -10
  12. data/lib/taskinator/logger.rb +1 -0
  13. data/lib/taskinator/persistence.rb +74 -36
  14. data/lib/taskinator/process.rb +75 -49
  15. data/lib/taskinator/queues/delayed_job.rb +1 -11
  16. data/lib/taskinator/queues/resque.rb +0 -15
  17. data/lib/taskinator/queues/sidekiq.rb +1 -14
  18. data/lib/taskinator/queues.rb +0 -5
  19. data/lib/taskinator/redis_connection.rb +1 -0
  20. data/lib/taskinator/task.rb +57 -57
  21. data/lib/taskinator/task_worker.rb +1 -8
  22. data/lib/taskinator/version.rb +1 -1
  23. data/lib/taskinator.rb +7 -6
  24. data/spec/examples/queue_adapter_examples.rb +0 -10
  25. data/spec/support/test_definition.rb +4 -0
  26. data/spec/support/test_flow.rb +2 -0
  27. data/spec/support/test_flows.rb +54 -3
  28. data/spec/support/test_queue.rb +41 -6
  29. data/spec/taskinator/create_process_worker_spec.rb +12 -3
  30. data/spec/taskinator/definition/builder_spec.rb +39 -9
  31. data/spec/taskinator/definition_spec.rb +19 -27
  32. data/spec/taskinator/executor_spec.rb +19 -1
  33. data/spec/taskinator/job_worker_spec.rb +0 -11
  34. data/spec/taskinator/persistence_spec.rb +122 -7
  35. data/spec/taskinator/process_spec.rb +39 -23
  36. data/spec/taskinator/queues/delayed_job_spec.rb +1 -19
  37. data/spec/taskinator/queues/resque_spec.rb +1 -22
  38. data/spec/taskinator/queues/sidekiq_spec.rb +1 -20
  39. data/spec/taskinator/task_spec.rb +160 -52
  40. data/spec/taskinator/task_worker_spec.rb +0 -17
  41. data/spec/taskinator/test_flows_spec.rb +43 -0
  42. metadata +2 -5
  43. data/lib/taskinator/process_worker.rb +0 -21
  44. data/spec/taskinator/process_worker_spec.rb +0 -51
@@ -1,3 +1,6 @@
1
+ require 'thread'
2
+ require 'thwait'
3
+
1
4
  module Taskinator
2
5
  class Process
3
6
  include ::Comparable
@@ -60,24 +63,21 @@ module Taskinator
60
63
  state :initial do
61
64
  event :enqueue, :transitions_to => :enqueued
62
65
  event :start, :transitions_to => :processing
63
-
64
- # need to be able to complete, for when there are no tasks
65
- event :complete, :transitions_to => :completed, :if => :no_tasks_defined?
66
-
66
+ event :complete, :transitions_to => :completed
67
67
  event :cancel, :transitions_to => :cancelled
68
68
  event :fail, :transitions_to => :failed
69
69
  end
70
70
 
71
71
  state :enqueued do
72
72
  event :start, :transitions_to => :processing
73
- event :complete, :transitions_to => :completed, :if => :no_tasks_defined?
73
+ event :complete, :transitions_to => :completed
74
74
  event :cancel, :transitions_to => :cancelled
75
75
  event :fail, :transitions_to => :failed
76
76
  end
77
77
 
78
78
  state :processing do
79
79
  event :pause, :transitions_to => :paused
80
- event :complete, :transitions_to => :completed, :if => :tasks_completed?
80
+ event :complete, :transitions_to => :completed
81
81
  event :fail, :transitions_to => :failed
82
82
  end
83
83
 
@@ -95,19 +95,21 @@ module Taskinator
95
95
  Taskinator.logger.debug("PROCESS: #{self.class.name}:#{uuid} :: #{from} => #{to}")
96
96
  end
97
97
 
98
- on_error do |error, from, to, event, *args|
99
- Taskinator.logger.error("PROCESS: #{self.class.name}:#{uuid} :: #{error.message}")
100
- Taskinator.logger.debug(error.backtrace)
101
- fail!(error)
102
- end
103
98
  end
104
99
 
105
100
  def no_tasks_defined?
106
101
  tasks.empty?
107
102
  end
108
103
 
104
+ # callback for when the process was cancelled
105
+ def on_cancelled_entry(*args)
106
+ Taskinator.instrumenter.instrument('taskinator.process.cancelled', instrumentation_payload) do
107
+ # intentionally left empty
108
+ end
109
+ end
110
+
111
+ # subclasses must implement this method
109
112
  def tasks_completed?(*args)
110
- # subclasses must implement this method
111
113
  raise NotImplementedError
112
114
  end
113
115
 
@@ -121,37 +123,42 @@ module Taskinator
121
123
  # need to override the ones defined by workflow
122
124
  include Persistence
123
125
 
124
- def enqueue
125
- # don't bother if there aren't any tasks!
126
- if tasks.empty?
127
- # simply complete the process...
128
- complete!
129
- else
130
- Taskinator.queue.enqueue_process(self)
126
+ def complete
127
+ Taskinator.instrumenter.instrument('taskinator.process.completed', instrumentation_payload) do
128
+ # notify the parent task (if there is one) that this process has completed
129
+ # note: parent may be a proxy, so explicity check for nil?
130
+ parent.complete! unless parent.nil?
131
131
  end
132
132
  end
133
133
 
134
- # callback for when the process has completed
135
- def on_completed_entry(*args)
136
- # notify the parent task (if there is one) that this process has completed
137
- # note: parent may be a proxy, so explicity check for nil?
138
- parent.complete! unless parent.nil?
139
- end
140
-
141
134
  # callback for when the process has failed
142
135
  def on_failed_entry(*args)
143
- # notify the parent task (if there is one) that this process has failed
144
- # note: parent may be a proxy, so explicity check for nil?
145
- parent.fail!(*args) unless parent.nil?
136
+ Taskinator.instrumenter.instrument('taskinator.process.failed', instrumentation_payload) do
137
+ # notify the parent task (if there is one) that this process has failed
138
+ # note: parent may be a proxy, so explicity check for nil?
139
+ parent.fail!(*args) unless parent.nil?
140
+ end
146
141
  end
147
142
 
148
143
  class Sequential < Process
144
+ def enqueue
145
+ Taskinator.instrumenter.instrument('taskinator.process.enqueued', instrumentation_payload) do
146
+ if tasks.empty?
147
+ complete! # weren't any tasks to start with
148
+ else
149
+ tasks.first.enqueue!
150
+ end
151
+ end
152
+ end
153
+
149
154
  def start
150
- task = tasks.first
151
- if task
152
- task.enqueue!
153
- else
154
- complete! # weren't any tasks to start with
155
+ Taskinator.instrumenter.instrument('taskinator.process.started', instrumentation_payload) do
156
+ task = tasks.first
157
+ if task
158
+ task.start!
159
+ else
160
+ complete! # weren't any tasks to start with
161
+ end
155
162
  end
156
163
  end
157
164
 
@@ -160,7 +167,7 @@ module Taskinator
160
167
  if next_task
161
168
  next_task.enqueue!
162
169
  else
163
- complete! if can_complete?
170
+ complete!
164
171
  end
165
172
  end
166
173
 
@@ -176,28 +183,55 @@ module Taskinator
176
183
 
177
184
  class Concurrent < Process
178
185
  attr_reader :complete_on
186
+ attr_reader :concurrency_method
179
187
 
180
188
  def initialize(definition, complete_on=CompleteOn::Default, options={})
181
189
  super(definition, options)
182
190
  @complete_on = complete_on
191
+ @concurrency_method = options.delete(:concurrency_method) || :thread
192
+ end
193
+
194
+ def enqueue
195
+ Taskinator.instrumenter.instrument('taskinator.process.enqueued', instrumentation_payload) do
196
+ if tasks.empty?
197
+ complete! # weren't any tasks to start with
198
+ else
199
+ tasks.each(&:enqueue!)
200
+ end
201
+ end
183
202
  end
184
203
 
185
204
  def start
186
- if tasks.empty?
187
- complete! # weren't any tasks to start with
188
- else
189
- tasks.each(&:enqueue!)
205
+ Taskinator.instrumenter.instrument('taskinator.process.started', instrumentation_payload) do
206
+ if tasks.empty?
207
+ complete! # weren't any tasks to start with
208
+ else
209
+ if concurrency_method == :fork
210
+ tasks.each do |task|
211
+ fork do
212
+ task.start!
213
+ end
214
+ end
215
+ Process.waitall
216
+ else
217
+ threads = tasks.map do |task|
218
+ Thread.new do
219
+ task.start!
220
+ end
221
+ end
222
+ ThreadsWait.all_waits(*threads)
223
+ end
224
+ end
190
225
  end
191
226
  end
192
227
 
193
228
  def task_completed(task)
194
229
  # when complete on first, then don't bother with subsequent tasks completing
195
230
  return if completed? || failed?
196
- complete! if can_complete?
231
+ complete!
197
232
  end
198
233
 
199
234
  def tasks_completed?(*args)
200
- # TODO: optimize this
201
235
  if (complete_on == CompleteOn::First)
202
236
  tasks.any?(&:completed?)
203
237
  else
@@ -214,13 +248,5 @@ module Taskinator
214
248
  %(#<#{self.class.name}:0x#{self.__id__.to_s(16)} uuid="#{uuid}", state=:#{current_state.name}, complete_on=:#{complete_on}, tasks=[#{tasks.inspect}]>)
215
249
  end
216
250
  end
217
-
218
- # reloads the process from storage
219
- # NB: only implemented by LazyLoader so that
220
- # the process can be lazy loaded, thereafter
221
- # it has no effect
222
- def reload
223
- false
224
- end
225
251
  end
226
252
  end
@@ -17,11 +17,6 @@ module Taskinator
17
17
  ::Delayed::Job.enqueue CreateProcessWorker.new(definition.name, uuid, Taskinator::Persistence.serialize(args)), :queue => queue
18
18
  end
19
19
 
20
- def enqueue_process(process)
21
- queue = process.queue || @config[:process_queue]
22
- ::Delayed::Job.enqueue ProcessWorker.new(process.uuid), :queue => queue
23
- end
24
-
25
20
  def enqueue_task(task)
26
21
  queue = task.queue || @config[:task_queue]
27
22
  ::Delayed::Job.enqueue TaskWorker.new(task.uuid), :queue => queue
@@ -39,12 +34,6 @@ module Taskinator
39
34
  end
40
35
  end
41
36
 
42
- ProcessWorker = Struct.new(:process_uuid) do
43
- def perform
44
- Taskinator::ProcessWorker.new(process_uuid).perform
45
- end
46
- end
47
-
48
37
  TaskWorker = Struct.new(:task_uuid) do
49
38
  def perform
50
39
  Taskinator::TaskWorker.new(task_uuid).perform
@@ -58,6 +47,7 @@ module Taskinator
58
47
  end
59
48
  end
60
49
  end
50
+
61
51
  end
62
52
  end
63
53
  end
@@ -15,10 +15,6 @@ module Taskinator
15
15
  @queue = config[:definition_queue]
16
16
  end
17
17
 
18
- ProcessWorker.class_eval do
19
- @queue = config[:process_queue]
20
- end
21
-
22
18
  TaskWorker.class_eval do
23
19
  @queue = config[:task_queue]
24
20
  end
@@ -33,11 +29,6 @@ module Taskinator
33
29
  Resque.enqueue_to(queue, CreateProcessWorker, definition.name, uuid, Taskinator::Persistence.serialize(args))
34
30
  end
35
31
 
36
- def enqueue_process(process)
37
- queue = process.queue || Resque.queue_from_class(ProcessWorker)
38
- Resque.enqueue_to(queue, ProcessWorker, process.uuid)
39
- end
40
-
41
32
  def enqueue_task(task)
42
33
  queue = task.queue || Resque.queue_from_class(TaskWorker)
43
34
  Resque.enqueue_to(queue, TaskWorker, task.uuid)
@@ -57,12 +48,6 @@ module Taskinator
57
48
  end
58
49
  end
59
50
 
60
- class ProcessWorker
61
- def self.perform(process_uuid)
62
- Taskinator::ProcessWorker.new(process_uuid).perform
63
- end
64
- end
65
-
66
51
  class TaskWorker
67
52
  def self.perform(task_uuid)
68
53
  Taskinator::TaskWorker.new(task_uuid).perform
@@ -14,12 +14,7 @@ module Taskinator
14
14
 
15
15
  def enqueue_create_process(definition, uuid, args)
16
16
  queue = definition.queue || @config[:definition_queue]
17
- ProcessWorker.client_push('class' => CreateProcessWorker, 'args' => [definition.name, uuid, Taskinator::Persistence.serialize(args)], 'queue' => queue)
18
- end
19
-
20
- def enqueue_process(process)
21
- queue = process.queue || @config[:process_queue]
22
- ProcessWorker.client_push('class' => ProcessWorker, 'args' => [process.uuid], 'queue' => queue)
17
+ CreateProcessWorker.client_push('class' => CreateProcessWorker, 'args' => [definition.name, uuid, Taskinator::Persistence.serialize(args)], 'queue' => queue)
23
18
  end
24
19
 
25
20
  def enqueue_task(task)
@@ -40,14 +35,6 @@ module Taskinator
40
35
  end
41
36
  end
42
37
 
43
- class ProcessWorker
44
- include ::Sidekiq::Worker
45
-
46
- def perform(process_uuid)
47
- Taskinator::ProcessWorker.new(process_uuid).perform
48
- end
49
- end
50
-
51
38
  class TaskWorker
52
39
  include ::Sidekiq::Worker
53
40
 
@@ -34,11 +34,6 @@ module Taskinator
34
34
  adapter.enqueue_create_process(definition, uuid, args)
35
35
  end
36
36
 
37
- def enqueue_process(process)
38
- Taskinator.logger.info("Enqueuing process #{process}")
39
- adapter.enqueue_process(process)
40
- end
41
-
42
37
  def enqueue_task(task)
43
38
  Taskinator.logger.info("Enqueuing task #{task}")
44
39
  adapter.enqueue_task(task)
@@ -14,6 +14,7 @@ require 'connection_pool'
14
14
  require 'redis'
15
15
  require 'uri'
16
16
 
17
+ # :nocov:
17
18
  module Taskinator
18
19
  class RedisConnection
19
20
  class << self
@@ -58,20 +58,18 @@ module Taskinator
58
58
  state :initial do
59
59
  event :enqueue, :transitions_to => :enqueued
60
60
  event :start, :transitions_to => :processing
61
+ event :complete, :transitions_to => :completed # specific to a SubProcess which has no tasks
61
62
  event :fail, :transitions_to => :failed
62
63
  end
63
64
 
64
65
  state :enqueued do
65
66
  event :start, :transitions_to => :processing
66
-
67
- # need to be able to complete, for when sub-tasks have no tasks
68
- event :complete, :transitions_to => :completed, :if => :can_complete_task?
69
-
67
+ event :complete, :transitions_to => :completed
70
68
  event :fail, :transitions_to => :failed
71
69
  end
72
70
 
73
71
  state :processing do
74
- event :complete, :transitions_to => :completed, :if => :can_complete_task?
72
+ event :complete, :transitions_to => :completed
75
73
  event :fail, :transitions_to => :failed
76
74
  end
77
75
 
@@ -82,16 +80,6 @@ module Taskinator
82
80
  Taskinator.logger.debug("TASK: #{self.class.name}:#{uuid} :: #{from} => #{to}")
83
81
  end
84
82
 
85
- on_error do |error, from, to, event, *args|
86
- Taskinator.logger.error("TASK: #{self.class.name}:#{uuid} :: #{error.message}")
87
- Taskinator.logger.debug(error.backtrace)
88
- fail!(error)
89
- end
90
- end
91
-
92
- def can_complete_task?(*args)
93
- # subclasses must implement this method
94
- raise NotImplementedError
95
83
  end
96
84
 
97
85
  # include after defining the workflow
@@ -99,27 +87,28 @@ module Taskinator
99
87
  # need to override the ones defined by workflow
100
88
  include Persistence
101
89
 
102
- def enqueue
103
- Taskinator.queue.enqueue_task(self)
104
- end
105
-
106
- # callback for when the task has completed
107
- def on_completed_entry(*args)
108
- self.incr_completed
109
- # notify the process that this task has completed
110
- process.task_completed(self)
90
+ def complete
91
+ Taskinator.instrumenter.instrument('taskinator.task.completed', instrumentation_payload) do
92
+ # notify the process that this task has completed
93
+ process.task_completed(self)
94
+ self.incr_completed
95
+ end
111
96
  end
112
97
 
113
98
  # callback for when the task has failed
114
99
  def on_failed_entry(*args)
115
- self.incr_failed
116
- # notify the process that this task has failed
117
- process.task_failed(self, args.last)
100
+ Taskinator.instrumenter.instrument('taskinator.task.failed', instrumentation_payload) do
101
+ self.incr_failed
102
+ # notify the process that this task has failed
103
+ process.task_failed(self, args.last)
104
+ end
118
105
  end
119
106
 
120
107
  # callback for when the task has cancelled
121
108
  def on_cancelled_entry(*args)
122
- self.incr_cancelled
109
+ Taskinator.instrumenter.instrument('taskinator.task.cancelled', instrumentation_payload) do
110
+ self.incr_cancelled
111
+ end
123
112
  end
124
113
 
125
114
  # helper method, delegating to process
@@ -150,21 +139,24 @@ module Taskinator
150
139
  @args = args
151
140
  end
152
141
 
153
- def executor
154
- @executor ||= Taskinator::Executor.new(@definition, self)
142
+ def enqueue
143
+ Taskinator.instrumenter.instrument('taskinator.task.enqueued', instrumentation_payload) do
144
+ Taskinator.queue.enqueue_task(self)
145
+ end
155
146
  end
156
147
 
157
148
  def start
158
- # ASSUMPTION: when the method returns, the task is considered to be complete
159
- Taskinator.instrumenter.instrument(:execute_step_task, :uuid => uuid) do
149
+ Taskinator.instrumenter.instrument('taskinator.task.started', instrumentation_payload) do
160
150
  executor.send(method, *args)
161
151
  end
162
- @is_complete = true
163
- end
152
+ # ASSUMPTION: when the method returns, the task is considered to be complete
153
+ complete!
164
154
 
165
- # NOTE: this _does not_ work when checking out-of-process
166
- def can_complete_task?
167
- defined?(@is_complete) && @is_complete
155
+ rescue => e
156
+ Taskinator.logger.error(e)
157
+ Taskinator.logger.debug(e.backtrace)
158
+ fail!(e)
159
+ raise e
168
160
  end
169
161
 
170
162
  def accept(visitor)
@@ -174,6 +166,10 @@ module Taskinator
174
166
  visitor.visit_args(:args)
175
167
  end
176
168
 
169
+ def executor
170
+ @executor ||= Taskinator::Executor.new(@definition, self)
171
+ end
172
+
177
173
  def inspect
178
174
  %(#<#{self.class.name}:0x#{self.__id__.to_s(16)} uuid="#{uuid}", method=:#{method}, args=#{args}, state=:#{current_state.name}>)
179
175
  end
@@ -198,19 +194,24 @@ module Taskinator
198
194
  end
199
195
 
200
196
  def enqueue
201
- Taskinator.queue.enqueue_job(self)
197
+ Taskinator.instrumenter.instrument('taskinator.task.enqueued', instrumentation_payload) do
198
+ Taskinator.queue.enqueue_job(self)
199
+ end
202
200
  end
203
201
 
202
+ # can't use the start! method, since a block is required
204
203
  def perform
205
- Taskinator.instrumenter.instrument(:execute_job_task, :uuid => uuid) do
204
+ Taskinator.instrumenter.instrument('taskinator.task.started', instrumentation_payload) do
206
205
  yield(job, args)
207
206
  end
208
- @is_complete = true
209
- end
207
+ # ASSUMPTION: when the method returns, the task is considered to be complete
208
+ complete!
210
209
 
211
- # NOTE: this _does not_ work when checking out-of-process
212
- def can_complete_task?
213
- defined?(@is_complete) && @is_complete
210
+ rescue => e
211
+ Taskinator.logger.error(e)
212
+ Taskinator.logger.debug(e.backtrace)
213
+ fail!(e)
214
+ raise e
214
215
  end
215
216
 
216
217
  def accept(visitor)
@@ -237,15 +238,22 @@ module Taskinator
237
238
  @sub_process.parent = self
238
239
  end
239
240
 
241
+ def enqueue
242
+ Taskinator.instrumenter.instrument('taskinator.task.enqueued', instrumentation_payload) do
243
+ sub_process.enqueue!
244
+ end
245
+ end
246
+
240
247
  def start
241
- Taskinator.instrumenter.instrument(:execute_subprocess, :uuid => uuid) do
248
+ Taskinator.instrumenter.instrument('taskinator.task.started', instrumentation_payload) do
242
249
  sub_process.start!
243
250
  end
244
- end
245
251
 
246
- def can_complete_task?
247
- # NOTE: this works out-of-process, so there isn't any issue
248
- sub_process.completed?
252
+ rescue => e
253
+ Taskinator.logger.error(e)
254
+ Taskinator.logger.debug(e.backtrace)
255
+ fail!(e)
256
+ raise e
249
257
  end
250
258
 
251
259
  def accept(visitor)
@@ -257,13 +265,5 @@ module Taskinator
257
265
  %(#<#{self.class.name}:0x#{self.__id__.to_s(16)} uuid="#{uuid}", sub_process=#{sub_process.inspect}, state=:#{current_state.name}>)
258
266
  end
259
267
  end
260
-
261
- # reloads the task from storage
262
- # NB: only implemented by LazyLoader so that
263
- # the task can be lazy loaded, thereafter
264
- # it has no effect
265
- def reload
266
- false
267
- end
268
268
  end
269
269
  end
@@ -9,14 +9,7 @@ module Taskinator
9
9
  def perform
10
10
  task = Taskinator::Task.fetch(@uuid)
11
11
  return if task.paused? || task.cancelled?
12
- begin
13
- task.start!
14
- task.complete! if task.can_complete?
15
- rescue => e
16
- Taskinator.logger.error(e)
17
- task.fail!(e)
18
- raise e
19
- end
12
+ task.start!
20
13
  end
21
14
  end
22
15
  end
@@ -1,3 +1,3 @@
1
1
  module Taskinator
2
- VERSION = "0.0.18"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/taskinator.rb CHANGED
@@ -20,7 +20,6 @@ require 'taskinator/process'
20
20
 
21
21
  require 'taskinator/task_worker'
22
22
  require 'taskinator/job_worker'
23
- require 'taskinator/process_worker'
24
23
  require 'taskinator/create_process_worker'
25
24
 
26
25
  require 'taskinator/executor'
@@ -99,9 +98,11 @@ module Taskinator
99
98
  end
100
99
 
101
100
  def queue
102
- adapter = self.queue_adapter || :resque
103
- config = queue_config || {}
104
- @queue ||= Taskinator::Queues.create_adapter(adapter, config)
101
+ @queue ||= begin
102
+ adapter = self.queue_adapter || :resque
103
+ config = queue_config || {}
104
+ Taskinator::Queues.create_adapter(adapter, config)
105
+ end
105
106
  end
106
107
 
107
108
  # set the instrumenter to use.
@@ -116,14 +117,14 @@ module Taskinator
116
117
  end
117
118
 
118
119
  class NoOpInstrumenter
119
- # just yield to the given block
120
+ # :nocov:
120
121
  def instrument(event, payload={})
121
122
  yield(payload) if block_given?
122
123
  end
123
124
  end
124
125
 
125
126
  class ConsoleInstrumenter
126
- # just yield to the given block
127
+ # :nocov:
127
128
  def instrument(event, payload={})
128
129
  puts [event.inspect, payload.to_yaml]
129
130
  yield(payload) if block_given?
@@ -20,16 +20,6 @@ shared_examples_for "a queue adapter" do |adapter_name, adapter_type|
20
20
  end
21
21
  end
22
22
 
23
- describe "#enqueue_process" do
24
- it { expect(subject).to respond_to(:enqueue_process) }
25
-
26
- it "should enqueue a process" do
27
- expect {
28
- subject.enqueue_process(double('process', :uuid => 'xx-xx-xx-xx', :queue => nil))
29
- }.to_not raise_error
30
- end
31
- end
32
-
33
23
  describe "#enqueue_task" do
34
24
  it { expect(subject).to respond_to(:enqueue_task) }
35
25
 
@@ -1,3 +1,7 @@
1
1
  module TestDefinition
2
2
  extend Taskinator::Definition
3
+
4
+ def do_task(*args)
5
+ end
6
+
3
7
  end
@@ -2,6 +2,8 @@ module TestFlow
2
2
  extend Taskinator::Definition
3
3
 
4
4
  define_process :some_arg1, :some_arg2 do
5
+
6
+ # TODO: add support for "continue_on_error"
5
7
  task :error_task, :continue_on_error => true
6
8
 
7
9
  task :the_task