dwf 0.1.6 → 0.1.10
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/.github/workflows/build_gem.yaml +0 -4
- data/.github/workflows/test.yaml +1 -1
- data/CHANGELOG.md +81 -0
- data/README.md +62 -9
- data/dwf.gemspec +2 -1
- data/lib/dwf/callback.rb +23 -29
- data/lib/dwf/client.rb +41 -1
- data/lib/dwf/configuration.rb +15 -0
- data/lib/dwf/errors.rb +3 -0
- data/lib/dwf/item.rb +43 -17
- data/lib/dwf/version.rb +5 -0
- data/lib/dwf/worker.rb +3 -1
- data/lib/dwf/workflow.rb +31 -15
- data/lib/dwf.rb +9 -1
- data/spec/dwf/client_spec.rb +177 -0
- data/spec/dwf/configuration_spec.rb +12 -0
- data/spec/dwf/item_spec.rb +37 -1
- data/spec/dwf/worker_spec.rb +32 -0
- data/spec/dwf/workflow_spec.rb +184 -0
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fba0bf3a5195787db6540ddfe2431a265b09b78859f333ed9295b4e4677d0a25
|
4
|
+
data.tar.gz: 66488a6107dec45933fc1631e713db3fb43a6e31dbd5d5a351a79a6bbb31f1f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88c01767c12ae11ff5f2aaeaf68b08a8c04a54bb909c51babd0f02cd915b1c8d790131c197034bbce46a8ab4d8e0dfa40a0f7afe4f1353bff65d952654438454
|
7
|
+
data.tar.gz: 799827548d718f4ae763c00c4519f636f448eb0aba40e6d5a60f83443cf2f479b3d8fbfd536744e11fc9092d22be3a18a7bb764b25cafcb27eccef3168ab0170
|
data/.github/workflows/test.yaml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,86 @@
|
|
1
1
|
# Changelog
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
|
+
## 0.1.10
|
4
|
+
### Added
|
5
|
+
- Allow to use argument within workflow and update the defining callback way
|
6
|
+
```
|
7
|
+
class TestWf < Dwf::Workflow
|
8
|
+
def configure(arguments)
|
9
|
+
run A
|
10
|
+
run B, after: A, params: argument
|
11
|
+
run C, after: A, params: argument
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
wf = TestWf.create(arguments)
|
16
|
+
wf.callback_type = Dwf::Workflow::SK_BATCH
|
17
|
+
|
18
|
+
```
|
19
|
+
- Support `find` workflow and `reload` workflow
|
20
|
+
```
|
21
|
+
wf = TestWf.create
|
22
|
+
Dwf::Workflow.find(wf.id)
|
23
|
+
wf.reload
|
24
|
+
```
|
25
|
+
|
26
|
+
## 0.1.9
|
27
|
+
### Added
|
28
|
+
### Fixed
|
29
|
+
- fix incorrect argument at configuration
|
30
|
+
|
31
|
+
## 0.1.8
|
32
|
+
### Added
|
33
|
+
- add pinlining feature
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class SendOutput < Dwf::Item
|
37
|
+
def perform
|
38
|
+
output('it works')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
```
|
43
|
+
|
44
|
+
`output` method used to output data from the job to add outgoing jobs
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class ReceiveOutput < Dwf::Item
|
48
|
+
def perform
|
49
|
+
message = payloads.first[:output] # it works
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
`payloads` is an array that containing outputs from incoming jobs
|
55
|
+
|
56
|
+
```
|
57
|
+
[
|
58
|
+
{
|
59
|
+
id: "SendOutput|1849a3f9-5fce-401e-a73a-91fc1048356",
|
60
|
+
class: "SendOutput",
|
61
|
+
output: 'it works'
|
62
|
+
}
|
63
|
+
]
|
64
|
+
```
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
Dwf.config do |config|
|
68
|
+
config.opts = { url 'redis://127.0.0.1:6379' }
|
69
|
+
config.namespace = 'dwf'
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## 0.1.7
|
74
|
+
### Added
|
75
|
+
- Allow to config redis and queue
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Dwf.config do |config|
|
79
|
+
config.opts = { url 'redis://127.0.0.1:6379' }
|
80
|
+
config.namespace = 'dwf'
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
3
84
|
## 0.1.6
|
4
85
|
### Added
|
5
86
|
- Sidekiq batch callback: separate batches
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Installation
|
5
5
|
## 1. Add `dwf` to Gemfile
|
6
6
|
```ruby
|
7
|
-
gem 'dwf', '~> 0.1.
|
7
|
+
gem 'dwf', '~> 0.1.9'
|
8
8
|
```
|
9
9
|
## 2. Execute flow
|
10
10
|
### Declare jobs
|
@@ -38,6 +38,14 @@ class TestWf < Dwf::Workflow
|
|
38
38
|
end
|
39
39
|
```
|
40
40
|
|
41
|
+
|
42
|
+
### Execute flow
|
43
|
+
```ruby
|
44
|
+
wf = TestWf.create
|
45
|
+
wf.callback_type = Dwf::Workflow::SK_BATCH
|
46
|
+
wf.start!
|
47
|
+
```
|
48
|
+
|
41
49
|
#### Note
|
42
50
|
`dwf` supports 2 callback types `Dwf::Workflow::BUILD_IN` and `Dwf::Workflow::SK_BATCH`
|
43
51
|
- `Dwf::Workflow::BUILD_IN` is a build-in callback
|
@@ -45,12 +53,6 @@ end
|
|
45
53
|
|
46
54
|
By default `dwf` will use `Dwf::Workflow::BUILD_IN` callback.
|
47
55
|
|
48
|
-
### Execute flow
|
49
|
-
```ruby
|
50
|
-
wf = TestWf.create(callback_type: Dwf::Workflow::SK_BATCH)
|
51
|
-
wf.start!
|
52
|
-
```
|
53
|
-
|
54
56
|
### Output
|
55
57
|
```
|
56
58
|
A Working
|
@@ -70,14 +72,65 @@ D say hello
|
|
70
72
|
D Finished
|
71
73
|
```
|
72
74
|
|
75
|
+
# Config redis and default queue
|
76
|
+
`dwf` uses redis as the key value stograge through [redis-rb](https://github.com/redis/redis-rb), So you can pass redis configuration by `redis_opts`
|
77
|
+
```ruby
|
78
|
+
Dwf.config do |config|
|
79
|
+
SENTINELS = [
|
80
|
+
{ host: "127.0.0.1", port: 26380 },
|
81
|
+
{ host: "127.0.0.1", port: 26381 }
|
82
|
+
]
|
83
|
+
config.redis_opts = { host: 'mymaster', sentinels: SENTINELS, role: :master }
|
84
|
+
config.namespace = 'dwf'
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
# Pinelining
|
89
|
+
You can pass jobs result to next nodes
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class SendOutput < Dwf::Item
|
93
|
+
def perform
|
94
|
+
output('it works')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
```
|
99
|
+
|
100
|
+
`output` method used to output data from the job to add outgoing jobs
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
class ReceiveOutput < Dwf::Item
|
104
|
+
def perform
|
105
|
+
message = payloads.first[:output] # it works
|
106
|
+
end
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
`payloads` is an array that containing outputs from incoming jobs
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
[
|
114
|
+
{
|
115
|
+
id: "SendOutput|1849a3f9-5fce-401e-a73a-91fc1048356",
|
116
|
+
class: "SendOutput",
|
117
|
+
output: 'it works'
|
118
|
+
}
|
119
|
+
]
|
120
|
+
```
|
121
|
+
|
73
122
|
# Todo
|
74
123
|
- [x] Make it work
|
75
124
|
- [x] Support pass params
|
76
125
|
- [x] Support with build-in callback
|
77
126
|
- [x] Add github workflow
|
78
|
-
- [
|
79
|
-
- [
|
127
|
+
- [x] Redis configurable
|
128
|
+
- [x] Pinelining
|
129
|
+
- [X] Test
|
130
|
+
- [ ] Consistent item name
|
80
131
|
- [ ] Support [Resque](https://github.com/resque/resque)
|
132
|
+
- [ ] Key value store plugable
|
133
|
+
- [ ] research https://github.com/moneta-rb/moneta
|
81
134
|
|
82
135
|
# References
|
83
136
|
- https://github.com/chaps-io/gush
|
data/dwf.gemspec
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "dwf"
|
9
|
-
spec.version = '0.1.
|
9
|
+
spec.version = '0.1.10'
|
10
10
|
spec.authors = ["dthtien"]
|
11
11
|
spec.email = ["tiendt2311@gmail.com"]
|
12
12
|
|
@@ -27,5 +27,6 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency 'byebug', '~> 11.1.3'
|
28
28
|
spec.add_dependency 'redis', '~> 4.2.0'
|
29
29
|
spec.add_development_dependency 'rspec', '~> 3.2'
|
30
|
+
spec.add_development_dependency 'mock_redis', '~> 0.27.2'
|
30
31
|
spec.add_dependency 'sidekiq', '~> 6.2.0'
|
31
32
|
end
|
data/lib/dwf/callback.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require_relative 'client'
|
2
3
|
|
3
4
|
module Dwf
|
4
5
|
class Callback
|
6
|
+
DEFAULT_KEY = 'default_key'
|
7
|
+
|
5
8
|
def process_next_step(status, options)
|
6
9
|
previous_job_names = options['names']
|
7
10
|
workflow_id = options['workflow_id']
|
@@ -12,7 +15,7 @@ module Dwf
|
|
12
15
|
return if processing_job_names.empty?
|
13
16
|
|
14
17
|
overall = Sidekiq::Batch.new(status.parent_bid)
|
15
|
-
overall.jobs {
|
18
|
+
overall.jobs { setup_batches(processing_job_names, workflow_id) }
|
16
19
|
end
|
17
20
|
|
18
21
|
def start(job)
|
@@ -21,38 +24,36 @@ module Dwf
|
|
21
24
|
|
22
25
|
private
|
23
26
|
|
24
|
-
def
|
27
|
+
def setup_batches(processing_job_names, workflow_id)
|
25
28
|
jobs = fetch_jobs(processing_job_names, workflow_id)
|
26
29
|
jobs_classification = classify_jobs jobs
|
27
30
|
|
28
|
-
jobs_classification.
|
29
|
-
|
30
|
-
|
31
|
-
:success,
|
32
|
-
'Dwf::Callback#process_next_step',
|
33
|
-
names: batch_jobs.map(&:klass),
|
34
|
-
workflow_id: workflow_id
|
35
|
-
)
|
36
|
-
|
37
|
-
batch.jobs do
|
38
|
-
batch_jobs.each do |job|
|
39
|
-
job.persist_and_perform_async! if job.ready_to_start?
|
40
|
-
end
|
31
|
+
jobs_classification.each do |key, batch_jobs|
|
32
|
+
with_lock workflow_id, key do
|
33
|
+
setup_batch(batch_jobs, workflow_id)
|
41
34
|
end
|
42
35
|
end
|
43
36
|
end
|
44
37
|
|
38
|
+
def setup_batch(jobs, workflow_id)
|
39
|
+
batch = Sidekiq::Batch.new
|
40
|
+
batch.on(
|
41
|
+
:success,
|
42
|
+
'Dwf::Callback#process_next_step',
|
43
|
+
names: jobs.map(&:klass),
|
44
|
+
workflow_id: workflow_id
|
45
|
+
)
|
46
|
+
batch.jobs do
|
47
|
+
jobs.each { |job| job.persist_and_perform_async! if job.ready_to_start? }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
45
51
|
def classify_jobs(jobs)
|
46
52
|
hash = {}
|
47
53
|
jobs.each do |job|
|
48
54
|
outgoing_jobs = job.outgoing
|
49
|
-
key = outgoing_jobs.empty? ?
|
50
|
-
|
51
|
-
if hash[key].nil?
|
52
|
-
hash[key] = [job]
|
53
|
-
else
|
54
|
-
hash[key] = hash[key].push(job)
|
55
|
-
end
|
55
|
+
key = outgoing_jobs.empty? ? DEFAULT_KEY : outgoing_jobs.join
|
56
|
+
hash[key] = hash[key].nil? ? [job] : hash[key].push(job)
|
56
57
|
end
|
57
58
|
|
58
59
|
hash
|
@@ -64,13 +65,6 @@ module Dwf
|
|
64
65
|
end.compact
|
65
66
|
end
|
66
67
|
|
67
|
-
def perform_job(job_name, workflow_id)
|
68
|
-
with_lock workflow_id, job_name do
|
69
|
-
job = client.find_job(workflow_id, job_name)
|
70
|
-
job.persist_and_perform_async! if job.ready_to_start?
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
68
|
def with_lock(workflow_id, job_name)
|
75
69
|
client.check_or_lock(workflow_id, job_name)
|
76
70
|
yield
|
data/lib/dwf/client.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
1
3
|
module Dwf
|
2
4
|
class Client
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config = Dwf.configuration)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
3
11
|
def find_job(workflow_id, job_name)
|
4
12
|
job_name_match = /(?<klass>\w*[^-])-(?<identifier>.*)/.match(job_name)
|
5
13
|
data = if job_name_match
|
@@ -14,6 +22,16 @@ module Dwf
|
|
14
22
|
Dwf::Item.from_hash(Dwf::Utils.symbolize_keys(data))
|
15
23
|
end
|
16
24
|
|
25
|
+
def find_workflow(id)
|
26
|
+
data = redis.get("dwf.workflows.#{id}")
|
27
|
+
raise WorkflowNotFound, "Workflow with given id doesn't exist" if data.nil?
|
28
|
+
|
29
|
+
hash = JSON.parse(data)
|
30
|
+
hash = Dwf::Utils.symbolize_keys(hash)
|
31
|
+
nodes = parse_nodes(id)
|
32
|
+
workflow_from_hash(hash, nodes)
|
33
|
+
end
|
34
|
+
|
17
35
|
def persist_job(job)
|
18
36
|
redis.hset("dwf.jobs.#{job.workflow_id}.#{job.klass}", job.id, job.as_json)
|
19
37
|
end
|
@@ -93,8 +111,30 @@ module Dwf
|
|
93
111
|
job
|
94
112
|
end
|
95
113
|
|
114
|
+
def parse_nodes(id)
|
115
|
+
keys = redis.scan_each(match: "dwf.jobs.#{id}.*")
|
116
|
+
|
117
|
+
keys.map do |key|
|
118
|
+
redis.hvals(key).map do |json|
|
119
|
+
Dwf::Utils.symbolize_keys JSON.parse(json)
|
120
|
+
end
|
121
|
+
end.flatten
|
122
|
+
end
|
123
|
+
|
124
|
+
def workflow_from_hash(hash, nodes = [])
|
125
|
+
flow = Module.const_get(hash[:klass]).new(*hash[:arguments])
|
126
|
+
flow.jobs = []
|
127
|
+
flow.stopped = hash.fetch(:stopped, false)
|
128
|
+
flow.id = hash[:id]
|
129
|
+
flow.jobs = nodes.map do |node|
|
130
|
+
Dwf::Item.from_hash(node)
|
131
|
+
end
|
132
|
+
|
133
|
+
flow
|
134
|
+
end
|
135
|
+
|
96
136
|
def redis
|
97
|
-
@redis ||= Redis.new
|
137
|
+
@redis ||= Redis.new(config.redis_opts)
|
98
138
|
end
|
99
139
|
end
|
100
140
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dwf
|
4
|
+
class Configuration
|
5
|
+
NAMESPACE = 'dwf'
|
6
|
+
REDIS_OPTS = { url: 'redis://localhost:6379' }.freeze
|
7
|
+
|
8
|
+
attr_accessor :redis_opts, :namespace
|
9
|
+
|
10
|
+
def initialize(hash = {})
|
11
|
+
@namespace = hash.fetch(:namespace, NAMESPACE)
|
12
|
+
@redis_opts = hash.fetch(:redis_opts, REDIS_OPTS)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/dwf/errors.rb
ADDED
data/lib/dwf/item.rb
CHANGED
@@ -1,27 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'client'
|
3
4
|
|
4
5
|
module Dwf
|
5
6
|
class Item
|
6
|
-
DEFAULT_QUEUE = 'default'
|
7
|
-
|
8
7
|
attr_reader :workflow_id, :id, :params, :queue, :klass, :started_at,
|
9
|
-
:enqueued_at, :finished_at, :failed_at, :callback_type
|
8
|
+
:enqueued_at, :finished_at, :failed_at, :callback_type, :output_payload
|
10
9
|
attr_accessor :incoming, :outgoing
|
11
10
|
|
12
11
|
def initialize(options = {})
|
13
|
-
|
14
|
-
@id = options[:id]
|
15
|
-
@params = options[:params]
|
16
|
-
@queue = options[:queue] || DEFAULT_QUEUE
|
17
|
-
@incoming = options[:incoming] || []
|
18
|
-
@outgoing = options[:outgoing] || []
|
19
|
-
@klass = options[:klass] || self.class
|
20
|
-
@failed_at = options[:failed_at]
|
21
|
-
@finished_at = options[:finished_at]
|
22
|
-
@enqueued_at = options[:enqueued_at]
|
23
|
-
@started_at = options[:started_at]
|
24
|
-
@callback_type = options[:callback_type]
|
12
|
+
assign_attributes(options)
|
25
13
|
end
|
26
14
|
|
27
15
|
def self.from_hash(hash)
|
@@ -40,14 +28,24 @@ module Dwf
|
|
40
28
|
callback_type == Dwf::Workflow::BUILD_IN
|
41
29
|
end
|
42
30
|
|
31
|
+
def reload
|
32
|
+
item = client.find_job(workflow_id, name)
|
33
|
+
assign_attributes(item.to_hash)
|
34
|
+
end
|
35
|
+
|
43
36
|
def perform_async
|
44
|
-
Dwf::Worker.set(queue: queue
|
37
|
+
Dwf::Worker.set(queue: queue || client.config.namespace)
|
38
|
+
.perform_async(workflow_id, name)
|
45
39
|
end
|
46
40
|
|
47
41
|
def name
|
48
42
|
@name ||= "#{klass}|#{id}"
|
49
43
|
end
|
50
44
|
|
45
|
+
def output(data)
|
46
|
+
@output_payload = data
|
47
|
+
end
|
48
|
+
|
51
49
|
def no_dependencies?
|
52
50
|
incoming.empty?
|
53
51
|
end
|
@@ -58,6 +56,17 @@ module Dwf
|
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
59
|
+
def payloads
|
60
|
+
incoming.map do |job_name|
|
61
|
+
job = client.find_job(workflow_id, job_name)
|
62
|
+
{
|
63
|
+
id: job.name,
|
64
|
+
class: job.klass.to_s,
|
65
|
+
output: job.output_payload
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
61
70
|
def enqueue!
|
62
71
|
@enqueued_at = current_timestamp
|
63
72
|
@started_at = nil
|
@@ -142,7 +151,8 @@ module Dwf
|
|
142
151
|
failed_at: failed_at,
|
143
152
|
params: params,
|
144
153
|
workflow_id: workflow_id,
|
145
|
-
callback_type: callback_type
|
154
|
+
callback_type: callback_type,
|
155
|
+
output_payload: output_payload
|
146
156
|
}
|
147
157
|
end
|
148
158
|
|
@@ -159,5 +169,21 @@ module Dwf
|
|
159
169
|
def client
|
160
170
|
@client ||= Dwf::Client.new
|
161
171
|
end
|
172
|
+
|
173
|
+
def assign_attributes(options)
|
174
|
+
@workflow_id = options[:workflow_id]
|
175
|
+
@id = options[:id]
|
176
|
+
@params = options[:params]
|
177
|
+
@queue = options[:queue]
|
178
|
+
@incoming = options[:incoming] || []
|
179
|
+
@outgoing = options[:outgoing] || []
|
180
|
+
@klass = options[:klass] || self.class
|
181
|
+
@failed_at = options[:failed_at]
|
182
|
+
@finished_at = options[:finished_at]
|
183
|
+
@enqueued_at = options[:enqueued_at]
|
184
|
+
@started_at = options[:started_at]
|
185
|
+
@callback_type = options[:callback_type]
|
186
|
+
@output_payload = options[:output_payload]
|
187
|
+
end
|
162
188
|
end
|
163
189
|
end
|
data/lib/dwf/version.rb
ADDED
data/lib/dwf/worker.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sidekiq'
|
2
4
|
require_relative 'client'
|
3
5
|
|
@@ -7,7 +9,7 @@ module Dwf
|
|
7
9
|
|
8
10
|
def perform(workflow_id, job_name)
|
9
11
|
job = client.find_job(workflow_id, job_name)
|
10
|
-
return job.enqueue_outgoing_jobs if job.
|
12
|
+
return job.enqueue_outgoing_jobs if job.succeeded?
|
11
13
|
|
12
14
|
job.mark_as_started
|
13
15
|
job.perform
|
data/lib/dwf/workflow.rb
CHANGED
@@ -10,8 +10,8 @@ module Dwf
|
|
10
10
|
BUILD_IN = 'build-in',
|
11
11
|
SK_BATCH = 'sk-batch'
|
12
12
|
].freeze
|
13
|
-
|
14
|
-
|
13
|
+
attr_accessor :jobs, :callback_type, :stopped, :id
|
14
|
+
attr_reader :dependencies, :started_at, :finished_at, :persisted, :arguments
|
15
15
|
|
16
16
|
class << self
|
17
17
|
def create(*args)
|
@@ -19,41 +19,58 @@ module Dwf
|
|
19
19
|
flow.save
|
20
20
|
flow
|
21
21
|
end
|
22
|
+
|
23
|
+
def find(id)
|
24
|
+
Dwf::Client.new.find_workflow(id)
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
|
-
def initialize(
|
28
|
+
def initialize(*args)
|
25
29
|
@dependencies = []
|
26
|
-
@id =
|
30
|
+
@id = build_id
|
27
31
|
@jobs = []
|
28
32
|
@persisted = false
|
29
33
|
@stopped = false
|
30
|
-
@
|
34
|
+
@arguments = args
|
35
|
+
@callback_type = BUILD_IN
|
31
36
|
|
32
37
|
setup
|
33
38
|
end
|
34
39
|
|
40
|
+
def persist!
|
41
|
+
client.persist_workflow(self)
|
42
|
+
jobs.each(&:persist!)
|
43
|
+
mark_as_persisted
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
alias save persist!
|
48
|
+
|
35
49
|
def start!
|
50
|
+
mark_as_started
|
51
|
+
persist!
|
36
52
|
initial_jobs.each do |job|
|
37
53
|
cb_build_in? ? job.persist_and_perform_async! : Dwf::Callback.new.start(job)
|
38
54
|
end
|
39
55
|
end
|
40
56
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
57
|
+
def reload
|
58
|
+
flow = self.class.find(id)
|
59
|
+
self.stopped = flow.stopped
|
60
|
+
self.jobs = flow.jobs
|
61
|
+
|
62
|
+
self
|
46
63
|
end
|
47
64
|
|
48
65
|
def cb_build_in?
|
49
66
|
callback_type == BUILD_IN
|
50
67
|
end
|
51
68
|
|
52
|
-
def
|
53
|
-
|
69
|
+
def build_id
|
70
|
+
client.build_workflow_id
|
54
71
|
end
|
55
72
|
|
56
|
-
def configure; end
|
73
|
+
def configure(*arguments); end
|
57
74
|
|
58
75
|
def run(klass, options = {})
|
59
76
|
node = klass.new(
|
@@ -140,7 +157,6 @@ module Dwf
|
|
140
157
|
@stopped = false
|
141
158
|
end
|
142
159
|
|
143
|
-
|
144
160
|
private
|
145
161
|
|
146
162
|
def initial_jobs
|
@@ -148,7 +164,7 @@ module Dwf
|
|
148
164
|
end
|
149
165
|
|
150
166
|
def setup
|
151
|
-
configure
|
167
|
+
configure(*arguments)
|
152
168
|
resolve_dependencies
|
153
169
|
end
|
154
170
|
|
data/lib/dwf.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "bundler/setup"
|
3
4
|
|
4
5
|
require 'sidekiq'
|
@@ -12,8 +13,15 @@ require_relative 'dwf/item'
|
|
12
13
|
require_relative 'dwf/client'
|
13
14
|
require_relative 'dwf/worker'
|
14
15
|
require_relative 'dwf/callback'
|
16
|
+
require_relative 'dwf/configuration'
|
15
17
|
|
16
18
|
module Dwf
|
17
|
-
|
19
|
+
def self.configuration
|
20
|
+
@configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.config
|
24
|
+
yield configuration
|
25
|
+
end
|
18
26
|
end
|
19
27
|
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'mock_redis'
|
5
|
+
|
6
|
+
describe Dwf::Client, client: true do
|
7
|
+
let(:client) { described_class.new }
|
8
|
+
let(:workflow_id) { SecureRandom.uuid }
|
9
|
+
let(:id) { SecureRandom.uuid }
|
10
|
+
let(:redis) { Redis.new }
|
11
|
+
before do
|
12
|
+
redis_instance = MockRedis.new
|
13
|
+
allow(Redis).to receive(:new).and_return redis_instance
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#find_job' do
|
17
|
+
let!(:job) do
|
18
|
+
j = Dwf::Item.new(workflow_id: workflow_id, id: id)
|
19
|
+
j.persist!
|
20
|
+
j
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'find by item class name' do
|
24
|
+
it {
|
25
|
+
item = client.find_job(workflow_id, Dwf::Item.name)
|
26
|
+
expect(item.workflow_id).to eq workflow_id
|
27
|
+
expect(item.id).to eq id
|
28
|
+
expect(item.name).to eq job.name
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'find by item name' do
|
33
|
+
it {
|
34
|
+
item = client.find_job(workflow_id, job.name)
|
35
|
+
expect(item.workflow_id).to eq workflow_id
|
36
|
+
expect(item.id).to eq id
|
37
|
+
expect(item.name).to eq job.name
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#find_workflow' do
|
43
|
+
before do
|
44
|
+
wf = Dwf::Workflow.new
|
45
|
+
wf.id = workflow_id
|
46
|
+
wf.save
|
47
|
+
j = Dwf::Item.new(id: id, workflow_id: workflow_id)
|
48
|
+
j.persist!
|
49
|
+
end
|
50
|
+
|
51
|
+
it do
|
52
|
+
wf = client.find_workflow(workflow_id)
|
53
|
+
|
54
|
+
expect(wf).not_to be_nil
|
55
|
+
expect(wf.jobs.first).to be_kind_of(Dwf::Item)
|
56
|
+
end
|
57
|
+
|
58
|
+
it do
|
59
|
+
expect do
|
60
|
+
client.find_workflow(SecureRandom.uuid)
|
61
|
+
end.to raise_error Dwf::WorkflowNotFound
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#persist_job' do
|
66
|
+
let!(:job) { Dwf::Item.new(workflow_id: workflow_id, id: id) }
|
67
|
+
|
68
|
+
it do
|
69
|
+
expect(redis.exists?("dwf.jobs.#{job.workflow_id}.#{job.klass}"))
|
70
|
+
.to be_falsy
|
71
|
+
|
72
|
+
client.persist_job(job)
|
73
|
+
|
74
|
+
expect(redis.exists?("dwf.jobs.#{job.workflow_id}.#{job.klass}"))
|
75
|
+
.to be_truthy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#persist_workflow' do
|
80
|
+
let(:workflow) { Dwf::Workflow.new }
|
81
|
+
|
82
|
+
it do
|
83
|
+
expect(redis.exists?("dwf.workflows.#{workflow.id}")).to be_falsy
|
84
|
+
client.persist_workflow(workflow)
|
85
|
+
expect(redis.exists?("dwf.workflows.#{workflow.id}")).to be_truthy
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#check_or_lock' do
|
90
|
+
before do
|
91
|
+
allow_any_instance_of(described_class).to receive(:sleep)
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'job is running' do
|
95
|
+
let(:job_name) { 'ahihi' }
|
96
|
+
|
97
|
+
before do
|
98
|
+
allow(client).to receive(:set)
|
99
|
+
redis.set("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
|
100
|
+
client.check_or_lock(workflow_id, job_name)
|
101
|
+
end
|
102
|
+
|
103
|
+
it { expect(client).not_to have_received(:set) }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'job is not running' do
|
107
|
+
let(:job_name) { 'ahihi' }
|
108
|
+
|
109
|
+
before do
|
110
|
+
allow(redis).to receive(:set)
|
111
|
+
client.check_or_lock(workflow_id, job_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
it do
|
115
|
+
expect(redis).to have_received(:set)
|
116
|
+
.with("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#release_lock' do
|
122
|
+
before do
|
123
|
+
allow(redis).to receive(:del)
|
124
|
+
client.release_lock(workflow_id, 'ahihi')
|
125
|
+
end
|
126
|
+
|
127
|
+
it do
|
128
|
+
expect(redis).to have_received(:del)
|
129
|
+
.with("dwf_enqueue_outgoing_jobs_#{workflow_id}-ahihi")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#build_job_id' do
|
134
|
+
before do
|
135
|
+
allow(redis).to receive(:hexists)
|
136
|
+
client.build_job_id(workflow_id, 'ahihi')
|
137
|
+
end
|
138
|
+
|
139
|
+
it { expect(redis).to have_received(:hexists) }
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '#build_workflow_id' do
|
143
|
+
before do
|
144
|
+
allow(redis).to receive(:exists?)
|
145
|
+
client.build_workflow_id
|
146
|
+
end
|
147
|
+
|
148
|
+
it { expect(redis).to have_received(:exists?) }
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#key_exists?' do
|
152
|
+
before do
|
153
|
+
allow(redis).to receive(:exists?)
|
154
|
+
client.key_exists?('ahihi')
|
155
|
+
end
|
156
|
+
|
157
|
+
it { expect(redis).to have_received(:exists?).with('ahihi') }
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#set' do
|
161
|
+
before do
|
162
|
+
allow(redis).to receive(:set)
|
163
|
+
client.set('ahihi', 'a')
|
164
|
+
end
|
165
|
+
|
166
|
+
it { expect(redis).to have_received(:set).with('ahihi', 'a') }
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#delete' do
|
170
|
+
before do
|
171
|
+
allow(redis).to receive(:del)
|
172
|
+
client.delete('ahihi')
|
173
|
+
end
|
174
|
+
|
175
|
+
it { expect(redis).to have_received(:del).with('ahihi') }
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dwf::Configuration, configuration: true do
|
6
|
+
let(:configuration) { described_class.new }
|
7
|
+
|
8
|
+
specify do
|
9
|
+
expect(configuration.namespace).to eq described_class::NAMESPACE
|
10
|
+
expect(configuration.redis_opts).to eq described_class::REDIS_OPTS
|
11
|
+
end
|
12
|
+
end
|
data/spec/dwf/item_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe Dwf::Item, item: true do
|
|
16
16
|
params: {},
|
17
17
|
incoming: incoming,
|
18
18
|
outgoing: outgoing,
|
19
|
-
queue: Dwf::
|
19
|
+
queue: Dwf::Configuration::NAMESPACE,
|
20
20
|
klass: 'Dwf::Item',
|
21
21
|
started_at: started_at,
|
22
22
|
finished_at: finished_at,
|
@@ -181,4 +181,40 @@ describe Dwf::Item, item: true do
|
|
181
181
|
it { expect(a_item).not_to have_received(:persist_and_perform_async!) }
|
182
182
|
end
|
183
183
|
end
|
184
|
+
|
185
|
+
describe '#output' do
|
186
|
+
before { item.output(1) }
|
187
|
+
|
188
|
+
it { expect(item.output_payload).to eq 1 }
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#payloads' do
|
192
|
+
let(:incoming) { ["A|#{SecureRandom.uuid}"] }
|
193
|
+
let(:client_double) { double(find_job: nil) }
|
194
|
+
let!(:a_item) do
|
195
|
+
described_class.new(
|
196
|
+
workflow_id: SecureRandom.uuid,
|
197
|
+
id: SecureRandom.uuid,
|
198
|
+
finished_at: finished_at,
|
199
|
+
output_payload: 1
|
200
|
+
)
|
201
|
+
end
|
202
|
+
|
203
|
+
before do
|
204
|
+
allow(Dwf::Client).to receive(:new).and_return client_double
|
205
|
+
allow(client_double)
|
206
|
+
.to receive(:find_job).and_return a_item
|
207
|
+
end
|
208
|
+
|
209
|
+
it do
|
210
|
+
expected_payload = [
|
211
|
+
{
|
212
|
+
class: a_item.class.name,
|
213
|
+
id: a_item.name,
|
214
|
+
output: 1
|
215
|
+
}
|
216
|
+
]
|
217
|
+
expect(item.payloads).to eq expected_payload
|
218
|
+
end
|
219
|
+
end
|
184
220
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'mock_redis'
|
5
|
+
require 'sidekiq/testing'
|
6
|
+
|
7
|
+
describe Dwf::Worker, client: true do
|
8
|
+
let(:workflow_id) { SecureRandom.uuid }
|
9
|
+
let(:id) { SecureRandom.uuid }
|
10
|
+
let(:redis) { Redis.new }
|
11
|
+
let(:worker) { described_class.perform_async(workflow_id, job.name) }
|
12
|
+
before do
|
13
|
+
redis_instance = MockRedis.new
|
14
|
+
allow(Redis).to receive(:new).and_return redis_instance
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#find_job' do
|
18
|
+
let!(:job) do
|
19
|
+
j = Dwf::Item.new(workflow_id: workflow_id, id: id)
|
20
|
+
j.persist!
|
21
|
+
j
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
worker
|
26
|
+
Sidekiq::Worker.drain_all
|
27
|
+
job.reload
|
28
|
+
end
|
29
|
+
|
30
|
+
it { expect(job.finished?).to be_truthy }
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'mock_redis'
|
5
|
+
class AItem < Dwf::Item; end
|
6
|
+
|
7
|
+
class BItem < Dwf::Item; end
|
8
|
+
|
9
|
+
class CItem < Dwf::Item; end
|
10
|
+
|
11
|
+
describe Dwf::Workflow, workflow: true do
|
12
|
+
let(:workflow_id) { SecureRandom.uuid }
|
13
|
+
let(:item_id) { SecureRandom.uuid }
|
14
|
+
let(:client) do
|
15
|
+
double(
|
16
|
+
persist_workflow: nil,
|
17
|
+
persist_job: nil,
|
18
|
+
build_workflow_id: workflow_id,
|
19
|
+
build_job_id: item_id,
|
20
|
+
find_workflow: nil
|
21
|
+
)
|
22
|
+
end
|
23
|
+
before do
|
24
|
+
allow(Dwf::Client).to receive(:new).and_return client
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#create' do
|
28
|
+
it do
|
29
|
+
workflow = described_class.create
|
30
|
+
expect(client).to have_received(:persist_workflow)
|
31
|
+
.with(an_instance_of(described_class))
|
32
|
+
expect(workflow.id).to eq workflow_id
|
33
|
+
expect(workflow.persisted).to be_truthy
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#find' do
|
38
|
+
before { Dwf::Workflow.find(workflow_id) }
|
39
|
+
|
40
|
+
it { expect(client).to have_received(:find_workflow).with(workflow_id) }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#persist!' do
|
44
|
+
let(:workflow) { described_class.new }
|
45
|
+
let(:job) do
|
46
|
+
Dwf::Item.new(
|
47
|
+
worflow_id: workflow_id,
|
48
|
+
id: item_id
|
49
|
+
)
|
50
|
+
end
|
51
|
+
before do
|
52
|
+
workflow.jobs << job
|
53
|
+
workflow.persist!
|
54
|
+
end
|
55
|
+
|
56
|
+
it do
|
57
|
+
expect(client).to have_received(:persist_workflow)
|
58
|
+
.with(an_instance_of(described_class))
|
59
|
+
expect(client).to have_received(:persist_job).with(job)
|
60
|
+
expect(workflow.id).to eq workflow_id
|
61
|
+
expect(workflow.persisted).to be_truthy
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#start' do
|
66
|
+
let(:workflow) { described_class.new }
|
67
|
+
let(:job) do
|
68
|
+
Dwf::Item.new(
|
69
|
+
worflow_id: workflow_id,
|
70
|
+
id: item_id
|
71
|
+
)
|
72
|
+
end
|
73
|
+
before do
|
74
|
+
workflow.jobs << job
|
75
|
+
workflow.persist!
|
76
|
+
end
|
77
|
+
|
78
|
+
it do
|
79
|
+
expect(client).to have_received(:persist_workflow)
|
80
|
+
.with(an_instance_of(described_class))
|
81
|
+
expect(client).to have_received(:persist_job).with(job)
|
82
|
+
expect(workflow.id).to eq workflow_id
|
83
|
+
expect(workflow.persisted).to be_truthy
|
84
|
+
expect(workflow.stopped).to be_falsy
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#run' do
|
89
|
+
let!(:workflow) { described_class.new }
|
90
|
+
|
91
|
+
before do
|
92
|
+
workflow.run AItem, after: BItem, before: CItem
|
93
|
+
end
|
94
|
+
|
95
|
+
it do
|
96
|
+
expect(workflow.jobs).not_to be_empty
|
97
|
+
expected = [
|
98
|
+
{
|
99
|
+
from: BItem.to_s,
|
100
|
+
to: "AItem|#{item_id}"
|
101
|
+
},
|
102
|
+
{
|
103
|
+
from: "AItem|#{item_id}",
|
104
|
+
to: CItem.to_s
|
105
|
+
}
|
106
|
+
]
|
107
|
+
expect(workflow.dependencies).to match_array expected
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#find_job' do
|
112
|
+
let!(:workflow) { described_class.new }
|
113
|
+
before do
|
114
|
+
workflow.jobs = [
|
115
|
+
AItem.new,
|
116
|
+
BItem.new(id: item_id),
|
117
|
+
CItem.new
|
118
|
+
]
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'searches by klass' do
|
122
|
+
job = workflow.find_job('AItem')
|
123
|
+
expect(job).to be_kind_of AItem
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'searches by name' do
|
127
|
+
job = workflow.find_job("BItem|#{item_id}")
|
128
|
+
expect(job).to be_kind_of BItem
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#setup' do
|
133
|
+
let!(:workflow) { described_class.new }
|
134
|
+
before do
|
135
|
+
workflow.run AItem
|
136
|
+
workflow.run BItem, after: AItem
|
137
|
+
workflow.run CItem, after: BItem
|
138
|
+
|
139
|
+
workflow.send(:setup)
|
140
|
+
end
|
141
|
+
|
142
|
+
it do
|
143
|
+
job_a = workflow.find_job('AItem')
|
144
|
+
|
145
|
+
expect(job_a.incoming).to be_empty
|
146
|
+
expect(job_a.outgoing).to eq ["BItem|#{item_id}"]
|
147
|
+
|
148
|
+
job_b = workflow.find_job('BItem')
|
149
|
+
|
150
|
+
expect(job_b.incoming).to eq ['AItem']
|
151
|
+
expect(job_b.outgoing).to eq ["CItem|#{item_id}"]
|
152
|
+
|
153
|
+
job_c = workflow.find_job('CItem')
|
154
|
+
|
155
|
+
expect(job_c.incoming).to eq ['BItem']
|
156
|
+
expect(job_c.outgoing).to be_empty
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#callback_type' do
|
161
|
+
let!(:workflow) { described_class.new }
|
162
|
+
|
163
|
+
it {
|
164
|
+
expect(workflow.callback_type).to eq described_class::BUILD_IN
|
165
|
+
workflow.callback_type = described_class::SK_BATCH
|
166
|
+
expect(workflow.callback_type).to eq described_class::SK_BATCH
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '#reload' do
|
171
|
+
let!(:workflow) do
|
172
|
+
flow = described_class.new
|
173
|
+
flow.id = workflow_id
|
174
|
+
flow
|
175
|
+
end
|
176
|
+
|
177
|
+
before do
|
178
|
+
allow(client).to receive(:find_workflow).and_return workflow
|
179
|
+
workflow.reload
|
180
|
+
end
|
181
|
+
|
182
|
+
it { expect(client).to have_received(:find_workflow).with(workflow_id) }
|
183
|
+
end
|
184
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dwf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dthtien
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mock_redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.27.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.27.2
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: sidekiq
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,12 +100,19 @@ files:
|
|
86
100
|
- lib/dwf.rb
|
87
101
|
- lib/dwf/callback.rb
|
88
102
|
- lib/dwf/client.rb
|
103
|
+
- lib/dwf/configuration.rb
|
104
|
+
- lib/dwf/errors.rb
|
89
105
|
- lib/dwf/item.rb
|
90
106
|
- lib/dwf/utils.rb
|
107
|
+
- lib/dwf/version.rb
|
91
108
|
- lib/dwf/worker.rb
|
92
109
|
- lib/dwf/workflow.rb
|
110
|
+
- spec/dwf/client_spec.rb
|
111
|
+
- spec/dwf/configuration_spec.rb
|
93
112
|
- spec/dwf/item_spec.rb
|
94
113
|
- spec/dwf/utils_spec.rb
|
114
|
+
- spec/dwf/worker_spec.rb
|
115
|
+
- spec/dwf/workflow_spec.rb
|
95
116
|
- spec/spec_helper.rb
|
96
117
|
homepage: https://github.com/dthtien/wf
|
97
118
|
licenses:
|
@@ -118,6 +139,10 @@ specification_version: 4
|
|
118
139
|
summary: Gush cloned without ActiveJob but requried Sidekiq. This project is for researching
|
119
140
|
DSL purpose
|
120
141
|
test_files:
|
142
|
+
- spec/dwf/client_spec.rb
|
143
|
+
- spec/dwf/configuration_spec.rb
|
121
144
|
- spec/dwf/item_spec.rb
|
122
145
|
- spec/dwf/utils_spec.rb
|
146
|
+
- spec/dwf/worker_spec.rb
|
147
|
+
- spec/dwf/workflow_spec.rb
|
123
148
|
- spec/spec_helper.rb
|