taskflow-mongoid 0.1.0 → 0.1.1
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 +4 -4
- data/README.md +131 -4
- data/images/flow-state.png +0 -0
- data/lib/taskflow/version.rb +1 -1
- data/lib/taskflow/worker.rb +57 -58
- data/lib/taskflow.rb +10 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d7fcb6e49960147821299b3fadcfa8cb29c4ea6
|
4
|
+
data.tar.gz: 3aab13db0e1dc980bc0c2d7ca759d78025f27bdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3265a9cd061760cb56d2e43ce5417b86043834ccb8f7850387eb483aa77d8e6d97f689eef9e616937c853db7d5109819c09885c79ebb1443db4da6fd41fc529
|
7
|
+
data.tar.gz: 9184401ef5b052ecd3761fcb2c50ce7379a1bbdcda05f3d65f141a1938c049bda11dfab4693b07bd4f589f652b174368b4fdd1b4471a362cc04678561cba4bd2
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Taskflow
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Taskflow is a cool rails plugin for creating and schedule task flows. NOTE: taskflow is based sidekiq, and use ActiveRecord/Mongoid as its database adapter, choose the right gem(another version taskflow is also available on my github) for your project.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -22,7 +20,8 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
example
|
23
|
+
Let's see an example first, the 'PlayFlow' has 7 task in total. When we start the flow, the t1(PendingTask) runs first, after t1 finishes, t2,t3,t4 would run in parallel; When t4 is done, the t5(OkTask) begins, when t2,t3,t5 are all done, the SummaryTask start to run, then run the last OkTask.
|
24
|
+
|
26
25
|
```ruby
|
27
26
|
class PlayFlow < Taskflow::Flow
|
28
27
|
NAME = "Play FLow"
|
@@ -141,6 +140,134 @@ Taskflow::Worker JID-d7d0c92da5ab820bc1f66651 INFO: finished-task finished
|
|
141
140
|
Taskflow::Worker JID-d7d0c92da5ab820bc1f66651 INFO: done: 0.021 sec
|
142
141
|
```
|
143
142
|
|
143
|
+
|
144
|
+
## Documentations
|
145
|
+
### Taskflow state diagram
|
146
|
+
flow state
|
147
|
+

