workflow-join 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f609a97329dc400e969449c824e52ad91413eee
4
- data.tar.gz: f441e161a922f387b0b5ee908873c6b8b1db265e
3
+ metadata.gz: 8c3c2da76cc4df53310b7bf849f82b376191ea55
4
+ data.tar.gz: 1c180e494c16da06665ceda094062e1da1825578
5
5
  SHA512:
6
- metadata.gz: 7b7762a647e1133c54eb90734c5a8af1e3e18522d1b486f316d25c9d753a27a3b0a0a65f83afe6a50fad229395de36900ffbbfb4960ef6681ec3986786a630a9
7
- data.tar.gz: 936d6597d74068ba6f79339b1098be00bcead2854fb1f08f4c138bfb57b192c8a198e31271230d983cde2e54e63f2f6d1523df99a783dd2df106b4e3d48ba7dc
6
+ metadata.gz: 8a3b94ed6356eca07083c260186f28ff580ee5b535ba53148b2baeb03f232710c3b45dd641d0371576d055f35db0b3110d80da1a44bab6e67f2712d566dbfce0
7
+ data.tar.gz: bbcf979a422528c2bade2bdbb26abf103c03d8866a7ec3f84528eb122035f386aa4c9906c85b1d564027a5617b2d7df69f304955c3e0e53d971ebfa708e657c9
@@ -7,6 +7,9 @@ inherit_from: .rubocop_todo.yml
7
7
  Style/SignalException:
8
8
  EnforcedStyle: semantic
9
9
 
10
+ Style/Alias:
11
+ EnforcedStyle: prefer_alias_method
12
+
10
13
  Metrics/ClassLength:
11
14
  Max: 200
12
15
 
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2016-09-16 13:15:05 +0200 using RuboCop version 0.42.0.
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: 4
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
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in workflow-join.gemspec
4
4
  gemspec
5
+
6
+ gem 'rspec-sidekiq', '~> 1', group: :test
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.
@@ -1,19 +1,20 @@
1
1
  require 'workflow'
2
2
  require 'workflow/join/version'
3
3
 
4
- 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
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 = 'Either guard instance variable name or a code block is required'.freeze
85
- GUARD_POINTCUT_ERROR = 'Both :inner and :outer states are required'.freeze
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 !getter.nil? ^ block_given?
94
- fail Workflow::WorkflowDefinitionError, GUARD_POINTCUT_ERROR unless inner && outer
95
-
96
- guard = if block_given?
97
- Proc.new
98
- else
99
- g = getter.to_sym
100
- lambda do |host|
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,3 @@
1
+ require 'sidekiq'
2
+
3
+ require 'workflow/join/sidekiq/job'
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Workflow
2
2
  module Join
3
- VERSION = "0.2.1"
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
@@ -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.10'
28
- spec.add_development_dependency 'rake', '~> 10.0'
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.2'
35
- spec.add_dependency 'activerecord', '>= 3.2.1', '< 5.0.0'
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.2.1
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-09-16 00:00:00.000000000 Z
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.10'
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.10'
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.0'
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.0'
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.2'
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.2'
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: 5.0.0
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.2.1
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: 5.0.0
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