dwf 0.1.9 → 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/CHANGELOG.md +23 -0
- data/README.md +6 -2
- data/dwf.gemspec +1 -1
- data/lib/dwf/client.rb +34 -0
- data/lib/dwf/errors.rb +3 -0
- data/lib/dwf/workflow.rb +31 -15
- data/spec/dwf/client_spec.rb +23 -0
- data/spec/dwf/workflow_spec.rb +184 -0
- metadata +5 -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/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
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
|
+
|
3
26
|
## 0.1.9
|
4
27
|
### Added
|
5
28
|
### Fixed
|
data/README.md
CHANGED
@@ -41,7 +41,8 @@ end
|
|
41
41
|
|
42
42
|
### Execute flow
|
43
43
|
```ruby
|
44
|
-
wf = TestWf.create
|
44
|
+
wf = TestWf.create
|
45
|
+
wf.callback_type = Dwf::Workflow::SK_BATCH
|
45
46
|
wf.start!
|
46
47
|
```
|
47
48
|
|
@@ -125,8 +126,11 @@ end
|
|
125
126
|
- [x] Add github workflow
|
126
127
|
- [x] Redis configurable
|
127
128
|
- [x] Pinelining
|
128
|
-
- [
|
129
|
+
- [X] Test
|
130
|
+
- [ ] Consistent item name
|
129
131
|
- [ ] Support [Resque](https://github.com/resque/resque)
|
132
|
+
- [ ] Key value store plugable
|
133
|
+
- [ ] research https://github.com/moneta-rb/moneta
|
130
134
|
|
131
135
|
# References
|
132
136
|
- https://github.com/chaps-io/gush
|
data/dwf.gemspec
CHANGED
data/lib/dwf/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
1
3
|
module Dwf
|
2
4
|
class Client
|
3
5
|
attr_reader :config
|
@@ -20,6 +22,16 @@ module Dwf
|
|
20
22
|
Dwf::Item.from_hash(Dwf::Utils.symbolize_keys(data))
|
21
23
|
end
|
22
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
|
+
|
23
35
|
def persist_job(job)
|
24
36
|
redis.hset("dwf.jobs.#{job.workflow_id}.#{job.klass}", job.id, job.as_json)
|
25
37
|
end
|
@@ -99,6 +111,28 @@ module Dwf
|
|
99
111
|
job
|
100
112
|
end
|
101
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
|
+
|
102
136
|
def redis
|
103
137
|
@redis ||= Redis.new(config.redis_opts)
|
104
138
|
end
|
data/lib/dwf/errors.rb
ADDED
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/spec/dwf/client_spec.rb
CHANGED
@@ -39,6 +39,29 @@ describe Dwf::Client, client: true do
|
|
39
39
|
end
|
40
40
|
end
|
41
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
|
+
|
42
65
|
describe '#persist_job' do
|
43
66
|
let!(:job) { Dwf::Item.new(workflow_id: workflow_id, id: id) }
|
44
67
|
|
@@ -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
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/dwf/callback.rb
|
102
102
|
- lib/dwf/client.rb
|
103
103
|
- lib/dwf/configuration.rb
|
104
|
+
- lib/dwf/errors.rb
|
104
105
|
- lib/dwf/item.rb
|
105
106
|
- lib/dwf/utils.rb
|
106
107
|
- lib/dwf/version.rb
|
@@ -111,6 +112,7 @@ files:
|
|
111
112
|
- spec/dwf/item_spec.rb
|
112
113
|
- spec/dwf/utils_spec.rb
|
113
114
|
- spec/dwf/worker_spec.rb
|
115
|
+
- spec/dwf/workflow_spec.rb
|
114
116
|
- spec/spec_helper.rb
|
115
117
|
homepage: https://github.com/dthtien/wf
|
116
118
|
licenses:
|
@@ -142,4 +144,5 @@ test_files:
|
|
142
144
|
- spec/dwf/item_spec.rb
|
143
145
|
- spec/dwf/utils_spec.rb
|
144
146
|
- spec/dwf/worker_spec.rb
|
147
|
+
- spec/dwf/workflow_spec.rb
|
145
148
|
- spec/spec_helper.rb
|