|
148
|
+
|
149
|
+
### the Taskflow::Flow
|
150
|
+
First, you should create your taskflow by inherit `Taskflow::Flow`, and you *must implement the `configure` method* to tell taskflow engine the detail info.
|
151
|
+
|
152
|
+
In `configure` method, you can use the keyword `run` to define task:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# the keyword run
|
156
|
+
run Task_Class, name: 'task_name'
|
157
|
+
# task1 would run before another_task_obj
|
158
|
+
run Task_Class, name: 'task1',:before=>another_task_obj
|
159
|
+
# task2 would run after another_task_obj
|
160
|
+
run Task_Class, name: 'task2',:after=>another_task_obj
|
161
|
+
# the wait_task would run after task3,task4,task5 all done
|
162
|
+
run Task_Class, name: 'wait_task',:after=>[task3,task4,task5]
|
163
|
+
# pass some parameter to task, the params would set as task's input
|
164
|
+
run Task_Class, name: 'params_task',params: { :param1=>'abc' }
|
165
|
+
```
|
166
|
+
|
167
|
+
You can use `after` or `before` to specify the schedule order for certain task, if there's no after or before, the current task would just run after the previous task.
|
168
|
+
|
169
|
+
#### 0. config taskflow
|
170
|
+
You can config taskflow worker run in which sidekiq queue(default is `default`), forexample in `config/environments/production.rb`:
|
171
|
+
```ruby
|
172
|
+
Taskflow.configure do |config|
|
173
|
+
config.worker_options = { queue: 'workflow' }
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
#### 1. launch taskflow
|
178
|
+
```ruby
|
179
|
+
# the params would be set as taskflow's input field
|
180
|
+
Taskflow::Flow.launch 'PlayFlow',:params=>{word: 'hello'},:launched_by=>'Jason',:workflow_description=>'description'
|
181
|
+
# check whether can launch taskflow, if there's already a taskflow which has the some taskflow_klass and params, return false
|
182
|
+
Taskflow::Flow.can_launch? 'PlayFlow',:params=>{word: 'hello'},:launched_by=>'Jason',:workflow_description=>'description'
|
183
|
+
```
|
184
|
+
|
185
|
+
#### 2. taskflow control
|
186
|
+
```ruby
|
187
|
+
# Taskflow::Flow#stop! stop taskflow
|
188
|
+
flow.stop!
|
189
|
+
flow.stop! 'tom' # => stopped by tom
|
190
|
+
# Taskflow::Flow#resume, resume paused flow
|
191
|
+
flow.resume
|
192
|
+
```
|
193
|
+
|
194
|
+
### the Taskflow::Task
|
195
|
+
Define your own taskflow task. Inherit the class `Taskflow::Task`, and *implement go method*.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
def go(logger)
|
199
|
+
# write your task code here
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
#### 1.Sidekiq logger
|
204
|
+
the parameter `logger` of `Taskflow::Task#go` is sidekiq logger, so you can use it to log to sidekiq log file for debug.
|
205
|
+
|
206
|
+
#### 2.taskflow logger
|
207
|
+
There's another logger in `Taskflow::Task#go` : `tflogger`, `tflogger` can write log information to database. for example:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
def go(logger)
|
211
|
+
tflogger.info 'the info message would write to database'
|
212
|
+
tflogger.error 'this error message would write to database,too'
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
#### 3. input & output
|
217
|
+
There's a very cool feature. Every task has its own `input` and `ouput`.
|
218
|
+
```ruby
|
219
|
+
def go(logger)
|
220
|
+
puts input # => the input hash
|
221
|
+
puts input[:some_key] # => get the input value of the key 'some_key'
|
222
|
+
puts upstream.first.ouput # => get the first upstream's ouput, also is a hash
|
223
|
+
end
|
224
|
+
```
|
225
|
+
|
226
|
+
You have the `set/append_xxx` to modify the input and output.
|
227
|
+
```ruby
|
228
|
+
def go(log)
|
229
|
+
set_output :some_key=>'value' # => set the output to { :some_key=> 'value'}
|
230
|
+
append_output :some_key2=>'value2' # => add { :some_key2=>'value2' } to output
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
#### 4. data
|
235
|
+
Every task has its own data. After the task is done, the data would be cleanned.
|
236
|
+
```ruby
|
237
|
+
def go(log)
|
238
|
+
puts data # => print data
|
239
|
+
puts data[:key] # => access data
|
240
|
+
set_data :key=>'value'
|
241
|
+
append_data :key=>'value'
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
#### 5. relationship
|
246
|
+
In the task, you can access its upstream and downstream.
|
247
|
+
```ruby
|
248
|
+
def go(log)
|
249
|
+
upstream.each{|task| puts task.name }
|
250
|
+
upstream.each{|task| puts task.output }
|
251
|
+
puts downstream.first.name
|
252
|
+
end
|
253
|
+
```
|
254
|
+
|
255
|
+
#### 6. task control
|
256
|
+
```ruby
|
257
|
+
task.resume # => resume paused task
|
258
|
+
task.wakeup(hash_data) # => wakeup suspend task with some hash data
|
259
|
+
task.wakeup # => just wakeup
|
260
|
+
task.skip # => skip paused task
|
261
|
+
```
|
262
|
+
And, in `Taskflow::Task#go`, you can use keyword `suspend` to suspend current task, then the task result would convert to `suspend`,state would be `paused`.
|
263
|
+
```ruby
|
264
|
+
def go(log)
|
265
|
+
log.info 'before suspend'
|
266
|
+
suspend # => the task would be suspend right now.
|
267
|
+
log.info 'never print me'
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
144
271
|
## Development
|
145
272
|
|
146
273
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
Binary file
|
data/lib/taskflow/version.rb
CHANGED
data/lib/taskflow/worker.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
class Taskflow::Worker
|
3
3
|
include ::Sidekiq::Worker
|
4
|
-
sidekiq_options :retry => false
|
5
4
|
|
6
5
|
def perform(task_flow_id,job_id,opts={})
|
7
6
|
flow = Taskflow::Flow.find task_flow_id
|
@@ -28,63 +27,63 @@ class Taskflow::Worker
|
|
28
27
|
rescue=>exception
|
29
28
|
task.error = {
|
30
29
|
class: exception.class.to_s,
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
message: exception.to_s,
|
31
|
+
backtrace: exception.backtrace
|
32
|
+
}
|
33
|
+
task.state = 'paused'
|
34
|
+
task.result = 'error'
|
35
|
+
task.ended_at = Time.now
|
36
|
+
task.save
|
37
|
+
end
|
38
|
+
update_flow flow.reload
|
39
|
+
flow.schedule
|
40
|
+
end
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
42
|
+
private
|
43
|
+
def check_flow_state(flow)
|
44
|
+
if flow.state == 'stopped' || flow.halt_by
|
45
|
+
throw :control, :flow_halt
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def check_task_state(task)
|
49
|
+
case task.state
|
50
|
+
when 'pending'
|
51
|
+
task.update_attributes state: 'running'
|
52
|
+
when 'running'
|
53
|
+
throw :control, :already_running
|
54
|
+
when 'paused'
|
55
|
+
throw :control, :suspend if task.result == 'suspend'
|
56
|
+
when 'stopped'
|
57
|
+
throw :control, :already_stopped
|
58
|
+
when 'skipped'
|
59
|
+
throw :control,:skip
|
60
|
+
else
|
61
|
+
raise "Unkown task state #{task.state}"
|
62
|
+
end
|
63
|
+
end
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
65
|
+
def update_flow(flow)
|
66
|
+
return if flow.halt_by || flow.state == 'stopped'
|
67
|
+
flow.progress = flow.tasks.map(&:progress).sum / flow.tasks.size
|
68
|
+
if flow.halt_by
|
69
|
+
flow.state = 'stopped'
|
70
|
+
elsif flow.tasks.all?{|t| %w(stopped skipped).include? t.state }
|
71
|
+
flow.state = 'stopped'
|
72
|
+
elsif flow.tasks.any?{|t| t.state == 'paused' }
|
73
|
+
flow.state = 'paused'
|
74
|
+
flow.result = flow.tasks.find_by(state: 'paused').result
|
75
|
+
else
|
76
|
+
flow.state = 'running'
|
77
|
+
end
|
78
|
+
if flow.state == 'stopped'
|
79
|
+
flow.result = flow.tasks.all?{|t| t.result == 'success' } ? 'success' : 'warning'
|
80
|
+
flow.ended_at = Time.now
|
81
|
+
if flow.next_config
|
82
|
+
logger.info "Auto boot next flow, #{flow.next_config}"
|
83
|
+
Taskflow::Flow.launch flow.next_config[:name],flow.next_config[:config]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
flow.save
|
87
|
+
end
|
89
88
|
|
90
|
-
|
89
|
+
end
|
data/lib/taskflow.rb
CHANGED
@@ -7,5 +7,14 @@ require 'taskflow/logger'
|
|
7
7
|
require 'taskflow/record'
|
8
8
|
|
9
9
|
module Taskflow
|
10
|
-
|
10
|
+
|
11
|
+
def self.worker_options=(opts)
|
12
|
+
orig = HashWithIndifferentAccess.new(Worker.sidekiq_options_hash || {})
|
13
|
+
Worker.sidekiq_options_hash = orig.merge(opts).merge(retry: false)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield self
|
18
|
+
end
|
11
19
|
end
|
20
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taskflow-mongoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- qujianping
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- Rakefile
|
82
82
|
- bin/console
|
83
83
|
- bin/setup
|
84
|
+
- images/flow-state.png
|
84
85
|
- lib/taskflow.rb
|
85
86
|
- lib/taskflow/flow.rb
|
86
87
|
- lib/taskflow/logger.rb
|