workflow-join 0.1.1 → 0.2.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: 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