pluginaweek-state_machine 0.7.6 → 0.8.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.
data/CHANGELOG.rdoc CHANGED
@@ -1,6 +1,26 @@
1
1
  == master
2
2
 
3
- * Add support for customizing generated methods like #{attribute}_name using :as instead of always prefixing with the attribute name
3
+ == 0.8.0 / 2009-08-15
4
+
5
+ * Add support for DataMapper 0.10.0
6
+ * Always interpet nil return values from actions as failed attempts
7
+ * Fix loopbacks not causing records to save in ORM integrations if no other fields were changed
8
+ * Fix events not failing with useful errors when an object's state is invalid
9
+ * Use more friendly NoMethodError messages for state-driven behaviors
10
+ * Fix before_transition callbacks getting run twice when using event attributes in ORM integrations
11
+ * Add the ability to query for the availability of specific transitions on an object
12
+ * Allow after_transition callbacks to be explicitly run on failed attempts
13
+ * By default, don't run after_transition callbacks on failed attempts
14
+ * Fix not allowing multiple methods to be specified as arguments in callbacks
15
+ * Fix initial states being set when loading records from the database in Sequel integration
16
+ * Allow static initial states to be set earlier in the initialization of an object
17
+ * Use friendly validation errors for nil states
18
+ * Fix states not being validated properly when using custom names in ActiveRecord / DataMapper integrations
19
+
20
+ == 0.7.6 / 2009-06-17
21
+
22
+ * Allow multiple state machines on the same class to target the same attribute
23
+ * Add support for :attribute to customize the attribute target, assuming the name is the first argument of #state_machine
4
24
  * Simplify reading from / writing to machine-related attributes on objects
5
25
  * Fix locale for ActiveRecord getting added to the i18n load path multiple times [Reiner Dieterich]
6
26
  * Fix callbacks, guards, and state-driven behaviors not always working on tainted classes [Brandon Dimcheff]
data/README.rdoc CHANGED
@@ -412,7 +412,7 @@ To generate multiple state machine graphs:
412
412
 
413
413
  *Note* that this will generate a different file for every state machine defined
414
414
  in the class. The generated files will use an output filename of the format
415
- #{class_name}_#{attribute}.#{format}.
415
+ #{class_name}_#{machine_name}.#{format}.
416
416
 
417
417
  For examples of actual images generated using this task, see those under the
418
418
  examples folder.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
5
5
 
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = 'state_machine'
8
- s.version = '0.7.5'
8
+ s.version = '0.8.0'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'Adds support for creating state machines for attributes on any Ruby class'
11
11
  s.description = s.summary
data/lib/state_machine.rb CHANGED
@@ -5,10 +5,12 @@ require 'state_machine/machine'
5
5
  # functionality on any Ruby class.
6
6
  module StateMachine
7
7
  module MacroMethods
8
- # Creates a new state machine for the given attribute. The default
9
- # attribute, if not specified, is <tt>:state</tt>.
8
+ # Creates a new state machine with the given name. The default name, if not
9
+ # specified, is <tt>:state</tt>.
10
10
  #
11
11
  # Configuration options:
12
+ # * <tt>:attribute</tt> - The name of the attribute to store the state value
13
+ # in. By default, this is the same as the name of the machine.
12
14
  # * <tt>:initial</tt> - The initial state of the attribute. This can be a
13
15
  # static state or a lambda block which will be evaluated at runtime
14
16
  # (e.g. lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling}).
@@ -16,10 +18,6 @@ module StateMachine
16
18
  # * <tt>:action</tt> - The instance method to invoke when an object
17
19
  # transitions. Default is nil unless otherwise specified by the
18
20
  # configured integration.
19
- # * <tt>:as</tt> - The name to use for prefixing all generated machine
20
- # instance / class methods (e.g. if the attribute is +state_id+, then
21
- # "state" would generate :state_name, :state_transitions, etc. instead of
22
- # :state_id_name and :state_id_transitions)
23
21
  # * <tt>:namespace</tt> - The name to use for namespacing all generated
