dwf 0.1.6 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a08560cdfefa1c9f8e1f42416599e94550de0f0e1492bb5b05424e2bf94a482f
4
- data.tar.gz: 84a83b6849b64e7fe5da74f5734e2d8faa96df25eef63a5d0de78229fcc733ba
3
+ metadata.gz: fb0f6f6f30b7ed1ba19120a4a1f77e450940e45433153de51f47df9f108d0fae
4
+ data.tar.gz: e0e86fdd169b7cbcdc811cf62f87b3043fbbd708bdf0f41fb006b392e4ab784b
5
5
  SHA512:
6
- metadata.gz: 6eb847d86872dfa70c1a602a3ae8ef0e0ca166cfa22d0a5e3706454ef0144ac434b48489a916d811e9dc66ebb9ef4979b00b63dee8da4d08c44af6d1bd714dad
7
- data.tar.gz: cda011bc87919531f5de78721c3640c2460119cb4dd5668bd4ebb8093e19d46e848d27cfc2112c10021bd41e52e2365ced092f779a25a1509086fdf7095221a9
6
+ metadata.gz: 4a3770d4d7f63b7ede02d59e1ce43cb2634b39cbd9e037c920e4f816acb7b393de0f9030fda735e8ea0873f3679da120ff405f725e87c60c1b9eb6a7fbf355e3
7
+ data.tar.gz: 469e25cdf11441c59c80ff47197dcabcf40e0b086ab06babb6b39a0597483517c479ee9ea65767819b69afebf83db6dcdb5926a9839a2e40d02c03cf39a6eb57
@@ -8,10 +8,6 @@ on:
8
8
  branches: [ master ]
9
9
  paths:
10
10
  - 'dwf.gemspec'
11
- pull_request:
12
- branches: [ master ]
13
- paths:
14
- - 'dwf.gemspec'
15
11
 
16
12
  jobs:
17
13
  build:
@@ -3,7 +3,7 @@ name: Test
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - '**'
6
+ - 'master'
7
7
  pull_request:
8
8
  branches:
9
9
  - '**'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+ ## 0.1.7
4
+ ### Added
5
+ - Allow to config redis and queue
6
+
7
+ ```ruby
8
+ Dwf.config do |config|
9
+ config.opts = { url 'redis://127.0.0.1:6379' }
10
+ config.namespace = 'dwf'
11
+ end
12
+ ```
13
+
3
14
  ## 0.1.6
4
15
  ### Added
5
16
  - 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.5'
7
+ gem 'dwf', '~> 0.1.6'
8
8
  ```
9
9
  ## 2. Execute flow
10
10
  ### Declare jobs
@@ -38,12 +38,6 @@ class TestWf < Dwf::Workflow
38
38
  end
39
39
  ```
40
40
 
