workflow-join 0.2.1 → 0.3.0
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/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +2 -13
- data/Gemfile +2 -0
- data/README.md +37 -0
- data/lib/workflow/join.rb +54 -34
- data/lib/workflow/join/sidekiq.rb +3 -0
- data/lib/workflow/join/sidekiq/job.rb +125 -0
- data/lib/workflow/join/version.rb +1 -1
- data/workflow-join.gemspec +6 -4
- metadata +41 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c3c2da76cc4df53310b7bf849f82b376191ea55
|
4
|
+
data.tar.gz: 1c180e494c16da06665ceda094062e1da1825578
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a3b94ed6356eca07083c260186f28ff580ee5b535ba53148b2baeb03f232710c3b45dd641d0371576d055f35db0b3110d80da1a44bab6e67f2712d566dbfce0
|
7
|
+
data.tar.gz: bbcf979a422528c2bade2bdbb26abf103c03d8866a7ec3f84528eb122035f386aa4c9906c85b1d564027a5617b2d7df69f304955c3e0e53d971ebfa708e657c9
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2016-
|
3
|
+
# on 2016-11-17 12:26:01 +0100 using RuboCop version 0.42.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -14,10 +14,6 @@ Lint/BlockAlignment:
|
|
14
14
|
Exclude:
|
15
15
|
- 'lib/workflow/join.rb'
|
16
16
|
|
17
|
-
# Offense count: 1
|
18
|
-
Metrics/AbcSize:
|
19
|
-
Max: 57
|
20
|
-
|
21
17
|
# Offense count: 1
|
22
18
|
# Cop supports --auto-correct.
|
23
19
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
@@ -26,12 +22,6 @@ Style/LambdaCall:
|
|
26
22
|
Exclude:
|
27
23
|
- 'lib/workflow/join.rb'
|
28
24
|
|
29
|
-
# Offense count: 1
|
30
|
-
# Cop supports --auto-correct.
|
31
|
-
Style/MutableConstant:
|
32
|
-
Exclude:
|
33
|
-
- 'lib/workflow/join/version.rb'
|
34
|
-
|
35
25
|
# Offense count: 2
|
36
26
|
# Cop supports --auto-correct.
|
37
27
|
# Configuration parameters: PreferredDelimiters.
|
@@ -47,14 +37,13 @@ Style/RescueModifier:
|
|
47
37
|
- 'lib/workflow/join.rb'
|
48
38
|
- 'lib/workflow/join/active_record/pending_callbacks.rb'
|
49
39
|
|
50
|
-
# Offense count:
|
40
|
+
# Offense count: 3
|
51
41
|
# Cop supports --auto-correct.
|
52
42
|
# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
|
53
43
|
# SupportedStyles: single_quotes, double_quotes
|
54
44
|
Style/StringLiterals:
|
55
45
|
Exclude:
|
56
46
|
- 'bin/console'
|
57
|
-
- 'lib/workflow/join/version.rb'
|
58
47
|
|
59
48
|
# Offense count: 2
|
60
49
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -105,6 +105,43 @@ created and simple application restart will enable the feature.
|
|
105
105
|
This part is hardly tested, though, that’s why the preferred way would be to
|
106
106
|
create columns manually.
|
107
107
|
|
108
|
+
### Use Sidekiq jobs as guards
|
109
|
+
|
110
|
+
Starting with version `0.3.0`, we support sidekiq jobs as guards:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class LongRunningChecker
|
114
|
+
include ::Sidekiq::Worker
|
115
|
+
|
116
|
+
def perform(*_args)
|
117
|
+
do_requests(*args) # sleep 10
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Master < ActiveRecord::Base
|
122
|
+
include Workflow
|
123
|
+
|
124
|
+
workflow do
|
125
|
+
state :initial do
|
126
|
+
event :validate, transitions_to: :validated
|
127
|
+
end
|
128
|
+
state :validated
|
129
|
+
|
130
|
+
guard inner: :validated, job: LongRunningChecker
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
## Changelog
|
136
|
+
|
137
|
+
#### `0.3.0` `Sidekiq` jobs as guards
|
138
|
+
|
139
|
+
#### `0.2.4` `ActiveRecord` support
|
140
|
+
|
141
|
+
## It is of any good?
|
142
|
+
|
143
|
+
Yes.
|
144
|
+
|
108
145
|
## Development
|
109
146
|
|
110
147
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/workflow/join.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'workflow'
|
2
2
|
require 'workflow/join/version'
|
3
3
|
|
4
|
-
if ENV['USE_SIMPLE_PERSISTENCE'] == 'true'
|
5
|
-
|
6
|
-
|
7
|
-
else
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
4
|
+
with_ar = if ENV['USE_SIMPLE_PERSISTENCE'] == 'true'
|
5
|
+
require 'workflow/join/simple'
|
6
|
+
puts "☆ Using simple mode (no persistence,) due to ‘ENV['USE_SIMPLE_PERSISTENCE']’"
|
7
|
+
else
|
8
|
+
begin
|
9
|
+
require 'active_record'
|
10
|
+
require 'workflow/join/active_record'
|
11
|
+
rescue LoadError => e
|
12
|
+
require 'workflow/join/simple'
|
13
|
+
puts "★ Error requiring ActiveRecord (message: “#{e.message}”.) Will run in simple mode (no persistence.)"
|
14
|
+
end
|
15
|
+
end
|
16
16
|
|
17
|
+
# rubocop:disable Metrics/AbcSize
|
17
18
|
Workflow::ClassMethods.prepend(Module.new do
|
18
19
|
def workflow(&specification)
|
19
20
|
# extend instances
|
@@ -78,40 +79,59 @@ Workflow::ClassMethods.prepend(Module.new do
|
|
78
79
|
super
|
79
80
|
end
|
80
81
|
end)
|
82
|
+
# rubocop:enable Metrics/AbcSize
|
81
83
|
|
82
84
|
module Workflow
|
83
85
|
module Join
|
84
|
-
GUARD_PARAMS_ERROR = '
|
85
|
-
GUARD_POINTCUT_ERROR = 'Both :inner and :outer
|
86
|
+
GUARD_PARAMS_ERROR = 'One of: [guard instance variable, code block, job] is required'.freeze
|
87
|
+
GUARD_POINTCUT_ERROR = 'Both :inner and :outer state / :job are required'.freeze
|
86
88
|
GUARD_IS_NOT_WORKFLOW = 'Guard given must be a workflow instance, was: “%s”'.freeze
|
89
|
+
DEVELOPER_ERROR = 'Developer is an idiot, please excuse and file and issue'.freeze
|
87
90
|
|
88
91
|
def guards
|
89
92
|
@guards ||= {}
|
90
93
|
end
|
91
94
|
|
92
|
-
def guard(getter = nil, inner: nil, outer: nil)
|
93
|
-
fail Workflow::WorkflowDefinitionError, GUARD_PARAMS_ERROR unless
|
94
|
-
fail Workflow::WorkflowDefinitionError, GUARD_POINTCUT_ERROR unless inner && outer
|
95
|
-
|
96
|
-
guard =
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
case
|
102
|
-
when /\A@/ =~ g.to_s && host.instance_variable_defined?(g)
|
103
|
-
host.instance_variable_get(g)
|
104
|
-
when host.methods.include?(g) && host.method(g).arity <= 0
|
105
|
-
host.send g
|
106
|
-
end.tap do |guard_instance|
|
107
|
-
fail Workflow::WorkflowDefinitionError, GUARD_IS_NOT_WORKFLOW % guard_instance \
|
108
|
-
unless guard_instance.nil? || guard_instance.is_a?(Workflow)
|
109
|
-
end
|
110
|
-
end
|
95
|
+
def guard(getter = nil, inner: nil, outer: nil, job: nil)
|
96
|
+
fail Workflow::WorkflowDefinitionError, GUARD_PARAMS_ERROR unless [getter, job, block_given?].one?
|
97
|
+
fail Workflow::WorkflowDefinitionError, GUARD_POINTCUT_ERROR unless inner && (outer || job)
|
98
|
+
|
99
|
+
guard = case getter ||= job
|
100
|
+
when NilClass then Proc.new # block_given? == true, see L#97 check
|
101
|
+
when Symbol, String then guard_for_instance_variable(getter)
|
102
|
+
when Class then guard_for_class(getter)
|
103
|
+
else fail Workflow::WorkflowDefinitionError, DEVELOPER_ERROR
|
111
104
|
end
|
112
|
-
(guards[inner.to_sym] ||= []) << [guard, outer.to_sym]
|
105
|
+
(guards[inner.to_sym] ||= []) << [guard, (outer || ::Workflow::Join::Sidekiq::Job::DONE).to_sym]
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def guard_for_instance_variable(getter)
|
111
|
+
g = getter.to_sym
|
112
|
+
lambda do |host|
|
113
|
+
case
|
114
|
+
when /\A@/ =~ g.to_s && host.instance_variable_defined?(g)
|
115
|
+
host.instance_variable_get(g)
|
116
|
+
when host.methods.include?(g) && host.method(g).arity <= 0
|
117
|
+
host.send g
|
118
|
+
end.tap do |guard_instance|
|
119
|
+
fail Workflow::WorkflowDefinitionError, GUARD_IS_NOT_WORKFLOW % guard_instance \
|
120
|
+
unless guard_instance.nil? || guard_instance.is_a?(Workflow)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def guard_for_class(getter)
|
126
|
+
lambda do |host|
|
127
|
+
Workflow::Join::Sidekiq::Job.lookup!(host, getter).tap do |job|
|
128
|
+
job.run! if job.can_run?
|
129
|
+
end
|
130
|
+
end
|
113
131
|
end
|
114
132
|
end
|
115
133
|
end
|
116
134
|
|
117
135
|
Workflow::Specification.prepend Workflow::Join
|
136
|
+
|
137
|
+
require 'workflow/join/sidekiq' if with_ar
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Workflow
|
2
|
+
module Join
|
3
|
+
module Sidekiq
|
4
|
+
class Job < ::ActiveRecord::Base
|
5
|
+
# attr_accessor :worker, :args, :result, :fails
|
6
|
+
DONE = :done
|
7
|
+
TABLE_NAME = 'workflow_jobs'.freeze
|
8
|
+
|
9
|
+
self.table_name = TABLE_NAME
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def migration(table_name = TABLE_NAME)
|
13
|
+
<<-MIGRATION
|
14
|
+
class CreateWorkflowJobs < ::ActiveRecord::Migration
|
15
|
+
def change
|
16
|
+
create_table :#{table_name} do |t|
|
17
|
+
t.string :workflow_state, default: 'scheduled', null: false
|
18
|
+
|
19
|
+
t.string :host
|
20
|
+
t.integer :host_id
|
21
|
+
t.string :worker
|
22
|
+
|
23
|
+
t.text :args
|
24
|
+
t.text :result
|
25
|
+
t.text :fails
|
26
|
+
|
27
|
+
t.string :workflow_pending_transitions
|
28
|
+
t.string :workflow_pending_callbacks
|
29
|
+
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
add_index :#{table_name}, :workflow_state, unique: false
|
34
|
+
add_index :#{table_name}, [:host, :host_id, :worker], unique: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
MIGRATION
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup!(host, worker)
|
41
|
+
params = { host: host.class.to_s, host_id: host.id, worker: worker.to_s }
|
42
|
+
where(**params).last || create!(**params)
|
43
|
+
end
|
44
|
+
|
45
|
+
def worker(worker)
|
46
|
+
case worker
|
47
|
+
when String, Symbol then Kernel.const_get(worker)
|
48
|
+
when Module then worker
|
49
|
+
else fail ArgumentError, "Workflow::Join::Sidekiq::Job#worker expects a string/class as an argument, got #{worker.inspect}."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private_class_method :new, :create, :create! # use lookup!, buddy
|
55
|
+
|
56
|
+
########################################################################
|
57
|
+
|
58
|
+
include Workflow
|
59
|
+
|
60
|
+
workflow do
|
61
|
+
state :scheduled do
|
62
|
+
event :run, transitions_to: :running
|
63
|
+
end
|
64
|
+
state :running do
|
65
|
+
event :fail, transitions_to: :failed
|
66
|
+
event :success, transitions_to: DONE
|
67
|
+
end
|
68
|
+
state :failed do
|
69
|
+
event :success, transitions_to: DONE
|
70
|
+
end
|
71
|
+
state DONE
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_running_entry(_old_state, _event, *args)
|
75
|
+
Job::Worker.perform_async(*args, ★: id) # FIXME: Anything more elegant?
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_failed_entry(_old_state, _event, *args)
|
79
|
+
timestamp = Time.respond_to?(:zone) && Time.zone ? Time.zone.now : Time.now
|
80
|
+
(self.fails ||= {})[timestamp] = args
|
81
|
+
end
|
82
|
+
|
83
|
+
########################################################################
|
84
|
+
|
85
|
+
serialize :args, Array
|
86
|
+
serialize :result, Hash
|
87
|
+
serialize :fails, Hash
|
88
|
+
|
89
|
+
def to_hash
|
90
|
+
{
|
91
|
+
# id: id,
|
92
|
+
host: host,
|
93
|
+
host_id: host_id,
|
94
|
+
worker: worker,
|
95
|
+
args: args,
|
96
|
+
result: result,
|
97
|
+
errors: fails,
|
98
|
+
workflow_state: workflow_state,
|
99
|
+
state: workflow_state.to_sym
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
########################################################################
|
104
|
+
|
105
|
+
class Worker
|
106
|
+
include ::Sidekiq::Worker
|
107
|
+
|
108
|
+
def perform(*args)
|
109
|
+
Job.find(args.pop['★']).tap do |job|
|
110
|
+
# FIXME: Log this somehow
|
111
|
+
begin
|
112
|
+
job.args = args
|
113
|
+
job.result = Job.worker(job.worker).new.perform(*job.args)
|
114
|
+
job.success!
|
115
|
+
rescue => e
|
116
|
+
job.fail! e
|
117
|
+
raise e
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/workflow-join.gemspec
CHANGED
@@ -24,13 +24,15 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.add_development_dependency 'bundler', '~> 1
|
28
|
-
spec.add_development_dependency 'rake', '~> 10
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10'
|
29
29
|
spec.add_development_dependency 'rspec'
|
30
30
|
spec.add_development_dependency 'rubocop'
|
31
31
|
spec.add_development_dependency 'pry'
|
32
32
|
spec.add_development_dependency 'sqlite3'
|
33
|
+
spec.add_development_dependency 'awesome_print'
|
33
34
|
|
34
|
-
spec.add_dependency 'workflow', '~> 1
|
35
|
-
spec.add_dependency 'activerecord', '
|
35
|
+
spec.add_dependency 'workflow', '~> 1'
|
36
|
+
spec.add_dependency 'activerecord', '~> 3'
|
37
|
+
spec.add_dependency 'sidekiq', '~> 3'
|
36
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workflow-join
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aleksei Matiushkin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
19
|
+
version: '1'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1
|
26
|
+
version: '1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '10
|
33
|
+
version: '10'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '10
|
40
|
+
version: '10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,40 +94,62 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: awesome_print
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: workflow
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '1
|
117
|
+
version: '1'
|
104
118
|
type: :runtime
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '1
|
124
|
+
version: '1'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: activerecord
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
|
-
- - "
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 3.2.1
|
118
|
-
- - "<"
|
129
|
+
- - "~>"
|
119
130
|
- !ruby/object:Gem::Version
|
120
|
-
version:
|
131
|
+
version: '3'
|
121
132
|
type: :runtime
|
122
133
|
prerelease: false
|
123
134
|
version_requirements: !ruby/object:Gem::Requirement
|
124
135
|
requirements:
|
125
|
-
- - "
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sidekiq
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
126
144
|
- !ruby/object:Gem::Version
|
127
|
-
version: 3
|
128
|
-
|
145
|
+
version: '3'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
129
151
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
152
|
+
version: '3'
|
131
153
|
description: Workflow extension that allows to fork workflows with other workflows
|
132
154
|
and join them at specific states.
|
133
155
|
email:
|
@@ -152,6 +174,8 @@ files:
|
|
152
174
|
- lib/workflow/join/active_record.rb
|
153
175
|
- lib/workflow/join/active_record/pending_callbacks.rb
|
154
176
|
- lib/workflow/join/active_record/pending_transitions.rb
|
177
|
+
- lib/workflow/join/sidekiq.rb
|
178
|
+
- lib/workflow/join/sidekiq/job.rb
|
155
179
|
- lib/workflow/join/simple.rb
|
156
180
|
- lib/workflow/join/simple/pending_callbacks.rb
|
157
181
|
- lib/workflow/join/simple/pending_transitions.rb
|