rails-workflow 1.4.5.4 → 1.4.6.4
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/.gitignore +2 -0
- data/.rubocop.yml +23 -0
- data/Gemfile +2 -1
- data/Rakefile +4 -4
- data/bin/console +3 -3
- data/lib/active_support/overloads.rb +13 -6
- data/lib/workflow.rb +12 -279
- data/lib/workflow/adapters/active_record.rb +57 -50
- data/lib/workflow/adapters/active_record_validations.rb +25 -19
- data/lib/workflow/adapters/adapter.rb +23 -0
- data/lib/workflow/adapters/remodel.rb +8 -9
- data/lib/workflow/callbacks.rb +60 -45
- data/lib/workflow/callbacks/callback.rb +23 -37
- data/lib/workflow/callbacks/method_callback.rb +12 -0
- data/lib/workflow/callbacks/proc_callback.rb +23 -0
- data/lib/workflow/callbacks/string_callback.rb +12 -0
- data/lib/workflow/callbacks/transition_callback.rb +88 -78
- data/lib/workflow/callbacks/transition_callbacks/method_caller.rb +53 -0
- data/lib/workflow/callbacks/transition_callbacks/proc_caller.rb +60 -0
- data/lib/workflow/configuration.rb +1 -0
- data/lib/workflow/definition.rb +73 -0
- data/lib/workflow/errors.rb +37 -6
- data/lib/workflow/event.rb +30 -15
- data/lib/workflow/helper_method_configurator.rb +100 -0
- data/lib/workflow/specification.rb +45 -22
- data/lib/workflow/state.rb +45 -36
- data/lib/workflow/transition_context.rb +5 -4
- data/lib/workflow/transitions.rb +94 -0
- data/lib/workflow/version.rb +2 -1
- data/rails-workflow.gemspec +18 -18
- data/tags.markdown +31 -0
- metadata +13 -5
- data/lib/workflow/callbacks/transition_callbacks/method_wrapper.rb +0 -102
- data/lib/workflow/callbacks/transition_callbacks/proc_wrapper.rb +0 -48
- data/lib/workflow/draw.rb +0 -79
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Workflow
|
2
3
|
module Callbacks
|
3
4
|
#
|
@@ -10,26 +11,35 @@ module Workflow
|
|
10
11
|
include Comparable
|
11
12
|
|
12
13
|
attr_reader :expression, :callback
|
13
|
-
def initialize(expression, inverted=false)
|
14
|
+
def initialize(expression, inverted = false)
|
14
15
|
@expression = expression
|
15
|
-
@callback
|
16
|
-
if inverted
|
17
|
-
@callback = invert_lambda(@callback)
|
18
|
-
end
|
16
|
+
@callback = make_lambda(expression)
|
17
|
+
@callback = invert_lambda(@callback) if inverted
|
19
18
|
end
|
20
19
|
|
21
20
|
def call(target)
|
22
|
-
callback.call(target, ->{})
|
21
|
+
callback.call(target, -> {})
|
23
22
|
end
|
24
23
|
|
25
|
-
def self.
|
26
|
-
|
24
|
+
def self.inverted(expression)
|
25
|
+
build(expression, true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.build(expression, inverted = false)
|
29
|
+
case expression
|
30
|
+
when Symbol
|
31
|
+
MethodCallback.new(expression, inverted)
|
32
|
+
when String
|
33
|
+
StringCallback.new(expression, inverted)
|
34
|
+
when Proc
|
35
|
+
ProcCallback.new(expression, inverted)
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
39
|
private
|
30
40
|
|
31
41
|
def invert_lambda(l)
|
32
|
-
|
42
|
+
->(*args, &blk) { !l.call(*args, &blk) }
|
33
43
|
end
|
34
44
|
|
35
45
|
# Filters support:
|
@@ -42,34 +52,6 @@ module Workflow
|
|
42
52
|
# All of these objects are converted into a lambda and handled
|
43
53
|
# the same after this point.
|
44
54
|
def make_lambda(filter)
|
45
|
-
case filter
|
46
|
-
when Symbol
|
47
|
-
lambda { |target, _, &blk| target.send filter, &blk }
|
48
|
-
when String
|
49
|
-
l = eval "lambda { |value| #{filter} }"
|
50
|
-
lambda { |target, value| target.instance_exec(value, &l) }
|
51
|
-
# when Conditionals::Value then filter
|
52
|
-
when ::Proc
|
53
|
-
if filter.arity > 1
|
54
|
-
return lambda { |target, _, &block|
|
55
|
-
raise ArgumentError unless block
|
56
|
-
target.instance_exec(target, block, &filter)
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
if filter.arity <= 0
|
61
|
-
lambda { |target, _| target.instance_exec(&filter) }
|
62
|
-
else
|
63
|
-
lambda { |target, _| target.instance_exec(target, &filter) }
|
64
|
-
end
|
65
|
-
else
|
66
|
-
scopes = Array(chain_config[:scope])
|
67
|
-
method_to_call = scopes.map{ |s| public_send(s) }.join("_")
|
68
|
-
|
69
|
-
lambda { |target, _, &blk|
|
70
|
-
filter.public_send method_to_call, target, &blk
|
71
|
-
}
|
72
|
-
end
|
73
55
|
end
|
74
56
|
|
75
57
|
def compute_identifier(filter)
|
@@ -83,3 +65,7 @@ module Workflow
|
|
83
65
|
end
|
84
66
|
end
|
85
67
|
end
|
68
|
+
|
69
|
+
require 'workflow/callbacks/proc_callback'
|
70
|
+
require 'workflow/callbacks/string_callback'
|
71
|
+
require 'workflow/callbacks/method_callback'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Workflow
|
3
|
+
module Callbacks
|
4
|
+
class ProcCallback < Callback
|
5
|
+
private
|
6
|
+
|
7
|
+
def make_lambda(filter)
|
8
|
+
if filter.arity > 1
|
9
|
+
return lambda do |target, _, &block|
|
10
|
+
raise ArgumentError unless block
|
11
|
+
target.instance_exec(target, block, &filter)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if filter.arity <= 0
|
16
|
+
->(target, _) { target.instance_exec(&filter) }
|
17
|
+
else
|
18
|
+
->(target, _) { target.instance_exec(target, &filter) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,6 +1,14 @@
|
|
1
1
|
|
2
|
+
# frozen_string_literal: true
|
2
3
|
module Workflow
|
3
4
|
module Callbacks
|
5
|
+
# @api private
|
6
|
+
# Wrapper object for proc and method callbacks, when that proc or method
|
7
|
+
# is intended to receive arguments that were passed to the transition.
|
8
|
+
# When the method/proc is to be called, a {MethodArgumentBuilder} determines
|
9
|
+
# and generates the array of arguments to send to the method.
|
10
|
+
# @see Workflow::Callbacks::TransitionCallbacks::MethodCaller
|
11
|
+
# @see Workflow::Callbacks::TransitionCallbacks::ProcCaller
|
4
12
|
class TransitionCallback
|
5
13
|
attr_reader :callback_type, :raw_proc, :calling_class
|
6
14
|
def initialize(callback_type, raw_proc, calling_class)
|
@@ -9,108 +17,110 @@ module Workflow
|
|
9
17
|
@calling_class = calling_class
|
10
18
|
end
|
11
19
|
|
12
|
-
def
|
13
|
-
if
|
14
|
-
|
15
|
-
elsif raw_proc.kind_of? ::Symbol
|
16
|
-
if zero_arity_method?(raw_proc, calling_class)
|
17
|
-
raw_proc
|
18
|
-
else
|
19
|
-
TransitionCallbacks::MethodWrapper.new(callback_type, raw_proc, calling_class)
|
20
|
-
end
|
20
|
+
def call(target, _value, &block)
|
21
|
+
if around_callback?
|
22
|
+
around_proc(target, block)
|
21
23
|
else
|
22
|
-
|
24
|
+
normal_proc(target)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def self.zero_arity_method?(method, calling_class)
|
36
|
-
if calling_class.instance_methods.include?(method)
|
37
|
-
method = calling_class.instance_method(method)
|
38
|
-
method.arity == 0
|
28
|
+
class << self
|
29
|
+
def build(callback_type, raw_proc, calling_class)
|
30
|
+
case raw_proc
|
31
|
+
when ::Proc
|
32
|
+
TransitionCallbacks::ProcCaller.build(callback_type, raw_proc, calling_class)
|
33
|
+
when ::Symbol
|
34
|
+
TransitionCallbacks::MethodCaller.build(callback_type, raw_proc, calling_class)
|
35
|
+
else raw_proc
|
36
|
+
end
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
42
|
-
|
43
|
-
<<-EOF
|
44
|
-
Proc.new do |target#{', callbacks' if around_callback?}|
|
45
|
-
from, to, event, event_args, attributes = transition_context.values
|
46
|
-
name_proc = Proc.new {|name|
|
47
|
-
case name
|
48
|
-
when :to then to
|
49
|
-
when :from then from
|
50
|
-
when :event then event
|
51
|
-
else (attributes.delete(name) || event_args.shift)
|
52
|
-
end
|
53
|
-
}
|
54
|
-
#{proc_body}
|
55
|
-
end
|
56
|
-
EOF
|
57
|
-
end
|
40
|
+
protected
|
58
41
|
|
59
42
|
def around_callback?
|
60
43
|
callback_type == :around
|
61
44
|
end
|
62
45
|
|
63
|
-
|
64
|
-
|
65
|
-
|
46
|
+
# Builds arguments appropriate for calling the wrapped proc or method.
|
47
|
+
class MethodArgumentBuilder
|
48
|
+
attr_reader :transition_context, :method, :event, :from, :to, :attributes, :event_args
|
49
|
+
attr_reader :args
|
50
|
+
def initialize(transition_context, method)
|
51
|
+
@transition_context = transition_context
|
52
|
+
@method = method
|
53
|
+
@from, @to, @event, @event_args, @attributes = transition_context.values
|
54
|
+
build_args
|
55
|
+
end
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
|
57
|
+
def build_args
|
58
|
+
@args = name_arguments + rest_arguments
|
59
|
+
kwargs = keyword_arguments.merge(keyrest_arguments)
|
60
|
+
@args << kwargs unless kwargs.empty?
|
70
61
|
end
|
71
|
-
params.join(', ') if params.any?
|
72
|
-
end
|
73
62
|
|
74
|
-
|
75
|
-
|
76
|
-
|
63
|
+
def name_arguments
|
64
|
+
name_params.map do |name|
|
65
|
+
case name
|
66
|
+
when :to then to
|
67
|
+
when :from then from
|
68
|
+
when :event then event
|
69
|
+
else (attributes.delete(name) || event_args.shift)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
77
73
|
|
78
|
-
|
79
|
-
|
80
|
-
|
74
|
+
def rest_arguments
|
75
|
+
if rest_param
|
76
|
+
event_args
|
77
|
+
else
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
def keyword_arguments
|
83
|
+
kw_params.map do |name|
|
84
|
+
[name, attributes.delete(name)]
|
85
|
+
end.to_h
|
86
|
+
end
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
88
|
+
def keyrest_arguments
|
89
|
+
if keyrest_param
|
90
|
+
attributes
|
91
|
+
else
|
92
|
+
{}
|
93
|
+
end
|
94
|
+
end
|
89
95
|
|
90
|
-
|
91
|
-
|
92
|
-
|
96
|
+
def name_params
|
97
|
+
params_by_type :opt, :req
|
98
|
+
end
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
100
|
+
def kw_params
|
101
|
+
params_by_type :keyreq, :keyopt
|
102
|
+
end
|
97
103
|
|
98
|
-
|
99
|
-
|
100
|
-
|
104
|
+
def keyrest_param
|
105
|
+
params_by_type(:keyrest).first
|
106
|
+
end
|
101
107
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end.map(&:last)
|
106
|
-
end
|
108
|
+
def rest_param
|
109
|
+
params_by_type(:rest).first
|
110
|
+
end
|
107
111
|
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
def params_by_type(*types)
|
113
|
+
parameters.select do |type, _name|
|
114
|
+
types.include? type
|
115
|
+
end.map(&:last)
|
116
|
+
end
|
111
117
|
|
112
|
-
|
113
|
-
|
118
|
+
# Parameter definition for the object. See UnboundMethod#parameters
|
119
|
+
#
|
120
|
+
# @return [Array] Parameters
|
121
|
+
def parameters
|
122
|
+
method.parameters
|
123
|
+
end
|
114
124
|
end
|
115
125
|
end
|
116
126
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Workflow
|
3
|
+
module Callbacks
|
4
|
+
module TransitionCallbacks
|
5
|
+
# @api private
|
6
|
+
# A {Workflow::Callbacks::TransitionCallback} that calls an instance method
|
7
|
+
# With arity != 0.
|
8
|
+
class MethodCaller < ::Workflow::Callbacks::TransitionCallback
|
9
|
+
attr_reader :calling_class
|
10
|
+
|
11
|
+
def normal_proc(target)
|
12
|
+
transition_context = target.send :transition_context
|
13
|
+
method = target.method(raw_proc)
|
14
|
+
builder = MethodArgumentBuilder.new(transition_context, method)
|
15
|
+
target.instance_exec(*builder.args, &method)
|
16
|
+
end
|
17
|
+
|
18
|
+
def around_proc(target, callbacks)
|
19
|
+
transition_context = target.send :transition_context
|
20
|
+
method = target.method(raw_proc)
|
21
|
+
builder = MethodArgumentBuilder.new(transition_context, method)
|
22
|
+
target.send raw_proc, *builder.args, &callbacks
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def build(callback_type, raw_proc, calling_class)
|
27
|
+
return raw_proc if zero_arity_method?(raw_proc, calling_class)
|
28
|
+
new(callback_type, raw_proc, calling_class)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Returns true if the method has arity zero.
|
34
|
+
# Returns false if the method is defined and has non-zero arity.
|
35
|
+
# Returns nil if the method has not been defined.
|
36
|
+
def zero_arity_method?(method, calling_class)
|
37
|
+
return false unless calling_class.instance_methods.include?(method)
|
38
|
+
method = calling_class.instance_method(method)
|
39
|
+
method.arity.zero?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [UnboundMethod] Method representation from class
|
46
|
+
# {#calling_class}, named by {#raw_proc}
|
47
|
+
def callback_method
|
48
|
+
@meth ||= calling_class.instance_method(raw_proc)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
module Workflow
|
4
|
+
module Callbacks
|
5
|
+
module TransitionCallbacks
|
6
|
+
# @api private
|
7
|
+
# A {Workflow::Callbacks::TransitionCallback} that wraps a callback proc.
|
8
|
+
class ProcCaller < ::Workflow::Callbacks::TransitionCallback
|
9
|
+
def normal_proc(target)
|
10
|
+
transition_context = target.send :transition_context
|
11
|
+
builder = NormalProcArgBuilder.new(transition_context, raw_proc)
|
12
|
+
target.instance_exec target, *builder.args, &raw_proc
|
13
|
+
end
|
14
|
+
|
15
|
+
def around_proc(target, callbacks)
|
16
|
+
transition_context = target.send :transition_context
|
17
|
+
builder = AroundProcArgBuilder.new(transition_context, raw_proc)
|
18
|
+
target.instance_exec target, callbacks, *builder.args, &raw_proc
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def build(callback_type, raw_proc, calling_class)
|
23
|
+
return raw_proc if basic_callback?(raw_proc, callback_type)
|
24
|
+
new(callback_type, raw_proc, calling_class)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Returns true if the method has arity zero.
|
30
|
+
# Returns false if the method is defined and has non-zero arity.
|
31
|
+
# Returns nil if the method has not been defined.
|
32
|
+
def basic_callback?(method, callback_type)
|
33
|
+
case method.arity
|
34
|
+
when 0 then true
|
35
|
+
when 1 then [:req, :opt].include?(method.parameters[0][0])
|
36
|
+
when 2 then callback_type == :around
|
37
|
+
else false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NormalProcArgBuilder < MethodArgumentBuilder
|
43
|
+
private
|
44
|
+
|
45
|
+
def name_params
|
46
|
+
super[1..-1] || []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class AroundProcArgBuilder < MethodArgumentBuilder
|
51
|
+
private
|
52
|
+
|
53
|
+
def name_params
|
54
|
+
super[2..-1] || []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|