24
22
  # state / event instance methods (e.g. "heater" would generate
25
23
  # :turn_on_heater and :turn_off_heater for the :turn_on/:turn_off events).
@@ -49,12 +47,12 @@ module StateMachine
49
47
  # result, you will not be able to access any class methods unless you refer
50
48
  # to them directly (i.e. specifying the class name).
51
49
  #
52
- # For examples on the types of configured state machines and blocks, see
50
+ # For examples on the types of state machine configurations and blocks, see
53
51
  # the section below.
54
52
  #
55
53
  # == Examples
56
54
  #
57
- # With the default attribute and no configuration:
55
+ # With the default name/attribute and no configuration:
58
56
  #
59
57
  # class Vehicle
60
58
  # state_machine do
@@ -64,13 +62,14 @@ module StateMachine
64
62
  # end
65
63
  # end
66
64
  #
67
- # The above example will define a state machine for the +state+ attribute
68
- # on the class. Every vehicle will start without an initial state.
65
+ # The above example will define a state machine named "state" that will
66
+ # store the value in the +state+ attribute. Every vehicle will start
67
+ # without an initial state.
69
68
  #
70
- # With a custom attribute:
69
+ # With a custom name / attribute:
71
70
  #
72
71
  # class Vehicle
73
- # state_machine :status do
72
+ # state_machine :status, :attribute => :status_value do
74
73
  # ...
75
74
  # end
76
75
  # end
@@ -94,7 +93,8 @@ module StateMachine
94
93
  # == Instance Methods
95
94
  #
96
95
  # The following instance methods will be automatically generated by the
97
- # state machine. Any existing methods will not be overwritten.
96
+ # state machine based on the *name* of the machine. Any existing methods
97
+ # will not be overwritten.
98
98
  # * <tt>state</tt> - Gets the current value for the attribute
99
99
  # * <tt>state=(value)</tt> - Sets the current value for the attribute
100
100
  # * <tt>state?(name)</tt> - Checks the given state name against the current
@@ -102,8 +102,11 @@ module StateMachine
102
102
  # * <tt>state_name</tt> - Gets the name of the state for the current value
103
103
  # * <tt>state_events</tt> - Gets the list of events that can be fired on
104
104
  # the current object's state (uses the *unqualified* event names)
105
- # * <tt>state_transitions</tt> - Gets the list of possible transitions
106
- # that can be made on the current object's state
105
+ # * <tt>state_transitions(requirements = {})</tt> - Gets the list of possible
106
+ # transitions that can be made on the current object's state. Additional
107
+ # requirements, such as the :from / :to state and :on event can be specified
108
+ # to restrict the transitions to select. By default, the current state
109
+ # will be used for the :from state.
107
110
  #
108
111
  # For example,
109
112
  #
@@ -253,7 +256,7 @@ module StateMachine
253
256
  #
254
257
  # class Vehicle
255
258
  # include DataMapper::Resource
256
- # property :id, Integer, :serial => true
259
+ # property :id, Serial
257
260
  #
258
261
  # state_machine :initial => :parked do
259
262
  # event :ignite do
@@ -295,55 +298,11 @@ module StateMachine
295
298
  # see StateMachine::Machine#before_transition and
296
299
  # StateMachine::Machine#after_transition.
297
300
  #