41
- #### Note
42
- `dwf` supports 2 callback types `Dwf::Workflow::BUILD_IN` and `Dwf::Workflow::SK_BATCH`
43
- - `Dwf::Workflow::BUILD_IN` is a build-in callback
44
- - `Dwf::Workflow::SK_BATCH` is [sidekiq batch](https://github.com/mperham/sidekiq/wiki/Batches) callback which required [`sidekiq-pro`](https://sidekiq.org/products/pro.html)
45
-
46
- By default `dwf` will use `Dwf::Workflow::BUILD_IN` callback.
47
41
 
48
42
  ### Execute flow
49
43
  ```ruby
@@ -51,6 +45,13 @@ wf = TestWf.create(callback_type: Dwf::Workflow::SK_BATCH)
51
45
  wf.start!
52
46
  ```
53
47
 
48
+ #### Note
49
+ `dwf` supports 2 callback types `Dwf::Workflow::BUILD_IN` and `Dwf::Workflow::SK_BATCH`
50
+ - `Dwf::Workflow::BUILD_IN` is a build-in callback
51
+ - `Dwf::Workflow::SK_BATCH` is [sidekiq batch](https://github.com/mperham/sidekiq/wiki/Batches) callback which required [`sidekiq-pro`](https://sidekiq.org/products/pro.html)
52
+
53
+ By default `dwf` will use `Dwf::Workflow::BUILD_IN` callback.
54
+
54
55
  ### Output
55
56
  ```
56
57
  A Working
@@ -70,11 +71,25 @@ D say hello
70
71
  D Finished
71
72
  ```
72
73
 
74
+ # Config redis and default queue
75
+ ```ruby
76
+ Dwf.config do |config|
77
+ SENTINELS = [
78
+ { host: "127.0.0.1", port: 26380 },
79
+ { host: "127.0.0.1", port: 26381 }
80
+ ]
81
+ config.opts = { host: 'mymaster', sentinels: SENTINELS, role: :master }
82
+ config.namespace = 'dwf'
83
+ end
84
+ ```
85
+
86
+
73
87
  # Todo
74
88
  - [x] Make it work
75
89
  - [x] Support pass params
76
90
  - [x] Support with build-in callback
77
91
  - [x] Add github workflow
92
+ - [x] Redis configurable
78
93
  - [ ] [WIP] Test
79
94
  - [ ] Transfer output through each node
80
95
  - [ ] Support [Resque](https://github.com/resque/resque)
data/dwf.gemspec CHANGED
@@ -3,10 +3,11 @@
3
3
 
4
4
  lib = File.expand_path('../lib', __FILE__)
5
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require_relative 'lib/dwf/version'
6
7
 
7
8
  Gem::Specification.new do |spec|
8
9
  spec.name = "dwf"
9
- spec.version = '0.1.6'
10
+ spec.version = Dwf::VERSION
10
11
  spec.authors = ["dthtien"]
11
12
  spec.email = ["tiendt2311@gmail.com"]
12
13
 
@@ -27,5 +28,6 @@ Gem::Specification.new do |spec|
27
28
  spec.add_development_dependency 'byebug', '~> 11.1.3'
28
29
  spec.add_dependency 'redis', '~> 4.2.0'
29
30
  spec.add_development_dependency 'rspec', '~> 3.2'
31
+ spec.add_development_dependency 'mock_redis', '~> 0.27.2'
30
32
  spec.add_dependency 'sidekiq', '~> 6.2.0'
31
33
  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 { setup_batch(processing_job_names, workflow_id) }
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 setup_batch(processing_job_names, workflow_id)
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.values.each do |batch_jobs|
29
- batch = Sidekiq::Batch.new
30
- batch.on(
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? ? 'default_key' : outgoing_jobs.join
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,11 @@
1
1
  module Dwf
2
2
  class Client
3
+ attr_reader :config
4
+
5
+ def initialize(config = Dwf.configuration)
6
+ @config = config
7
+ end
8
+
3
9
  def find_job(workflow_id, job_name)
4
10
  job_name_match = /(?<klass>\w*[^-])-(?<identifier>.*)/.match(job_name)
5
11
  data = if job_name_match
@@ -94,7 +100,7 @@ module Dwf
94
100
  end
95
101
 
96
102
  def redis
97
- @redis ||= Redis.new
103
+ @redis ||= Redis.new(config.redis_opts)
98
104
  end
99
105
  end
100
106
  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_url, REDIS_OPTS)
13
+ end
14
+ end
15
+ end
data/lib/dwf/item.rb CHANGED
@@ -1,10 +1,9 @@
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
8
  :enqueued_at, :finished_at, :failed_at, :callback_type
10
9
  attr_accessor :incoming, :outgoing
@@ -13,7 +12,7 @@ module Dwf
13
12
  @workflow_id = options[:workflow_id]
14
13
  @id = options[:id]
15
14
  @params = options[:params]
16
- @queue = options[:queue] || DEFAULT_QUEUE
15
+ @queue = options[:queue]
17
16
  @incoming = options[:incoming] || []
18
17
  @outgoing = options[:outgoing] || []
19
18
  @klass = options[:klass] || self.class
@@ -41,7 +40,8 @@ module Dwf
41
40
  end
42
41
 
43
42
  def perform_async
44
- Dwf::Worker.set(queue: queue).perform_async(workflow_id, name)
43
+ Dwf::Worker.set(queue: queue || client.config.namespace)
44
+ .perform_async(workflow_id, name)
45
45
  end
46
46
 
47
47
  def name
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dwf
4
+ VERSION = '0.1.7'
5
+ end
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
 
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
- VERSION = '0.1.6'
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,154 @@
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 '#persist_job' do
43
+ let!(:job) { Dwf::Item.new(workflow_id: workflow_id, id: id) }
44
+
45
+ it do
46
+ expect(redis.exists?("dwf.jobs.#{job.workflow_id}.#{job.klass}"))
47
+ .to be_falsy
48
+
49
+ client.persist_job(job)
50
+
51
+ expect(redis.exists?("dwf.jobs.#{job.workflow_id}.#{job.klass}"))
52
+ .to be_truthy
53
+ end
54
+ end
55
+
56
+ describe '#persist_workflow' do
57
+ let(:workflow) { Dwf::Workflow.new }
58
+
59
+ it do
60
+ expect(redis.exists?("dwf.workflows.#{workflow.id}")).to be_falsy
61
+ client.persist_workflow(workflow)
62
+ expect(redis.exists?("dwf.workflows.#{workflow.id}")).to be_truthy
63
+ end
64
+ end
65
+
66
+ describe '#check_or_lock' do
67
+ before do
68
+ allow_any_instance_of(described_class).to receive(:sleep)
69
+ end
70
+
71
+ context 'job is running' do
72
+ let(:job_name) { 'ahihi' }
73
+
74
+ before do
75
+ allow(client).to receive(:set)
76
+ redis.set("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
77
+ client.check_or_lock(workflow_id, job_name)
78
+ end
79
+
80
+ it { expect(client).not_to have_received(:set) }
81
+ end
82
+
83
+ context 'job is not running' do
84
+ let(:job_name) { 'ahihi' }
85
+
86
+ before do
87
+ allow(redis).to receive(:set)
88
+ client.check_or_lock(workflow_id, job_name)
89
+ end
90
+
91
+ it do
92
+ expect(redis).to have_received(:set)
93
+ .with("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
94
+ end
95
+ end
96
+ end
97
+
98
+ describe '#release_lock' do
99
+ before do
100
+ allow(redis).to receive(:del)
101
+ client.release_lock(workflow_id, 'ahihi')
102
+ end
103
+
104
+ it do
105
+ expect(redis).to have_received(:del)
106
+ .with("dwf_enqueue_outgoing_jobs_#{workflow_id}-ahihi")
107
+ end
108
+ end
109
+
110
+ describe '#build_job_id' do
111
+ before do
112
+ allow(redis).to receive(:hexists)
113
+ client.build_job_id(workflow_id, 'ahihi')
114
+ end
115
+
116
+ it { expect(redis).to have_received(:hexists) }
117
+ end
118
+
119
+ describe '#build_workflow_id' do
120
+ before do
121
+ allow(redis).to receive(:exists?)
122
+ client.build_workflow_id
123
+ end
124
+
125
+ it { expect(redis).to have_received(:exists?) }
126
+ end
127
+
128
+ describe '#key_exists?' do
129
+ before do
130
+ allow(redis).to receive(:exists?)
131
+ client.key_exists?('ahihi')
132
+ end
133
+
134
+ it { expect(redis).to have_received(:exists?).with('ahihi') }
135
+ end
136
+
137
+ describe '#set' do
138
+ before do
139
+ allow(redis).to receive(:set)
140
+ client.set('ahihi', 'a')
141
+ end
142
+
143
+ it { expect(redis).to have_received(:set).with('ahihi', 'a') }
144
+ end
145
+
146
+ describe '#delete' do
147
+ before do
148
+ allow(redis).to receive(:del)
149
+ client.delete('ahihi')
150
+ end
151
+
152
+ it { expect(redis).to have_received(:del).with('ahihi') }
153
+ end
154
+ 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
@@ -16,7 +16,7 @@ describe Dwf::Item, item: true do
16
16
  params: {},
17
17
  incoming: incoming,
18
18
  outgoing: outgoing,
19
- queue: Dwf::Item::DEFAULT_QUEUE,
19
+ queue: Dwf::Configuration::NAMESPACE,
20
20
  klass: 'Dwf::Item',
21
21
  started_at: started_at,
22
22
  finished_at: finished_at,
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.6
4
+ version: 0.1.7
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-06 00:00:00.000000000 Z
11
+ date: 2021-09-08 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,10 +100,14 @@ files:
86
100
  - lib/dwf.rb
87
101
  - lib/dwf/callback.rb
88
102
  - lib/dwf/client.rb
103
+ - lib/dwf/configuration.rb
89
104
  - lib/dwf/item.rb
90
105
  - lib/dwf/utils.rb
106
+ - lib/dwf/version.rb
91
107
  - lib/dwf/worker.rb
92
108
  - lib/dwf/workflow.rb
109
+ - spec/dwf/client_spec.rb
110
+ - spec/dwf/configuration_spec.rb
93
111
  - spec/dwf/item_spec.rb
94
112
  - spec/dwf/utils_spec.rb
95
113
  - spec/spec_helper.rb
@@ -118,6 +136,8 @@ specification_version: 4
118
136
  summary: Gush cloned without ActiveJob but requried Sidekiq. This project is for researching
119
137
  DSL purpose
120
138
  test_files:
139
+ - spec/dwf/client_spec.rb
140
+ - spec/dwf/configuration_spec.rb
121
141
  - spec/dwf/item_spec.rb
122
142
  - spec/dwf/utils_spec.rb
123
143
  - spec/spec_helper.rb