workflow-join 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09d63e2d8e1cc948a6cd156f826c75b8e3c0ac88
4
- data.tar.gz: c6f9ef0cbfb2f147118ced9916ab77460f8bb127
3
+ metadata.gz: 40f7e69d9114f772e7a3057bdbbfedc974dd786d
4
+ data.tar.gz: 779672bcd9063eb69d6b5400ebe7318957df3669
5
5
  SHA512:
6
- metadata.gz: 462f516b6ea0a7a980b11338668d358a46890ab157c08c1b1f7b46969a8c030ca6e94e7917ceb044ec4aa76b1b204363c8490a44fc5d73b72679e8d20228a901
7
- data.tar.gz: 80b1130fd8a22d0a1baa6bb8a3574201d95bffd96b428596b7c226fb9ca65482d0a0a1c4f90330caeb7f859255be4dc67ef6fe962528ccb777891d394ddcf80e
6
+ metadata.gz: 684b92e932e55d231c37c57f49a43715b7369ad2fae41e8fca5ce1e56d64f3820b5241af20c845fc1423cc1361f428c2644b4fef82277c90edc2859b33b8fb86
7
+ data.tar.gz: 9c60b3ff584e173d0a91d9e2a9a5e7dbca12b1e707f4105081fe9882276f93de6c2f2b3944d63e99f0f952a9ec9df772043c77f196c6ab8041967e9442e43a98
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2016-09-15 16:00:14 +0200 using RuboCop version 0.42.0.
3
+ # on 2016-09-16 13:15:05 +0200 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
@@ -16,12 +16,7 @@ Lint/BlockAlignment:
16
16
 
17
17
  # Offense count: 1
18
18
  Metrics/AbcSize:
19
- Max: 49
20
-
21
- # Offense count: 1
22
- # Configuration parameters: CountComments.
23
- Metrics/MethodLength:
24
- Max: 46
19
+ Max: 57
25
20
 
26
21
  # Offense count: 1
27
22
  # Cop supports --auto-correct.
@@ -37,18 +32,20 @@ Style/MutableConstant:
37
32
  Exclude:
38
33
  - 'lib/workflow/join/version.rb'
39
34
 
40
- # Offense count: 1
35
+ # Offense count: 2
41
36
  # Cop supports --auto-correct.
42
37
  # Configuration parameters: PreferredDelimiters.
43
38
  Style/PercentLiteralDelimiters:
44
39
  Exclude:
40
+ - 'spec/workflow/active_record_spec.rb'
45
41
  - 'spec/workflow/join_spec.rb'
46
42
 
47
- # Offense count: 1
43
+ # Offense count: 2
48
44
  # Cop supports --auto-correct.
49
45
  Style/RescueModifier:
50
46
  Exclude:
51
47
  - 'lib/workflow/join.rb'
48
+ - 'lib/workflow/join/active_record/pending_callbacks.rb'
52
49
 
53
50
  # Offense count: 4
54
51
  # Cop supports --auto-correct.
data/Rakefile CHANGED
@@ -15,10 +15,17 @@ rescue Bundler::BundlerError => e
15
15
  exit e.status_code
16
16
  end
17
17
 
18
- desc 'Tests'
18
+ desc 'Tests w/ActiveRecord'
19
19
  RSpec::Core::RakeTask.new(:spec) do |spec|
20
20
  spec.rspec_opts = '-Ispec'
21
21
  # spec.rcov = true
22
22
  end
23
23
 