298
- # == Attribute aliases
299
- #
300
- # When a state machine is defined, several methods are generated scoped by
301
- # the name of the attribute, such as (if the attribute were "state"):
302
- # * <tt>state_name</tt>
303
- # * <tt>state_event</tt>
304
- # * <tt>state_transitions</tt>
305
- # * etc.
306
- #
307
- # If the attribute for the machine were something less common, such as
308
- # "state_id" or "state_value", this makes for more awkward scoped methods.
309
- #
310
- # Rather than scope based on the attribute, these methods can be customized
311
- # using the <tt>:as</tt> option as essentially an alias.
312
- #
313
- # For example,
314
- #
315
- # class Vehicle
316
- # state_machine :state_id, :as => :state do
317
- # event :turn_on do
318
- # transition all => :on
319
- # end
320
- #
321
- # event :turn_off do
322
- # transition all => :off
323
- # end
324
- #
325
- # state :on, :value => 1
326
- # state :off, :value => 2
327
- # end
328
- # end
329
- #
330
- # ...will generate the following methods:
331
- # * <tt>state_name</tt>
332
- # * <tt>state_event</tt>
333
- # * <tt>state_transitions</tt>
334
- #
335
- # ...instead of:
336
- # * <tt>state_id_name</tt>
337
- # * <tt>state_id_event</tt>
338
- # * <tt>state_id_transitions</tt>
339
- #
340
- # However, it will continue to read and write to the +state_id+ attribute.
341
- #
342
301
  # == Namespaces
343
302
  #
344
303
  # When a namespace is configured for a state machine, the name provided
345
304
  # will be used in generating the instance methods for interacting with
346
- # events/states in the machine. This is particularly useful when a class
305
+ # states/events in the machine. This is particularly useful when a class
347
306
  # has multiple state machines and it would be difficult to differentiate
348
307
  # between the various states / events.
349
308
  #
@@ -398,7 +357,7 @@ module StateMachine
398
357
  #
399
358
  # For integrations that support it, a group of default scope filters will
400
359
  # be automatically created for assisting in finding objects that have the
401
- # attribute set to the value for a given set of states.
360
+ # attribute set to one of a given set of states.
402
361
  #
403
362
  # For example,
404
363
  #
@@ -412,9 +371,9 @@ module StateMachine
412
371
  # :with_state, :with_states, :without_state, or :without_states), then a
413
372
  # scope will not be defined for that name.
414
373
  #
415
- # See StateMachine::Machine for more information about using
416
- # integrations and the individual integration docs for information about
417
- # the actual scopes that are generated.
374
+ # See StateMachine::Machine for more information about using integrations
375
+ # and the individual integration docs for information about the actual
376
+ # scopes that are generated.
418
377
  def state_machine(*args, &block)
419
378
  StateMachine::Machine.find_or_create(self, *args, &block)
420
379
  end
@@ -142,7 +142,7 @@ module StateMachine
142
142
  # requirements configured for this callback.
143
143
  #
144
144
  # If a terminator has been configured and it matches the result from the
145
- # evaluated method, then the callback chain should be halted
145
+ # evaluated method, then the callback chain should be halted.
146
146
  def call(object, context = {}, *args)
147
147
  if @guard.matches?(object, context)
148
148
  @methods.each do |method|
@@ -65,8 +65,8 @@ module StateMachine
65
65
  # state to be +idling+ if it's current state is +parked+ or +first_gear+
66
66
  # if it's current state is +idling+.
67
67
  #
68
- # To help defining these implicit transitions, a set of helpers are available
69
- # for defining slightly more complex matching:
68
+ # To help define these implicit transitions, a set of helpers are available
69
+ # for slightly more complex matching:
70
70
  # * <tt>all</tt> - Matches every state in the machine
71
71
  # * <tt>all - [:parked, :idling, ...]</tt> - Matches every state except those specified
72
72
  # * <tt>any</tt> - An alias for +all+ (matches every state in the machine)
@@ -147,7 +147,7 @@ module StateMachine
147
147
  # requirements
148
148
  assert_valid_keys(options, :from, :to, :except_from, :if, :unless) if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on, :if, :unless]).empty?
149
149
 
150
- guards << guard = Guard.new(options)
150
+ guards << guard = Guard.new(options.merge(:on => name))
151
151
  @known_states |= guard.known_states
152
152
  guard
153
153
  end
@@ -162,15 +162,16 @@ module StateMachine
162
162
 
163
163
  # Finds and builds the next transition that can be performed on the given
164
164
  # object. If no transitions can be made, then this will return nil.
165
- def transition_for(object)
166
- from = machine.states.match(object).name
165
+ def transition_for(object, requirements = {})
166
+ requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from)
167
167
 
