rails-workflow 1.4.4.4 → 1.4.5.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 +1 -1
- data/.yardopts +1 -1
- data/README.markdown +4 -664
- data/lib/active_support/overloads.rb +32 -0
- data/lib/workflow.rb +31 -37
- data/lib/workflow/callbacks.rb +5 -5
- data/lib/workflow/callbacks/{transition_callback_wrapper.rb → transition_callback.rb} +23 -23
- data/lib/workflow/callbacks/transition_callbacks/method_wrapper.rb +102 -0
- data/lib/workflow/callbacks/transition_callbacks/proc_wrapper.rb +48 -0
- data/lib/workflow/event.rb +55 -12
- data/lib/workflow/specification.rb +6 -6
- data/lib/workflow/state.rb +38 -10
- data/lib/workflow/version.rb +1 -1
- data/rails-workflow.gemspec +1 -1
- metadata +7 -5
- data/lib/workflow/callbacks/transition_callback_method_wrapper.rb +0 -65
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/callbacks'
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# Overloads for {ActiveSupport::Callbacks::Callback} so it can recognize
|
5
|
+
# {Workflow::Callbacks::TransitionCallback}, which is duck-type equivalent
|
6
|
+
# to a lambda for the present purposes.
|
7
|
+
module CallbackOverloads
|
8
|
+
private
|
9
|
+
def make_lambda(filter)
|
10
|
+
if filter.kind_of? Workflow::Callbacks::TransitionCallback
|
11
|
+
super(filter.wrapper)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def compute_identifier(filter)
|
18
|
+
if filter.kind_of? Workflow::Callbacks::TransitionCallback
|
19
|
+
super(filter.raw_proc)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Overload {ActiveSupport::Callbacks::Callback} with methods from {ActiveSupport::CallbackOverloads}.
|
28
|
+
# {Workflow::Callbacks::TransitionCallback}, which is duck-type equivalent
|
29
|
+
# to a lambda for the present purposes.
|
30
|
+
class ::ActiveSupport::Callbacks::Callback
|
31
|
+
prepend ActiveSupport::CallbackOverloads
|
32
|
+
end
|
data/lib/workflow.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'active_support/concern'
|
3
|
+
require 'active_support/callbacks'
|
3
4
|
require 'workflow/version'
|
4
5
|
require 'workflow/configuration'
|
5
6
|
require 'workflow/specification'
|
@@ -8,6 +9,8 @@ require 'workflow/adapters/active_record'
|
|
8
9
|
require 'workflow/adapters/remodel'
|
9
10
|
require 'workflow/adapters/active_record_validations'
|
10
11
|
require 'workflow/transition_context'
|
12
|
+
require 'active_support/overloads'
|
13
|
+
|
11
14
|
|
12
15
|
# See also README.markdown for documentation
|
13
16
|
module Workflow
|
@@ -18,35 +21,16 @@ module Workflow
|
|
18
21
|
include Callbacks
|
19
22
|
include Errors
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
def make_lambda(filter)
|
24
|
-
if filter.kind_of? Workflow::Callbacks::TransitionCallbackWrapper
|
25
|
-
super(filter.wrapper)
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def compute_identifier(filter)
|
32
|
-
if filter.kind_of? Workflow::Callbacks::TransitionCallbackWrapper
|
33
|
-
super(filter.raw_proc)
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class ::ActiveSupport::Callbacks::Callback
|
41
|
-
prepend ComputeIdentifier
|
42
|
-
end
|
24
|
+
# The application-wide Workflow configuration object
|
25
|
+
CONFIGURATION = Configuration.new
|
43
26
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def self.config
|
49
|
-
|
27
|
+
# Helper method for setting configuration options on {Workflow.config}
|
28
|
+
#
|
29
|
+
# @yield [Workflow::Configuration] config {Configuration} object to be manipulated.
|
30
|
+
# @return [nil]
|
31
|
+
def self.config(&block)
|
32
|
+
block.call(CONFIGURATION) if block_given?
|
33
|
+
return CONFIGURATION
|
50
34
|
end
|
51
35
|
|
52
36
|
included do
|
@@ -75,7 +59,7 @@ module Workflow
|
|
75
59
|
end
|
76
60
|
|
77
61
|
# Deprecated. Check for false return value from {#transition!}
|
78
|
-
# @return true if the last transition was halted by one of the transition callbacks.
|
62
|
+
# @return [Boolean] true if the last transition was halted by one of the transition callbacks.
|
79
63
|
def halted?
|
80
64
|
@halted
|
81
65
|
end
|
@@ -84,11 +68,21 @@ module Workflow
|
|
84
68
|
# @return [String] The reason the transition was aborted.
|
85
69
|
attr_reader :halted_because
|
86
70
|
|
71
|
+
# @api private
|
72
|
+
# @return [Workflow::TransitionContext] During transition, or nil if no transition is underway.
|
73
|
+
# During a state transition, contains transition-specific information:
|
74
|
+
# * The name of the {Workflow::State} being exited,
|
75
|
+
# * The name of the {Workflow::State} being entered,
|
76
|
+
# * The name of the {Workflow::Event} that was fired,
|
77
|
+
# * And whatever arguments were passed to the {Workflow#transition!} method.
|
78
|
+
attr_reader :transition_context
|
79
|
+
|
87
80
|
# Initiates state transition via the named event
|
88
81
|
#
|
89
82
|
# @param [Symbol] name name of event to initiate
|
90
|
-
# @param [
|
91
|
-
# @return [
|
83
|
+
# @param [Array] args State transition arguments.
|
84
|
+
# @return [Symbol] The name of the new state, or `false` if the transition failed.
|
85
|
+
# TODO: connect args to documentation on how arguments are accessed during state transitions.
|
92
86
|
def transition!(name, *args, **attributes)
|
93
87
|
name = name.to_sym
|
94
88
|
event = current_state.find_event(name)
|
@@ -128,8 +122,8 @@ module Workflow
|
|
128
122
|
|
129
123
|
# Stop the current transition and set the reason for the abort.
|
130
124
|
#
|
131
|
-
# @param
|
132
|
-
# @return [
|
125
|
+
# @param [String] reason Optional reason for halting transition.
|
126
|
+
# @return [nil]
|
133
127
|
def halt(reason = nil)
|
134
128
|
@halted_because = reason
|
135
129
|
@halted = true
|
@@ -138,8 +132,8 @@ module Workflow
|
|
138
132
|
|
139
133
|
# Sets halt reason and raises [TransitionHaltedError] error.
|
140
134
|
#
|
141
|
-
# @param
|
142
|
-
# @return [
|
135
|
+
# @param [String] reason Optional reason for halting
|
136
|
+
# @return [nil]
|
143
137
|
def halt!(reason = nil)
|
144
138
|
@halted_because = reason
|
145
139
|
@halted = true
|
@@ -214,8 +208,8 @@ module Workflow
|
|
214
208
|
|
215
209
|
# Instructs Workflow which column to use to persist workflow state.
|
216
210
|
#
|
217
|
-
# @param
|
218
|
-
# @return [
|
211
|
+
# @param [Symbol] column_name If provided, will set a new workflow column name.
|
212
|
+
# @return [Symbol] The current (or new) name for the workflow column on this class.
|
219
213
|
def workflow_column(column_name=nil)
|
220
214
|
if column_name
|
221
215
|
@workflow_state_column_name = column_name.to_sym
|
data/lib/workflow/callbacks.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'workflow/callbacks/callback'
|
2
|
-
require 'workflow/callbacks/
|
3
|
-
require 'workflow/callbacks/
|
2
|
+
require 'workflow/callbacks/transition_callback'
|
3
|
+
require 'workflow/callbacks/transition_callbacks/method_wrapper'
|
4
|
+
require 'workflow/callbacks/transition_callbacks/proc_wrapper'
|
4
5
|
|
5
6
|
module Workflow
|
6
7
|
module Callbacks
|
@@ -19,7 +20,6 @@ module Workflow
|
|
19
20
|
# @return [TransitionContext] representation of current state transition
|
20
21
|
#
|
21
22
|
included do
|
22
|
-
attr_reader :transition_context
|
23
23
|
CALLBACK_MAP.keys.each do |type|
|
24
24
|
define_callbacks type,
|
25
25
|
skip_after_callbacks_if_terminated: true
|
@@ -247,13 +247,13 @@ module Workflow
|
|
247
247
|
CALLBACK_MAP.each do |type, context_attribute|
|
248
248
|
define_method "#{callback}_#{type}" do |*names, &blk|
|
249
249
|
_insert_callbacks(names, context_attribute, blk) do |name, options|
|
250
|
-
set_callback(type, callback,
|
250
|
+
set_callback(type, callback, Callbacks::TransitionCallback.build_wrapper(callback, name, self), options)
|
251
251
|
end
|
252
252
|
end
|
253
253
|
|
254
254
|
define_method "prepend_#{callback}_#{type}" do |*names, &blk|
|
255
255
|
_insert_callbacks(names, context_attribute, blk) do |name, options|
|
256
|
-
set_callback(type, callback,
|
256
|
+
set_callback(type, callback, Callbacks::TransitionCallback.build_wrapper(callback, name, self), options.merge(prepend: true))
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
|
2
2
|
module Workflow
|
3
3
|
module Callbacks
|
4
|
-
class
|
5
|
-
attr_reader :callback_type, :raw_proc
|
4
|
+
class TransitionCallback
|
5
|
+
attr_reader :callback_type, :raw_proc, :calling_class
|
6
6
|
def initialize(callback_type, raw_proc, calling_class)
|
7
7
|
@callback_type = callback_type
|
8
8
|
@raw_proc = raw_proc
|
@@ -11,29 +11,34 @@ module Workflow
|
|
11
11
|
|
12
12
|
def self.build_wrapper(callback_type, raw_proc, calling_class)
|
13
13
|
if raw_proc.kind_of? ::Proc
|
14
|
-
new(callback_type, raw_proc, calling_class)
|
14
|
+
TransitionCallbacks::ProcWrapper.new(callback_type, raw_proc, calling_class)
|
15
15
|
elsif raw_proc.kind_of? ::Symbol
|
16
|
-
|
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
|
17
21
|
else
|
18
22
|
raw_proc
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
def wrapper
|
23
|
-
|
24
|
-
name_arguments_string,
|
25
|
-
rest_param_string,
|
26
|
-
kw_arguments_string,
|
27
|
-
keyrest_string,
|
28
|
-
procedure_string].compact.join(', ')
|
29
|
-
|
30
|
-
raw_proc = self.raw_proc
|
31
|
-
str = build_proc("target.instance_exec(#{arguments})")
|
32
|
-
eval(str)
|
27
|
+
raise NotImplementedError.new "Abstract Method Called"
|
33
28
|
end
|
34
29
|
|
35
30
|
protected
|
36
31
|
|
32
|
+
# Returns true if the method has arity zero.
|
33
|
+
# Returns false if the method is defined and has non-zero arity.
|
34
|
+
# Returns nil if the method has not been defined.
|
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
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
37
42
|
def build_proc(proc_body)
|
38
43
|
<<-EOF
|
39
44
|
Proc.new do |target#{', callbacks' if around_callback?}|
|
@@ -56,12 +61,7 @@ module Workflow
|
|
56
61
|
end
|
57
62
|
|
58
63
|
def name_arguments_string
|
59
|
-
|
60
|
-
names = []
|
61
|
-
names << 'target' if params.shift
|
62
|
-
(names << 'callbacks') && params.shift if around_callback?
|
63
|
-
names += params.map{|name| "name_proc.call(:#{name})"}
|
64
|
-
return names.join(', ') if names.any?
|
64
|
+
raise NotImplementedError.new "Abstract Method Called"
|
65
65
|
end
|
66
66
|
|
67
67
|
def kw_arguments_string
|
@@ -80,7 +80,7 @@ module Workflow
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def procedure_string
|
83
|
-
|
83
|
+
raise NotImplementedError.new "Abstract Method Called"
|
84
84
|
end
|
85
85
|
|
86
86
|
def name_params
|
@@ -106,11 +106,11 @@ module Workflow
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def parameters
|
109
|
-
|
109
|
+
raise NotImplementedError.new "Abstract Method Called"
|
110
110
|
end
|
111
111
|
|
112
112
|
def arity
|
113
|
-
|
113
|
+
raise NotImplementedError.new "Abstract Method Called"
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Workflow
|
2
|
+
module Callbacks
|
3
|
+
module TransitionCallbacks
|
4
|
+
# A {Workflow::Callbacks::TransitionCallback} that wraps an instance method
|
5
|
+
# With arity != 0.
|
6
|
+
# Because the wrapped method may not have been defined at the time the callback
|
7
|
+
# is defined, the string representing the method call is built at runtime
|
8
|
+
# rather than at compile time.
|
9
|
+
class MethodWrapper < ::Workflow::Callbacks::TransitionCallback
|
10
|
+
attr_reader :calling_class
|
11
|
+
|
12
|
+
# Builds a proc object that will correctly call the {#raw_proc}
|
13
|
+
# by inspecting its parameters and pulling arguments from the {Workflow::TransitionContext}
|
14
|
+
# object for the transition.
|
15
|
+
# Given an overloaded `==` operator so for {Workflow#skip_before_transition} and other
|
16
|
+
# `skip_transition` calls.
|
17
|
+
# @return [Type] description of returned object
|
18
|
+
def wrapper
|
19
|
+
cb_object = self
|
20
|
+
proc_string = build_proc(<<-EOF)
|
21
|
+
arguments = [
|
22
|
+
cb_object.send(:raw_proc).inspect,
|
23
|
+
cb_object.send(:name_arguments_string),
|
24
|
+
cb_object.send(:rest_param_string),
|
25
|
+
cb_object.send(:kw_arguments_string),
|
26
|
+
cb_object.send(:keyrest_string),
|
27
|
+
cb_object.send(:procedure_string)].compact.join(', ')
|
28
|
+
target.instance_eval("send(\#{arguments})")
|
29
|
+
EOF
|
30
|
+
_wrapper = eval(proc_string)
|
31
|
+
_wrapper.instance_exec(raw_proc, &OVERLOAD_EQUALITY_OPERATOR_PROC)
|
32
|
+
_wrapper
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# A that is instanced_exec'd on a new proc object within {#wrapper}
|
38
|
+
#
|
39
|
+
# Enables comparison of two wrapper procs to determine if they wrap the same
|
40
|
+
# Method.
|
41
|
+
OVERLOAD_EQUALITY_OPERATOR_PROC = Proc.new do |method_name|
|
42
|
+
def method_name
|
43
|
+
method_name
|
44
|
+
end
|
45
|
+
|
46
|
+
# Equality operator overload.
|
47
|
+
# If other is a {Symbol}, matches this object against {#method_name} defined above.
|
48
|
+
# If other is a {Proc}:
|
49
|
+
# * If it responds to {#method_name}, matches the method names of the two objects.
|
50
|
+
# * Otherwise false
|
51
|
+
#
|
52
|
+
# @param [Symbol] other A method name to compare against.
|
53
|
+
# @param [Proc] other A proc to compare against.
|
54
|
+
# @return [Boolean] Whether the two should be considered equivalent
|
55
|
+
def ==(other)
|
56
|
+
case other
|
57
|
+
when ::Proc
|
58
|
+
if other.respond_to?(:raw_proc)
|
59
|
+
self.method_name == other.method_name
|
60
|
+
else
|
61
|
+
false
|
62
|
+
end
|
63
|
+
when ::Symbol
|
64
|
+
self.method_name == other
|
65
|
+
else
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def name_arguments_string
|
72
|
+
if name_params.any?
|
73
|
+
name_params.map{|name|
|
74
|
+
"name_proc.call(:#{name})"
|
75
|
+
}.join(', ')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def procedure_string
|
80
|
+
'&callbacks' if around_callback?
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [UnboundMethod] Method representation from class {#calling_class}, named by {#raw_proc}
|
84
|
+
def callback_method
|
85
|
+
@meth ||= calling_class.instance_method(raw_proc)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Parameter definition for the object. See {UnboundMethod#parameters}
|
89
|
+
#
|
90
|
+
# @return [Array] Parameters
|
91
|
+
def parameters
|
92
|
+
callback_method.parameters
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Fixnum] Arity of the callback method
|
96
|
+
def arity
|
97
|
+
callback_method.arity
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
module Workflow
|
3
|
+
module Callbacks
|
4
|
+
module TransitionCallbacks
|
5
|
+
# A {Workflow::Callbacks::TransitionCallback} that wraps a callback proc.
|
6
|
+
class ProcWrapper < ::Workflow::Callbacks::TransitionCallback
|
7
|
+
# Builds a proc object that will correctly call the {#raw_proc}
|
8
|
+
# by inspecting its parameters and pulling arguments from the {Workflow::TransitionContext}
|
9
|
+
# object for the transition.
|
10
|
+
def wrapper
|
11
|
+
arguments = [
|
12
|
+
name_arguments_string,
|
13
|
+
rest_param_string,
|
14
|
+
kw_arguments_string,
|
15
|
+
keyrest_string,
|
16
|
+
procedure_string].compact.join(', ')
|
17
|
+
|
18
|
+
raw_proc = self.raw_proc
|
19
|
+
str = build_proc("target.instance_exec(#{arguments})")
|
20
|
+
eval(str)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def name_arguments_string
|
26
|
+
params = name_params
|
27
|
+
names = []
|
28
|
+
names << 'target' if params.shift
|
29
|
+
(names << 'callbacks') && params.shift if around_callback?
|
30
|
+
names += params.map{|name| "name_proc.call(:#{name})"}
|
31
|
+
return names.join(', ') if names.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
def procedure_string
|
35
|
+
'&raw_proc'
|
36
|
+
end
|
37
|
+
|
38
|
+
def parameters
|
39
|
+
raw_proc.parameters
|
40
|
+
end
|
41
|
+
|
42
|
+
def arity
|
43
|
+
raw_proc.arity
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/workflow/event.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
module Workflow
|
2
2
|
class Event
|
3
|
+
# @!attribute [r] name
|
4
|
+
# @return [Symbol] The name of the event.
|
5
|
+
# @!attribute [r] transitions
|
6
|
+
# @return [Array] Array of {Workflow::Event::Transition}s defined for this event.
|
7
|
+
# @!attribute [r] meta
|
8
|
+
# @return [Hash] Extra information defined for this event.
|
3
9
|
attr_reader :name, :transitions, :meta
|
4
10
|
|
5
|
-
|
11
|
+
# @api private
|
12
|
+
# See {Workflow::State#on} for creating objects of this class.
|
13
|
+
# @param [Symbol] name The name of the event to create.
|
14
|
+
# @param [Hash] meta: Optional Metadata for this object.
|
15
|
+
def initialize(name, meta: {})
|
6
16
|
@name = name.to_sym
|
7
17
|
@transitions = []
|
8
18
|
@meta = meta || {}
|
@@ -12,27 +22,51 @@ module Workflow
|
|
12
22
|
"<Event name=#{name.inspect} transitions(#{transitions.length})=#{transitions.inspect}>"
|
13
23
|
end
|
14
24
|
|
25
|
+
# Returns the {Workflow::State} that the target object should enter.
|
26
|
+
# This will be the first one in the list of transitions, whose conditions apply
|
27
|
+
# to the target object in its present state.
|
28
|
+
# @param [Object] target An object of the class that this event was defined on.
|
29
|
+
# @return [Workflow::State] The first applicable destination state, or nil if none.
|
15
30
|
def evaluate(target)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
else
|
20
|
-
nil
|
21
|
-
end
|
31
|
+
transitions.find{|transition|
|
32
|
+
transition.matches? target
|
33
|
+
}&.target_state
|
22
34
|
end
|
23
35
|
|
36
|
+
# Add a {Workflow::Transition} to the possible {#transitions} for this event.
|
37
|
+
#
|
38
|
+
# @param [Symbol] target_state the name of the state target state if this transition matches.
|
39
|
+
# @option conditions_def [Symbol] :if Name of instance method to evaluate. e.g. `:valid?`
|
40
|
+
# @option conditions_def [Array] :if Mixed array of Symbol, String or Proc conditions. All must match for the transition to apply.
|
41
|
+
# @option conditions_def [String] :if A string to evaluate on the target. e.g. `"self.foo == :bar"`
|
42
|
+
# @option conditions_def [Proc] :if A proc which will be evaluated on the object e.g. `->{self.foo == :bar}`
|
43
|
+
# @option conditions_def [Symbol] :unless Same as `:if` except all conditions must **not** match.
|
44
|
+
# @yield [] Optional block which, if provided, becomes an `:if` condition for the transition.
|
45
|
+
# @return [nil]
|
24
46
|
def to(target_state, **conditions_def, &block)
|
25
47
|
conditions = Conditions.new &&conditions_def, block
|
26
48
|
self.transitions << Transition.new(target_state, conditions_def, &block)
|
27
49
|
end
|
28
50
|
|
29
51
|
private
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
# Represents a possible transition via the event on which it is defined.
|
30
55
|
class Transition
|
31
|
-
|
32
|
-
|
56
|
+
# @!attribute [r] target_state
|
57
|
+
# @return [Workflow::State] The target state for this transition.
|
58
|
+
attr_accessor :target_state
|
59
|
+
|
60
|
+
# Whether or not the conditions match for the target object.
|
61
|
+
#
|
62
|
+
# @param [Object] target an object of the class for which this event/transition was defined.
|
63
|
+
# @return [Boolean] True if all conditions apply.
|
64
|
+
def matches?(target)
|
33
65
|
conditions.apply?(target)
|
34
66
|
end
|
35
|
-
|
67
|
+
|
68
|
+
# @param [Symbol] target_state the name of the state target state if this transition matches.
|
69
|
+
# @param [Hash] conditions_def See {Event#to}
|
36
70
|
def initialize(target_state, conditions_def, &block)
|
37
71
|
@target_state = target_state
|
38
72
|
@conditions = Conditions.new conditions_def, &block
|
@@ -41,9 +75,19 @@ module Workflow
|
|
41
75
|
def inspect
|
42
76
|
"<to=#{target_state.inspect} conditions=#{conditions.inspect}"
|
43
77
|
end
|
78
|
+
|
79
|
+
private
|
80
|
+
# @!attribute [r] conditions
|
81
|
+
# @return [Workflow::Event::Conditions] Conditions for this transition.
|
82
|
+
attr_reader :conditions
|
44
83
|
end
|
45
84
|
|
46
|
-
|
85
|
+
# @api private
|
86
|
+
# Maintains a list of callback procs which are evaluted to determine if the
|
87
|
+
# transition on which they were defined is valid for an object in its current state.
|
88
|
+
# Borrowed from ActiveSupport::Callbacks
|
89
|
+
# See [original source here](https://github.com/rails/rails/blob/bca2e69b785fa3cdbe148b0d2dd5d3b58f6daf53/activesupport/lib/active_support/callbacks.rb#L419)
|
90
|
+
class Conditions
|
47
91
|
def initialize(**options, &block)
|
48
92
|
@if = Array(options[:if])
|
49
93
|
@unless = Array(options[:unless])
|
@@ -61,7 +105,6 @@ module Workflow
|
|
61
105
|
|
62
106
|
private
|
63
107
|
|
64
|
-
# From https://github.com/rails/rails/blob/bca2e69b785fa3cdbe148b0d2dd5d3b58f6daf53/activesupport/lib/active_support/callbacks.rb#L419
|
65
108
|
def conditions_lambdas
|
66
109
|
@if.map { |c| Callbacks::Callback.new c } +
|
67
110
|
@unless.map { |c| Callbacks::Callback.new c, true }
|