rails-workflow 1.4.4.4 → 1.4.5.4
Sign up to get free protection for your applications and to get access to all the features.
- 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 }
|