168
168
  guards.each do |guard|
169
- if match = guard.match(object, :from => from)
169
+ if match = guard.match(object, requirements)
170
170
  # Guard allows for the transition to occur
171
+ from = requirements[:from]
171
172
  to = match[:to].values.empty? ? from : match[:to].values.first
172
173
 
173
- return Transition.new(object, machine, name, from, to)
174
+ return Transition.new(object, machine, name, from, to, !custom_from_state)
174
175
  end
175
176
  end
176
177
 
@@ -190,7 +191,7 @@ module StateMachine
190
191
  if transition = transition_for(object)
191
192
  transition.perform(*args)
192
193
  else
193
- machine.invalidate(object, machine.attribute, :invalid_transition, [[:event, name]])
194
+ machine.invalidate(object, :state, :invalid_transition, [[:event, name]])
194
195
  false
195
196
  end
196
197
  end
@@ -244,7 +245,7 @@ module StateMachine
244
245
 
245
246
  # Fires the event, raising an exception if it fails
246
247
  machine.define_instance_method("#{qualified_name}!") do |machine, object, *args|
247
- object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition, "Cannot transition #{machine.name} via :#{name} from #{machine.states.match(object).name.inspect}")
248
+ object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition, "Cannot transition #{machine.name} via :#{name} from #{machine.states.match!(object).name.inspect}")
248
249
  end
249
250
  end
250
251
  end
@@ -34,6 +34,14 @@ module StateMachine
34
34
 
35
35
  # Gets the list of transitions that can be run on the given object.
36
36
  #
37
+ # Valid requirement options:
38
+ # * <tt>:from</tt> - One or more states being transitioned from. If none
39
+ # are specified, then this will be the object's current state.
40
+ # * <tt>:to</tt> - One or more states being transitioned to. If none are
41
+ # specified, then this will match any to state.
42
+ # * <tt>:on</tt> - One or more events that fire the transition. If none
43
+ # are specified, then this will match any event.
44
+ #
37
45
  # == Examples
38
46
  #
39
47
  # class Vehicle
@@ -50,22 +58,25 @@ module StateMachine
50
58
  #
51
59
  # events = Vehicle.state_machine.events
52
60
  #
53
- # vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
54
- # events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
61
+ # vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
62
+ # events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
55
63
  #
56
64
  # vehicle.state = 'idling'
57
- # events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>]
58
- def transitions_for(object)
59
- map {|event| event.transition_for(object)}.compact
65
+ # events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>]
66
+ #
67
+ # # Search for explicit transitions regardless of the current state
68
+ # events.transitions_for(vehicle, :from => :parked) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
69
+ def transitions_for(object, requirements = {})
70
+ map {|event| event.transition_for(object, requirements)}.compact
60
71
  end
61
72
 
62
73
  # Gets the transition that should be performed for the event stored in the
63
74
  # given object's event attribute. This also takes an additional parameter
64
- # for automatically invalidating the object if the event or transition
65
- # are invalid. By default, this is turned off.
75
+ # for automatically invalidating the object if the event or transition are
76
+ # invalid. By default, this is turned off.
66
77
  #
67
- # *Note* that if a transition has already been generated for the event,
68
- # then that transition will be used.
78
+ # *Note* that if a transition has already been generated for the event, then
79
+ # that transition will be used.
69
80
  #
70
81
  # == Examples
71
82
  #
@@ -97,7 +108,7 @@ module StateMachine
97
108
  if event = self[event_name.to_sym, :name]
98
109
  unless result = machine.read(object, :event_transition) || event.transition_for(object)
99
110
  # No valid transition: invalidate
100
- machine.invalidate(object, :event, :invalid_event, [[:state, machine.states.match!(object).name]]) if invalidate
111
+ machine.invalidate(object, :event, :invalid_event, [[:state, machine.states.match!(object).name || 'nil']]) if invalidate
101
112
  result = false
102
113
  end
103
114
  else