24
- task default: [:spec]
24
+ desc 'Tests wo/ActiveRecord'
25
+ RSpec::Core::RakeTask.new(:spec_simple) do |spec|
26
+ ENV['USE_SIMPLE_PERSISTENCE'] = 'true'
27
+ spec.rspec_opts = '-Ispec'
28
+ # spec.rcov = true
29
+ end
30
+
31
+ task default: [:spec, :spec_simple]
@@ -1,57 +1,79 @@
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
16
+
4
17
  Workflow::ClassMethods.prepend(Module.new do
5
18
  def workflow(&specification)
6
19
  # extend instances
7
20
  prepend(Module.new do # this should be safe, since there could not be two subsequent workflow DSL
8
- attr_reader :pending_transitions # FIXME PERSIST!!!
21
+ if Workflow::Join.const_defined?('ActiveRecord')
22
+ include Workflow::Join::ActiveRecord # AR pending transitions and callbacks implementation
23
+ else
24
+ include Workflow::Join::Simple # simple pending transitions and callbacks implementation
25
+ end
26
+
9
27
  def guards!
28
+ spec.instance_variable_set(:@original_before_transition_proc, spec.before_transition_proc) \
29
+ unless spec.instance_variable_defined?(:@original_before_transition_proc)
30
+
10
31
  λλs = spec.guards.map do |inner, outers|
11
32
  outers.map do |getter, state|
12
33
  guard! inner, getter, state
13
- end
34
+ end.compact
14
35
  end.flatten
15
- λλs << spec.before_transition_proc if spec.before_transition_proc
36
+
37
+ (original_before_transition_proc = spec.instance_variable_get(:@original_before_transition_proc)) && \
38
+ λλs << original_before_transition_proc
16
39
  spec.before_transition_proc = ->(from, to, name, *args) { λλs.each { |λ| λ.(from, to, name, *args) } }
17
40
  end
18
41
 
19
42
  def guard!(inner, getter, state)
20
- slave = getter.call(self)
21
- slave.instance_variable_set(:@☛, (slave.instance_variable_get(:@☛) || []) | [self])
22
- slave_hook = "on_#{state}_entry".to_sym
23
- slave.class.prepend(Module.new do
24
- define_method slave_hook do |old_state, event, *args|
25
- @☛.each do |master|
26
- next unless master.pending_transitions.is_a?(Array)
27
- master.pending_transitions.reject! do |transition|
28
- begin
29
- master.send("#{transition}!".to_sym)
30
- true
31
- rescue
32
- false # no transition no cry
33
- end
34
- end
35
- end
36
- super(old_state, event, *args) rescue nil # no super no cry
43
+ return if getter.nil? || (slave = getter.call(self)).nil? # I’ll be back, hasta la vista, baby :)
44
+
45
+ slave.class.send :define_method, "on_#{state}_entry".to_sym do |old_state, event, *args|
46
+ pending_callbacks.each do |master|
47
+ master.reload if master.respond_to?(:reload)
48
+ next unless master.pending_transitions?
49
+ master.try_pending_transitions!
50
+ # halted is automagically removed after successful transition
51
+ # the line below is unneeded
52
+ # master.halted = master.pending_transitions?
37
53
  end
38
- end)
54
+ super(old_state, event, *args) rescue nil # no super no cry
55
+ end
56
+
39
57
  lambda do |_, to, name, *|
58
+ slave.reload if slave.respond_to?(:reload)
40
59
  if to.to_sym == inner && !slave.send("#{state}?".to_sym)
41
- @pending_transitions ||= []
42
- @pending_transitions |= [name]
60
+ pending_transition! name
61
+ slave.pending_callback!(self)
43
62
  halt("Waiting for guard workflow to enter “:#{state}” state")
44
63
  end
45
64
  end
46
65
  end
47
66
  end)
48
67
 
49
- # extend singleton class to update guards in constructor
50
- singleton_class.prepend(Module.new do
51
- def new(*)
52
- super.tap(&:guards!)
53
- end
54
- end)
68
+ if respond_to?(:after_commit)
69
+ after_commit { guards! }
70
+ else
71
+ singleton_class.prepend(Module.new do
72
+ def new(*)
73
+ super.tap(&:guards!)
74
+ end
75
+ end)
76
+ end
55
77
 
56
78
  super
57
79
  end
@@ -61,7 +83,7 @@ module Workflow
61
83
  module Join
62
84
  GUARD_PARAMS_ERROR = 'Either guard instance variable name or a code block is required'.freeze
63
85
  GUARD_POINTCUT_ERROR = 'Both :inner and :outer states are required'.freeze
64
- GUARD_IS_NOT_WORKFLOW = 'Guard given must be a workflow instance'.freeze
86
+ GUARD_IS_NOT_WORKFLOW = 'Guard given must be a workflow instance, was: “%s”'.freeze
65
87
 
66
88
  def guards
67
89
  @guards ||= {}
@@ -79,10 +101,11 @@ module Workflow
79
101
  case
80
102
  when /\A@/ =~ g.to_s && host.instance_variable_defined?(g)
81
103
  host.instance_variable_get(g)
82
- when host.methods.include?(g) && host.method(g).arity.zero?
104
+ when host.methods.include?(g) && host.method(g).arity <= 0
83
105
  host.send g
84
106
  end.tap do |guard_instance|
85
- fail Workflow::WorkflowDefinitionError, GUARD_IS_NOT_WORKFLOW unless guard_instance.is_a?(Workflow)
107
+ fail Workflow::WorkflowDefinitionError, GUARD_IS_NOT_WORKFLOW % guard_instance \
108
+ unless guard_instance.nil? || guard_instance.is_a?(Workflow)
86
109
  end
87
110
  end
88
111
  end
@@ -0,0 +1,43 @@
1
+ require 'workflow/join/active_record/pending_transitions'
2
+ require 'workflow/join/active_record/pending_callbacks'
3
+
4
+ module Workflow
5
+ module Join
6
+ module ActiveRecord
7
+ ENSURE_COLUMNS = lambda do |model, *columns|
8
+ columns.reduce(true) do |memo, column|
9
+ next memo if model.column_names.include?(column.to_s)
10
+
11
+ ::ActiveRecord::Base.connection.execute(
12
+ "ALTER TABLE #{model.table_name} ADD #{column} VARCHAR(255)"
13
+ )
14
+ false
15
+ end
16
+ end
17
+
18
+ include PendingTransitions
19
+ include PendingCallbacks
20
+
21
+ def self.included(base)
22
+ base.singleton_class.send :define_method, :prepended do |model|
23
+ fail LoadError, "This module might be included in ActiveRecord::Base instances only (#{base} given.)" \
24
+ unless model < ::ActiveRecord::Base
25
+
26
+ unless ENSURE_COLUMNS.call(model, :workflow_pending_transitions, :workflow_pending_callbacks)
27
+ fail LoadError, <<-MSG
28
+
29
+ =======================================================================================
30
+ This is an intended fail, next time the class is requested, it’ll be loaded properly!
31
+ To avoid this one should explicitly specify columns:
32
+ — workflow_pending_transitions,
33
+ — workflow_pending_callbacks
34
+ in all models, that are willing to use joined workflows.
35
+ =======================================================================================
36
+
37
+ MSG
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ module Workflow
2
+ module Join
3
+ module ActiveRecord
4
+ # table.column :workflow_pending_callbacks, :string
5
+ module PendingCallbacks
6
+ def pending_callbacks
7
+ workflow_pending_callbacks.to_s.split(';').map do |wpc|
8
+ c, id = wpc.split(',')
9
+ Kernel.const_get(c).find(id) rescue nil
10
+ end.compact
11
+ end
12
+
13
+ def pending_callbacks!(value)
14
+ pcs = case value
15
+ when Array then value.map { |instance| [instance.class.name, instance.id].join(',') }.join(';')
16
+ when String, Symbol then value.to_s
17
+ end
18
+ update_column :workflow_pending_callbacks, pcs
19
+ end
20
+
21
+ def pending_callbacks?
22
+ !pending_callbacks.empty?
23
+ end
24
+
25
+ def pending_callback!(value)
26
+ pending_callbacks!(pending_callbacks | [value])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ module Workflow
2
+ module Join
3
+ module ActiveRecord
4
+ # table.column :workflow_pending_transitions, :string
5
+ module PendingTransitions
6
+ def pending_transitions
7
+ workflow_pending_transitions.to_s.split(',').map(&:to_sym)
8
+ end
9
+
10
+ def pending_transitions!(value)
11
+ pts = case value
12
+ when Array then value.map(&:to_s).join(',')
13
+ when String, Symbol then value.to_s
14
+ end
15
+ update_column :workflow_pending_transitions, pts
16
+ end
17
+
18
+ def pending_transitions?
19
+ !pending_transitions.empty?
20
+ end
21
+
22
+ def pending_transition!(value)
23
+ pending_transitions!(pending_transitions | [value])
24
+ end
25
+
26
+ def try_pending_transitions!
27
+ pending_transitions!(pending_transitions.reject do |transition|
28
+ begin
29
+ respond_to?("can_#{transition}?") && \
30
+ public_send("can_#{transition}?") && \
31
+ public_send("#{transition}!".to_sym) && \
32
+ true
33
+ rescue
34
+ false # no transition no cry
35
+ end
36
+ end)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ require 'workflow/join/simple/pending_transitions'
2
+ require 'workflow/join/simple/pending_callbacks'
3
+
4
+ module Workflow
5
+ module Join
6
+ module Simple
7
+ include PendingTransitions
8
+ include PendingCallbacks
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module Workflow
2
+ module Join
3
+ module Simple
4
+ module PendingCallbacks
5
+ def pending_callbacks
6
+ @pending_callbacks ||= []
7
+ end
8
+
9
+ def pending_callbacks!(value)
10
+ @pending_callbacks = value
11
+ end
12
+
13
+ def pending_callbacks?
14
+ !pending_callbacks.empty?
15
+ end
16
+
17
+ def pending_callback!(value)
18
+ pending_callbacks!(pending_callbacks | [value])
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ module Workflow
2
+ module Join
3
+ module Simple
4
+ module PendingTransitions
5
+ def pending_transitions
6
+ @pending_transitions ||= []
7
+ end
8
+
9
+ def pending_transitions!(value)
10
+ @pending_transitions = value
11
+ end
12
+
13
+ def pending_transitions?
14
+ !pending_transitions.empty?
15
+ end
16
+
17
+ def pending_transition!(value)
18
+ pending_transitions!(pending_transitions | [value])
19
+ end
20
+
21
+ def try_pending_transitions!
22
+ pending_transitions.reject! do |transition|
23
+ begin
24
+ respond_to?("can_#{transition}?") && \
25
+ public_send("can_#{transition}?") && \
26
+ public_send("#{transition}!".to_sym) && \
27
+ true
28
+ rescue
29
+ false # no transition no cry
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  module Workflow
2
2
  module Join
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -29,6 +29,8 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'rspec'
30
30
  spec.add_development_dependency 'rubocop'
31
31
  spec.add_development_dependency 'pry'
32
+ spec.add_development_dependency 'sqlite3'
32
33
 
33
34
  spec.add_dependency 'workflow', '~> 1.2'
35
+ spec.add_dependency 'activerecord'
34
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workflow-join
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksei Matiushkin
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: workflow
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '1.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activerecord
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  description: Workflow extension that allows to fork workflows with other workflows
98
126
  and join them at specific states.
99
127
  email:
@@ -115,6 +143,12 @@ files:
115
143
  - bin/console
116
144
  - bin/setup
117
145
  - lib/workflow/join.rb
146
+ - lib/workflow/join/active_record.rb
147
+ - lib/workflow/join/active_record/pending_callbacks.rb
148
+ - lib/workflow/join/active_record/pending_transitions.rb
149
+ - lib/workflow/join/simple.rb
150
+ - lib/workflow/join/simple/pending_callbacks.rb
151
+ - lib/workflow/join/simple/pending_transitions.rb
118
152
  - lib/workflow/join/version.rb
119
153
  - workflow-join.gemspec
120
154
  homepage: https://github.com/am-kantox/workflow-join