dwf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ad9df2cc13b2cf7cdca6b837d6b4b1b97e3784002afd8f756f4dfd85017aa749
4
+ data.tar.gz: '0960d1ebafa201f77b56e1adb99a7df1237f0b277474f8b20815bd1617204393'
5
+ SHA512:
6
+ metadata.gz: 1ad226e5469a263d8d59214d4678ee890af48e1b20b1e6e630be9650b8a8e4b54c6b0ca96534615369f68055d9b69b3f65d3e8ee515bc118df51b22e734fd5aa
7
+ data.tar.gz: efc316c11eb6d8dfc6b38922195a9af98418a62bd5e77812620dbe7194aabdae404b0a985dacf50a104c82fb9eeb3c45ada5abe8c5b37838d6dd8e7361c4a206
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .byebug_history
2
+ tmp/
3
+ Gemfile.lock
4
+ wf-*.gem
5
+ :w
6
+ :W
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ source "https://gems.contribsys.com/" do
6
+ gem 'sidekiq-pro'
7
+ end
8
+
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 dthtien
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # DSL playground
2
+ [Gush](https://github.com/chaps-io/gush) cloned without [ActiveJob](https://guides.rubyonrails.org/active_job_basics.html) but requried [Sidekiq](https://github.com/mperham/sidekiq). This project is for researching DSL purpose
3
+
4
+ # Execute flow
5
+ ## Declare jobs
6
+
7
+ ```ruby
8
+ require_relative './wf/item'
9
+
10
+ class A < Wf::Item
11
+ def perform
12
+ puts "#{self.class.name} Working"
13
+ sleep 2
14
+ puts "#{self.class.name} Finished"
15
+ end
16
+ end
17
+
18
+ class E < A; end
19
+ class B < A; end
20
+ class C < E; end
21
+ class D < E; end
22
+ ```
23
+
24
+ ## Declare flow
25
+ ```ruby
26
+ class TestWf < Wf::Workflow
27
+ def configure
28
+ run A
29
+ run B, after: A
30
+ run C, after: A
31
+ run E, after: [B, C]
32
+ run D, after: [E]
33
+ end
34
+ end
35
+
36
+ ```
37
+
38
+ ### Execute flow
39
+ ```ruby
40
+ wf = TestWf.create
41
+ wf.start!
42
+ ```
43
+
44
+ ### Output
45
+ ```
46
+ A Working
47
+ A Finished
48
+ B Working
49
+ C Working
50
+ B Finished
51
+ C Finished
52
+ E Working
53
+ E Finished
54
+ D Working
55
+ D Finished
56
+ ```
57
+
58
+ # References
59
+ - https://github.com/chaps-io/gush
60
+ - https://github.com/mperham/sidekiq
data/dwf.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # coding: utf-8
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "dwf"
9
+ spec.version = '0.1.0'
10
+ spec.authors = ["dthtien"]
11
+ spec.email = ["tiendt2311@gmail.com"]
12
+
13
+ spec.summary = 'Gush cloned without ActiveJob but requried Sidekiq. This project is for researching DSL purpose'
14
+ spec.description = 'Workflow'
15
+ spec.homepage = 'https://github.com/dthtien/wf'
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 2.4.0"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = `git ls-files -z`.split("\x0")
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
24
+
25
+ # guide at: https://bundler.io/guides/creating_gem.html
26
+
27
+ spec.add_development_dependency 'byebug', '~> 11.1.3'
28
+ spec.add_dependency 'redis', '~> 4.2.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.2'
30
+ spec.add_dependency 'sidekiq', '~> 6.2.0'
31
+ end
@@ -0,0 +1,67 @@
1
+ require 'sidekiq-pro'
2
+ require_relative 'client'
3
+
4
+ module Dwf
5
+ class Callback
6
+ def process_next_step(status, options)
7
+ previous_job_names = options['names']
8
+ workflow_id = options['workflow_id']
9
+ processing_job_names = previous_job_names.map do |job_name|
10
+ job = client.find_job(workflow_id, job_name)
11
+ job.outgoing
12
+ end.flatten.uniq
13
+ return if processing_job_names.empty?
14
+
15
+ overall = Sidekiq::Batch.new(status.parent_bid)
16
+ overall.jobs { setup_batch(processing_job_names, workflow_id) }
17
+ end
18
+
19
+ def start(job)
20
+ job.outgoing.any? ? start_with_batch(job) : job.perform_async
21
+ end
22
+
23
+ private
24
+
25
+ def setup_batch(processing_job_names, workflow_id)
26
+ batch = Sidekiq::Batch.new
27
+ batch.on(
28
+ :success,
29
+ 'Dwf::Callback#process_next_step',
30
+ names: processing_job_names,
31
+ workflow_id: workflow_id
32
+ )
33
+
34
+ batch.jobs do
35
+ processing_job_names.each { |job_name| perform_job(job_name, workflow_id) }
36
+ end
37
+ end
38
+
39
+ def perform_job(job_name, workflow_id)
40
+ with_lock workflow_id, job_name do
41
+ job = client.find_job(workflow_id, job_name)
42
+ job.persist_and_perform_async! if job.ready_to_start?
43
+ end
44
+ end
45
+
46
+ def with_lock(workflow_id, job_name)
47
+ client.check_or_lock(workflow_id, job_name)
48
+ yield
49
+ client.release_lock(workflow_id, job_name)
50
+ end
51
+
52
+ def start_with_batch(job)
53
+ batch = Sidekiq::Batch.new
54
+ batch.on(
55
+ :success,
56
+ 'Dwf::Callback#process_next_step',
57
+ names: [job.name],
58
+ workflow_id: job.workflow_id
59
+ )
60
+ batch.jobs { job.perform_async }
61
+ end
62
+
63
+ def client
64
+ @client ||= Dwf::Client.new
65
+ end
66
+ end
67
+ end
data/lib/dwf/client.rb ADDED
@@ -0,0 +1,100 @@
1
+ module Dwf
2
+ class Client
3
+ def find_job(workflow_id, job_name)
4
+ job_name_match = /(?<klass>\w*[^-])-(?<identifier>.*)/.match(job_name)
5
+ data = if job_name_match
6
+ find_job_by_klass_and_id(workflow_id, job_name)
7
+ else
8
+ find_job_by_klass(workflow_id, job_name)
9
+ end
10
+
11
+ return nil if data.nil?
12
+
13
+ data = JSON.parse(data)
14
+ Dwf::Item.from_hash(Dwf::Utils.symbolize_keys(data))
15
+ end
16
+
17
+ def persist_job(job)
18
+ redis.hset("dwf.jobs.#{job.workflow_id}.#{job.klass}", job.id, job.as_json)
19
+ end
20
+
21
+ def check_or_lock(workflow_id, job_name)
22
+ key = "wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}"
23
+
24
+ if key_exists?(key)
25
+ sleep 2
26
+ else
27
+ set(key, 'running')
28
+ end
29
+ end
30
+
31
+ def release_lock(workflow_id, job_name)
32
+ delete("dwf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}")
33
+ end
34
+
35
+ def persist_workflow(workflow)
36
+ redis.set("dwf.workflows.#{workflow.id}", workflow.as_json)
37
+ end
38
+
39
+ def build_job_id(workflow_id, job_klass)
40
+ jid = nil
41
+
42
+ loop do
43
+ jid = SecureRandom.uuid
44
+ available = !redis.hexists(
45
+ "dwf.jobs.#{workflow_id}.#{job_klass}",
46
+ jid
47
+ )
48
+
49
+ break if available
50
+ end
51
+
52
+ jid
53
+ end
54
+
55
+ def build_workflow_id
56
+ wid = nil
57
+ loop do
58
+ wid = SecureRandom.uuid
59
+ available = !redis.exists?("dwf.workflow.#{wid}")
60
+
61
+ break if available
62
+ end
63
+
64
+ wid
65
+ end
66
+
67
+ def key_exists?(key)
68
+ redis.exists?(key)
69
+ end
70
+
71
+ def set(key, value)
72
+ redis.set(key, value)
73
+ end
74
+
75
+ def delete(key)
76
+ redis.del(key)
77
+ end
78
+
79
+ private
80
+
81
+ def find_job_by_klass_and_id(workflow_id, job_name)
82
+ job_klass, job_id = job_name.split('|')
83
+
84
+ redis.hget("dwf.jobs.#{workflow_id}.#{job_klass}", job_id)
85
+ end
86
+
87
+ def find_job_by_klass(workflow_id, job_name)
88
+ _new_cursor, result = redis.hscan("dwf.jobs.#{workflow_id}.#{job_name}", 0, count: 1)
89
+ return nil if result.empty?
90
+
91
+ _job_id, job = *result[0]
92
+
93
+ job
94
+ end
95
+
96
+ def redis
97
+ @redis ||= Redis.new
98
+ end
99
+ end
100
+ end
data/lib/dwf/item.rb ADDED
@@ -0,0 +1,153 @@
1
+ require_relative 'client'
2
+
3
+ module Dwf
4
+ class Item
5
+ attr_reader :workflow_id, :id, :params, :queue, :klass, :started_at,
6
+ :enqueued_at, :finished_at, :failed_at
7
+ attr_accessor :incomming, :outgoing
8
+
9
+ def initialize(options = {})
10
+ @workflow_id = options[:workflow_id]
11
+ @id = options[:id]
12
+ @params = options[:params]
13
+ @queue = options[:queue] || 'default'
14
+ @incomming = options[:incoming] || []
15
+ @outgoing = options[:outgoing] || []
16
+ @klass = options[:klass] || self.class
17
+ @finished_at = options[:finished_at]
18
+ @enqueued_at = options[:enqueued_at]
19
+ @started_at = options[:started_at]
20
+ end
21
+
22
+ def self.from_hash(hash)
23
+ Module.const_get(hash[:klass]).new(hash)
24
+ end
25
+
26
+ def persist_and_perform_async!
27
+ enqueue!
28
+ persist!
29
+ perform_async
30
+ end
31
+
32
+ def perform; end
33
+
34
+ def perform_async
35
+ Dwf::Worker.set(queue: queue).perform_async(workflow_id, name)
36
+ end
37
+
38
+ def name
39
+ @name ||= "#{klass}|#{id}"
40
+ end
41
+
42
+ def no_dependencies?
43
+ incomming.empty?
44
+ end
45
+
46
+ def parents_succeeded?
47
+ incomming.all? do |name|
48
+ client.find_job(workflow_id, name).succeeded?
49
+ end
50
+ end
51
+
52
+ def enqueue!
53
+ @enqueued_at = current_timestamp
54
+ @started_at = nil
55
+ @finished_at = nil
56
+ @failed_at = nil
57
+ end
58
+
59
+ def mark_as_started
60
+ start!
61
+ persist!
62
+ end
63
+
64
+ def mark_as_finished
65
+ finish!
66
+ persist!
67
+ end
68
+
69
+ def start!
70
+ @started_at = current_timestamp
71
+ @failed_at = nil
72
+ end
73
+
74
+ def finish!
75
+ @finished_at = current_timestamp
76
+ end
77
+
78
+ def fail!
79
+ @finished_at = @failed_at = current_timestamp
80
+ end
81
+
82
+ def enqueued?
83
+ !enqueued_at.nil?
84
+ end
85
+
86
+ def finished?
87
+ !finished_at.nil?
88
+ end
89
+
90
+ def failed?
91
+ !failed_at.nil?
92
+ end
93
+
94
+ def succeeded?
95
+ finished? && !failed?
96
+ end
97
+
98
+ def started?
99
+ !started_at.nil?
100
+ end
101
+
102
+ def running?
103
+ started? && !finished?
104
+ end
105
+
106
+ def ready_to_start?
107
+ !running? && !enqueued? && !finished? && !failed? && parents_succeeded?
108
+ end
109
+
110
+ def current_timestamp
111
+ Time.now.to_i
112
+ end
113
+
114
+ def enqueue_outgoing_jobs
115
+ outgoing.each do |job_name|
116
+ client.check_or_lock(workflow_id, job_name)
117
+ out = client.find_job(workflow_id, job_name)
118
+ out.persist_and_perform_async! if out.ready_to_start?
119
+ client.release_lock(workflow_id, job_name)
120
+ end
121
+ end
122
+
123
+ def to_hash
124
+ {
125
+ id: id,
126
+ klass: klass.to_s,
127
+ queue: queue,
128
+ incoming: incomming,
129
+ outgoing: outgoing,
130
+ finished_at: finished_at,
131
+ enqueued_at: enqueued_at,
132
+ started_at: started_at,
133
+ failed_at: failed_at,
134
+ params: params,
135
+ workflow_id: workflow_id
136
+ }
137
+ end
138
+
139
+ def as_json
140
+ to_hash.to_json
141
+ end
142
+
143
+ def persist!
144
+ client.persist_job(self)
145
+ end
146
+
147
+ private
148
+
149
+ def client
150
+ @client ||= Dwf::Client.new
151
+ end
152
+ end
153
+ end
data/lib/dwf/utils.rb ADDED
@@ -0,0 +1,38 @@
1
+ module Dwf
2
+ module Utils
3
+ def self.symbolize_keys(obj)
4
+ case obj
5
+ when Array
6
+ obj.inject([]) do |res, val|
7
+ res << case val
8
+ when Hash, Array
9
+ symbolize_keys(val)
10
+ else
11
+ val
12
+ end
13
+
14
+ res
15
+ end
16
+ when Hash
17
+ obj.inject({}) do |res, (key, val)|
18
+ nkey = case key
19
+ when String
20
+ key.to_sym
21
+ else
22
+ key
23
+ end
24
+ nval = case val
25
+ when Hash, Array
26
+ symbolize_keys(val)
27
+ else
28
+ val
29
+ end
30
+ res[nkey] = nval
31
+ res
32
+ end
33
+ else
34
+ obj
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/dwf/worker.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'sidekiq'
2
+ require_relative 'client'
3
+
4
+ module Dwf
5
+ class Worker
6
+ include Sidekiq::Worker
7
+
8
+ def perform(workflow_id, job_name)
9
+ job = client.find_job(workflow_id, job_name)
10
+ return job.enqueue_outgoing_jobs if job. succeeded?
11
+
12
+ job.mark_as_started
13
+ job.perform
14
+ job.mark_as_finished
15
+ # job.enqueue_outgoing_jobs
16
+ end
17
+
18
+ private
19
+
20
+ def client
21
+ @client ||= Dwf::Client.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,169 @@
1
+ require_relative 'client'
2
+ require_relative 'worker'
3
+ require_relative 'callback'
4
+
5
+ module Dwf
6
+ class Workflow
7
+ attr_reader :dependencies, :jobs, :started_at, :finished_at, :persisted, :stopped
8
+
9
+ class << self
10
+ def create
11
+ flow = new
12
+ flow.save
13
+ flow
14
+ end
15
+ end
16
+
17
+ def initialize
18
+ @dependencies = []
19
+ @id = id
20
+ @jobs = []
21
+ @persisted = false
22
+ @stopped = false
23
+
24
+ setup
25
+ end
26
+
27
+ def start!
28
+ initial_jobs.each do |job|
29
+ Dwf::Callback.new.start(job)
30
+ end
31
+ end
32
+
33
+ def save
34
+ client.persist_workflow(self)
35
+ jobs.each(&:persist!)
36
+ mark_as_persisted
37
+ true
38
+ end
39
+
40
+ def id
41
+ @id ||= client.build_workflow_id
42
+ end
43
+
44
+ def configure; end
45
+
46
+ def run(klass, options = {})
47
+ node = klass.new(
48
+ workflow_id: id,
49
+ id: client.build_job_id(id, klass.to_s),
50
+ params: options.fetch(:params, {}),
51
+ queue: options[:queue],
52
+ )
53
+
54
+ jobs << node
55
+
56
+ build_dependencies_structure(node, options)
57
+ node.name
58
+ end
59
+
60
+ def find_job(name)
61
+ match_data = /(?<klass>\w*[^-])-(?<identifier>.*)/.match(name.to_s)
62
+
63
+ if match_data.nil?
64
+ job = jobs.find { |node| node.klass.to_s == name.to_s }
65
+ else
66
+ job = jobs.find { |node| node.name.to_s == name.to_s }
67
+ end
68
+
69
+ job
70
+ end
71
+
72
+ def to_hash
73
+ name = self.class.to_s
74
+ {
75
+ name: name,
76
+ id: id,
77
+ arguments: @arguments,
78
+ total: jobs.count,
79
+ finished: jobs.count(&:finished?),
80
+ klass: name,
81
+ status: status,
82
+ stopped: stopped,
83
+ started_at: started_at,
84
+ finished_at: finished_at
85
+ }
86
+ end
87
+
88
+ def as_json
89
+ to_hash.to_json
90
+ end
91
+
92
+ def finished?
93
+ jobs.all?(&:finished?)
94
+ end
95
+
96
+ def started?
97
+ !!started_at
98
+ end
99
+
100
+ def running?
101
+ started? && !finished?
102
+ end
103
+
104
+ def failed?
105
+ jobs.any?(&:failed?)
106
+ end
107
+
108
+ def stopped?
109
+ stopped
110
+ end
111
+
112
+ def status
113
+ return :failed if failed?
114
+ return :running if running?
115
+ return :finished if finished?
116
+ return :stopped if stopped?
117
+
118
+ :running
119
+ end
120
+
121
+ def mark_as_persisted
122
+ @persisted = true
123
+ end
124
+
125
+ def mark_as_started
126
+ @stopped = false
127
+ end
128
+
129
+
130
+ private
131
+
132
+ def initial_jobs
133
+ jobs.select(&:no_dependencies?)
134
+ end
135
+
136
+ def setup
137
+ configure
138
+ resolve_dependencies
139
+ end
140
+
141
+ def resolve_dependencies
142
+ @dependencies.each do |dependency|
143
+ from = find_job(dependency[:from])
144
+ to = find_job(dependency[:to])
145
+
146
+ to.incomming << dependency[:from]
147
+ from.outgoing << dependency[:to]
148
+ end
149
+ end
150
+
151
+ def build_dependencies_structure(node, options)
152
+ deps_after = [*options[:after]]
153
+
154
+ deps_after.each do |dep|
155
+ @dependencies << { from: dep.to_s, to: node.name.to_s }
156
+ end
157
+
158
+ deps_before = [*options[:before]]
159
+
160
+ deps_before.each do |dep|
161
+ @dependencies << { from: node.name.to_s, to: dep.to_s }
162
+ end
163
+ end
164
+
165
+ def client
166
+ @client ||= Dwf::Client.new
167
+ end
168
+ end
169
+ end
data/lib/dwf.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require "bundler/setup"
3
+
4
+ require 'sidekiq'
5
+ require 'sidekiq-pro'
6
+ require 'json'
7
+ require 'byebug'
8
+ require 'redis'
9
+
10
+ require_relative 'dwf/utils'
11
+ require_relative 'dwf/workflow'
12
+ require_relative 'dwf/item'
13
+ require_relative 'dwf/client'
14
+ require_relative 'dwf/worker'
15
+ require_relative 'dwf/callback'
16
+
17
+ module Dwf
18
+ VERSION = '0.1.0'
19
+ end
20
+
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dwf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - dthtien
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: byebug
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 11.1.3
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 11.1.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 4.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 4.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sidekiq
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 6.2.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 6.2.0
69
+ description: Workflow
70
+ email:
71
+ - tiendt2311@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".ruby-version"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - dwf.gemspec
82
+ - lib/dwf.rb
83
+ - lib/dwf/callback.rb
84
+ - lib/dwf/client.rb
85
+ - lib/dwf/item.rb
86
+ - lib/dwf/utils.rb
87
+ - lib/dwf/worker.rb
88
+ - lib/dwf/workflow.rb
89
+ homepage: https://github.com/dthtien/wf
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 2.4.0
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.2.3
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Gush cloned without ActiveJob but requried Sidekiq. This project is for researching
112
+ DSL purpose
113
+ test_files: []