@@ -22,15 +22,6 @@ module StateMachine
22
22
  end
23
23
 
24
24
  module InstanceMethods
25
- # Defines the initial values for state machine attributes. The values
26
- # will be set *after* the original initialize method is invoked. This is
27
- # necessary in order to ensure that the object is initialized before
28
- # dynamic initial attributes are evaluated.
29
- def initialize(*args, &block)
30
- super
31
- initialize_state_machines
32
- end
33
-
34
25
  # Runs one or more events in parallel. All events will run through the
35
26
  # following steps:
36
27
  # * Before callbacks
@@ -151,8 +142,8 @@ module StateMachine
151
142
  end
152
143
 
153
144
  protected
154
- def initialize_state_machines #:nodoc:
155
- self.class.state_machines.initialize_states(self)
145
+ def initialize_state_machines(options = {}) #:nodoc:
146
+ self.class.state_machines.initialize_states(self, options)
156
147
  end
157
148
  end
158
149
  end
@@ -24,6 +24,9 @@ module StateMachine
24
24
  # requirements contain a mapping of {:from => matcher, :to => matcher}.
25
25
  attr_reader :state_requirements
26
26
 
27
+ # The requirement for verifying the success of the event
28
+ attr_reader :success_requirement
29
+
27
30
  # A list of all of the states known to this guard. This will pull states
28
31
  # from the following options (in the same order):
29
32
  # * +from+ / +except_from+
@@ -39,6 +42,9 @@ module StateMachine
39
42
  # Build event requirement
40
43
  @event_requirement = build_matcher(options, :on, :except_on)
41
44
 
45
+ # Build success requirement
46
+ @success_requirement = options.delete(:include_failures) ? AllMatcher.instance : WhitelistMatcher.new([true])
47
+
42
48
  if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on]).empty?
43
49
  # Explicit from/to requirements specified
44
50
  @state_requirements = [{:from => build_matcher(options, :from, :except_from), :to => build_matcher(options, :to, :except_to)}]
@@ -185,11 +191,16 @@ module StateMachine
185
191
  def match_query(query)
186
192
  query ||= {}
187
193
 
188
- if match_event(query) && (state_requirement = match_states(query))
194
+ if match_success(query) && match_event(query) && (state_requirement = match_states(query))
189
195
  state_requirement.merge(:on => event_requirement)
190
196
  end
191
197
  end
192
198
 
199
+ # Verifies that the success requirement matches the given query
200
+ def match_success(query)
201
+ matches_requirement?(query, :success, success_requirement)
202
+ end
203
+
193
204
  # Verifies that the event requirement matches the given query
194
205
  def match_event(query)
195
206
  matches_requirement?(query, :on, event_requirement)
@@ -84,14 +84,14 @@ module StateMachine
84
84
  # you can build two state machines (one public and one protected) like so:
85
85
  #
86
86
  # class Vehicle < ActiveRecord::Base
87
- # alias_attribute :public_state # Allow both machines to share the same state
88
87
  # attr_protected :state_event # Prevent access to events in the first machine
89
88
  #
90
89
  # state_machine do
91
90
  # # Define private events here
92
91
  # end
93
92
  #
94
- # state_machine :public_state do
93
+ # # Public machine targets the same state as the private machine
94
+ # state_machine :public_state, :attribute => :state do
95
95
  # # Define public events here
96
96
  # end
97
97
  # end
@@ -281,6 +281,14 @@ module StateMachine
281
281
  end
282
282
  end
283
283
 
284
+ # Forces the change in state to be recognized regardless of whether the
285
+ # state value actually changed
286
+ def write(object, attribute, value)
287
+ result = super
288
+ object.send("#{self.attribute}_will_change!") if attribute == :state && object.respond_to?("#{self.attribute}_will_change!")
289
+ result
290
+ end
291
+
284
292
  # Adds a validation error to the given object
285
293
  def invalidate(object, attribute, message, values = [])
286
294
  attribute = self.attribute(attribute)
@@ -308,11 +316,41 @@ module StateMachine
308
316
  callbacks[:after] << Callback.new {|object, transition| notify(:after, object, transition)}
309
317
  end
310
318
 
319
+ # Defines an initialization hook into the owner class for setting the
320
+ # initial state of the machine *before* any attributes are set on the
321
+ # object
322
+ def define_state_initializer
323
+ @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
324
+ # Ensure that the attributes setter gets used to force initialization
325
+ # of the state machines
326
+ def initialize(attributes = nil, *args)
327
+ attributes ||= {}
328
+ super
329
+ end
330
+
331
+ # Hooks in to attribute initialization to set the states *prior*
332
+ # to the attributes being set
333
+ def attributes=(*args)
334
+ if new_record? && !@initialized_state_machines
335
+ @initialized_state_machines = true
336
+
337
+ initialize_state_machines(:dynamic => false)
338
+ super
339
+ initialize_state_machines(:dynamic => true)
340
+ else
341
+ super
342
+ end
343
+ end
344
+ end_eval
345
+ end
346
+
311
347
  # Skips defining reader/writer methods since this is done automatically
312
348
  def define_state_accessor
349
+ name = self.name
350
+
313
351
  owner_class.validates_each(attribute) do |record, attr, value|
314
- machine = record.class.state_machine(attr)
315
- machine.invalidate(record, attr, :invalid) unless machine.states.match(record)
352
+ machine = record.class.state_machine(name)
353
+ machine.invalidate(record, :state, :invalid) unless machine.states.match(record)
316
354
  end
317
355
  end
318
356
 
@@ -321,13 +359,12 @@ module StateMachine
321
359
  # *anything* is set for the attribute's value
322
360
  def define_state_predicate
323
361
  name = self.name
324
- attribute = self.attribute
325
362
 
326
363
  # Still use class_eval here instance of define_instance_method since
327
364
  # we need to be able to call +super+
328
365
  @instance_helper_module.class_eval do
329
366
  define_method("#{name}?") do |*args|
330
- args.empty? ? super(*args) : self.class.state_machine(attribute).states.matches?(self, *args)
367
+ args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
331
368
  end
332
369
  end
333
370
  end
@@ -388,17 +425,18 @@ module StateMachine
388
425
  # inheritance is respected properly.
389
426
  def define_scope(name, scope)
390
427
  name = name.to_sym
391
- attribute = self.attribute
428
+ machine_name = self.name
392
429
 
393
- # Created the scope and then override it with state translation
430
+ # Create the scope and then override it with state translation
394
431
  owner_class.named_scope(name)
395
432
  owner_class.scopes[name] = lambda do |klass, *states|
396
- machine_states = klass.state_machine(attribute).states
433
+ machine_states = klass.state_machine(machine_name).states
397
434
  values = states.flatten.map {|state| machine_states.fetch(state).value}
398
435
 
399
436
  ::ActiveRecord::NamedScope::Scope.new(klass, scope.call(values))
400
437
  end
401
438
 
439
+ # Prevent the Machine class from wrapping the scope
402
440
  false
403
441
  end
404
442
 
@@ -409,10 +447,10 @@ module StateMachine
409
447
  # * #{type}_#{qualified_event}_from_#{from}
410
448
  # * #{type}_#{qualified_event}_to_#{to}
411
449
  # * #{type}_#{qualified_event}
412
- # * #{type}_transition_#{attribute}_from_#{from}_to_#{to}
413
- # * #{type}_transition_#{attribute}_from_#{from}
414
- # * #{type}_transition_#{attribute}_to_#{to}
415
- # * #{type}_transition_#{attribute}
450
+ # * #{type}_transition_#{machine_name}_from_#{from}_to_#{to}
451
+ # * #{type}_transition_#{machine_name}_from_#{from}
452
+ # * #{type}_transition_#{machine_name}_to_#{to}
453
+ # * #{type}_transition_#{machine_name}
416
454
  # * #{type}_transition
417
455
  #
418
456
  # This will always return true regardless of the results of the