state_machine 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +10 -0
- data/README.rdoc +36 -5
- data/Rakefile +1 -1
- data/lib/state_machine.rb +47 -9
- data/lib/state_machine/event.rb +38 -25
- data/lib/state_machine/machine.rb +59 -23
- data/lib/state_machine/transition.rb +34 -15
- data/test/app_root/app/models/switch.rb +3 -0
- data/test/app_root/script/console +8 -0
- data/test/app_root/script/rails_framework_root.rb +1 -0
- data/test/factory.rb +7 -7
- data/test/unit/event_test.rb +69 -21
- data/test/unit/machine_test.rb +25 -3
- data/test/unit/transition_test.rb +104 -36
- metadata +5 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.2.1 / 2008-07-05
|
4
|
+
|
5
|
+
* Add more descriptive exceptions
|
6
|
+
* Assume the default state attribute is "state" if one is not provided
|
7
|
+
* Add :except_from option for transitions if you want to blacklist states
|
8
|
+
* Add PluginAWeek::StateMachine::Machine#states
|
9
|
+
* Add PluginAWeek::StateMachine::Event#transitions
|
10
|
+
* Allow creating transitions with no from state (effectively allowing the transition for *any* from state)
|
11
|
+
* Reduce the number of objects created for each transition
|
12
|
+
|
3
13
|
== 0.2.0 / 2008-06-29
|
4
14
|
|
5
15
|
* Add a non-bang version of events (e.g. park) that will return a boolean value for success
|
data/README.rdoc
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
== state_machine
|
2
2
|
|
3
|
-
+state_machine+ adds support for creating state machines for attributes within
|
3
|
+
+state_machine+ adds support for creating state machines for attributes within
|
4
|
+
a model.
|
4
5
|
|
5
6
|
== Resources
|
6
7
|
|
@@ -28,15 +29,21 @@ and deciding how to behave based on the values in those columns. This can becom
|
|
28
29
|
cumbersome and difficult to maintain when the complexity of your models starts to
|
29
30
|
increase.
|
30
31
|
|
31
|
-
+state_machine+ simplifies this design by introducing the various parts of a
|
32
|
-
machine, including states, events, and transitions. However,
|
33
|
-
to be similar to ActiveRecord in terms of validations and callbacks,
|
34
|
-
so simple you don't even need to know what a state machine is :)
|
32
|
+
+state_machine+ simplifies this design by introducing the various parts of a real
|
33
|
+
state machine, including states, events, and transitions. However, the api is
|
34
|
+
designed to be similar to ActiveRecord in terms of validations and callbacks,
|
35
|
+
making it so simple you don't even need to know what a state machine is :)
|
35
36
|
|
36
37
|
== Usage
|
37
38
|
|
38
39
|
=== Example
|
39
40
|
|
41
|
+
Below is an example of many of the features offered by this plugin, including
|
42
|
+
* Initial states
|
43
|
+
* State callbacks
|
44
|
+
* Event callbacks
|
45
|
+
* Conditional transitions
|
46
|
+
|
40
47
|
class Vehicle < ActiveRecord::Base
|
41
48
|
state_machine :state, :initial => 'idling' do
|
42
49
|
before_exit 'parked', :put_on_seatbelt
|
@@ -74,8 +81,32 @@ so simple you don't even need to know what a state machine is :)
|
|
74
81
|
transition :to => 'parked', :from => 'stalled', :if => :auto_shop_busy?
|
75
82
|
end
|
76
83
|
end
|
84
|
+
|
85
|
+
def tow!
|
86
|
+
end
|
87
|
+
|
88
|
+
def fix!
|
89
|
+
end
|
90
|
+
|
91
|
+
def auto_shop_busy?
|
92
|
+
false
|
93
|
+
end
|
77
94
|
end
|
78
95
|
|
96
|
+
Using the above model as an example, you can interact with the state machine
|
97
|
+
like so:
|
98
|
+
|
99
|
+
vehicle = Vehicle.create # => #<Vehicle id: 1, seatbelt_on: false, state: "parked">
|
100
|
+
vehicle.ignite # => true
|
101
|
+
vehicle # => #<Vehicle id: 1, seatbelt_on: true, state: "idling">
|
102
|
+
vehicle.shift_up # => true
|
103
|
+
vehicle # => #<Vehicle id: 1, seatbelt_on: true, state: "first_gear">
|
104
|
+
vehicle.shift_up # => true
|
105
|
+
vehicle # => #<Vehicle id: 1, seatbelt_on: true, state: "second_gear">
|
106
|
+
|
107
|
+
# The bang (!) operator can raise exceptions if the event fails
|
108
|
+
vehicle.park! # => PluginAWeek::StateMachine::InvalidTransition: Cannot transition via :park from "second_gear"
|
109
|
+
|
79
110
|
== Tools
|
80
111
|
|
81
112
|
Jean Bovet - {Visual Automata Simulator}[http://www.cs.usfca.edu/~jbovet/vas.html].
|
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.2.
|
8
|
+
s.version = '0.2.1'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.summary = 'Adds support for creating state machines for attributes within a model'
|
11
11
|
|
data/lib/state_machine.rb
CHANGED
@@ -12,29 +12,65 @@ module PluginAWeek #:nodoc:
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module MacroMethods
|
15
|
-
# Creates a state machine for the given attribute.
|
15
|
+
# Creates a state machine for the given attribute. The default attribute
|
16
|
+
# is "state".
|
16
17
|
#
|
17
18
|
# Configuration options:
|
18
19
|
# * +initial+ - The initial value of the attribute. This can either be the actual value or a Proc for dynamic initial states.
|
19
20
|
#
|
20
|
-
#
|
21
|
+
# This also requires a block which will be used to actually configure the
|
22
|
+
# events and transitions for the state machine. *Note* that this block will
|
23
|
+
# be executed within the context of the state machine. As a result, you will
|
24
|
+
# not be able to access any class methods on the model unless you refer to
|
25
|
+
# them directly (i.e. specifying the class name).
|
21
26
|
#
|
22
|
-
#
|
27
|
+
# For examples on the types of configured state machines and blocks, see
|
28
|
+
# the section below.
|
29
|
+
#
|
30
|
+
# == Examples
|
31
|
+
#
|
32
|
+
# With the default attribute and no initial state:
|
33
|
+
#
|
34
|
+
# class Switch < ActiveRecord::Base
|
35
|
+
# state_machine do
|
36
|
+
# event :park do
|
37
|
+
# ...
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The above example will define a state machine for the attribute "state"
|
43
|
+
# on the model. Every switch will start with no initial state.
|
44
|
+
#
|
45
|
+
# With a custom attribute:
|
23
46
|
#
|
24
47
|
# class Switch < ActiveRecord::Base
|
25
|
-
# state_machine :
|
48
|
+
# state_machine :status do
|
26
49
|
# ...
|
27
50
|
# end
|
28
51
|
# end
|
29
52
|
#
|
30
|
-
# With a
|
53
|
+
# With a static initial state:
|
31
54
|
#
|
32
55
|
# class Switch < ActiveRecord::Base
|
33
|
-
# state_machine :
|
56
|
+
# state_machine :status, :initial => 'off' do
|
34
57
|
# ...
|
35
58
|
# end
|
36
59
|
# end
|
37
|
-
|
60
|
+
#
|
61
|
+
# With a dynamic initial state:
|
62
|
+
#
|
63
|
+
# class Switch < ActiveRecord::Base
|
64
|
+
# state_machine :status, :initial => Proc.new {|switch| (8..22).include?(Time.now.hour) ? 'on' : 'off'} do
|
65
|
+
# ...
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# == Events and Transitions
|
70
|
+
#
|
71
|
+
# For more information about how to configure an event and its associated
|
72
|
+
# transitions, see PluginAWeek::StateMachine::Machine#event
|
73
|
+
def state_machine(*args, &block)
|
38
74
|
unless included_modules.include?(PluginAWeek::StateMachine::InstanceMethods)
|
39
75
|
write_inheritable_attribute :state_machines, {}
|
40
76
|
class_inheritable_reader :state_machines
|
@@ -44,10 +80,12 @@ module PluginAWeek #:nodoc:
|
|
44
80
|
include PluginAWeek::StateMachine::InstanceMethods
|
45
81
|
end
|
46
82
|
|
83
|
+
options = args.extract_options!
|
84
|
+
attribute = args.any? ? args.first.to_s : 'state'
|
85
|
+
options[:initial] = state_machines[attribute].initial_state_without_processing if !options.include?(:initial) && state_machines[attribute]
|
86
|
+
|
47
87
|
# This will create a new machine for subclasses as well so that the owner_class and
|
48
88
|
# initial state can be overridden
|
49
|
-
attribute = attribute.to_s
|
50
|
-
options[:initial] = state_machines[attribute].initial_state_without_processing if !options.include?(:initial) && state_machines[attribute]
|
51
89
|
machine = state_machines[attribute] = PluginAWeek::StateMachine::Machine.new(self, attribute, options)
|
52
90
|
machine.instance_eval(&block) if block
|
53
91
|
machine
|
data/lib/state_machine/event.rb
CHANGED
@@ -11,16 +11,24 @@ module PluginAWeek #:nodoc:
|
|
11
11
|
# The name of the action that fires the event
|
12
12
|
attr_reader :name
|
13
13
|
|
14
|
+
# The list of transitions that can be made for this event
|
15
|
+
attr_reader :transitions
|
16
|
+
|
14
17
|
delegate :owner_class,
|
15
18
|
:to => :machine
|
16
19
|
|
17
|
-
# Creates a new event
|
20
|
+
# Creates a new event within the context of the given machine.
|
21
|
+
#
|
22
|
+
# Configuration options:
|
23
|
+
# * +before+ - Callbacks to invoke before the event is fired
|
24
|
+
# * +after+ - Callbacks to invoke after the event is fired
|
18
25
|
def initialize(machine, name, options = {})
|
19
26
|
options.assert_valid_keys(:before, :after)
|
20
27
|
|
21
28
|
@machine = machine
|
22
29
|
@name = name
|
23
30
|
@options = options.stringify_keys
|
31
|
+
@transitions = []
|
24
32
|
|
25
33
|
add_transition_actions
|
26
34
|
add_transition_callbacks
|
@@ -31,55 +39,60 @@ module PluginAWeek #:nodoc:
|
|
31
39
|
#
|
32
40
|
# Configuration options:
|
33
41
|
# * +to+ - The state that being transitioned to
|
34
|
-
# * +from+ - A state or array of states that can be transitioned from
|
42
|
+
# * +from+ - A state or array of states that can be transitioned from. If not specified, then the transition can occur for *any* from state
|
43
|
+
# * +except_from+ - A state or array of states that *cannot* be transitioned from.
|
35
44
|
# * +if+ - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :moving?, or :if => Proc.new {|car| car.speed > 60}). The method, proc or string should return or evaluate to a true or false value.
|
36
45
|
# * +unless+ - Specifies a method, proc or string to call to determine if the transition should not occur (e.g. :unless => :stopped?, or :unless => Proc.new {|car| car.speed <= 60}). The method, proc or string should return or evaluate to a true or false value.
|
37
46
|
#
|
38
47
|
# == Examples
|
39
48
|
#
|
49
|
+
# transition :to => 'parked'
|
40
50
|
# transition :to => 'parked', :from => 'first_gear'
|
41
51
|
# transition :to => 'parked', :from => %w(first_gear reverse)
|
42
52
|
# transition :to => 'parked', :from => 'first_gear', :if => :moving?
|
43
53
|
# transition :to => 'parked', :from => 'first_gear', :unless => :stopped?
|
54
|
+
# transition :to => 'parked', :except_from => 'parked'
|
44
55
|
def transition(options = {})
|
56
|
+
# Slice out the callback options
|
45
57
|
options.symbolize_keys!
|
46
|
-
options.
|
47
|
-
raise ArgumentError, ':to state must be specified' unless options.include?(:to)
|
58
|
+
callback_options = {:if => options.delete(:if), :unless => options.delete(:unless)}
|
48
59
|
|
49
|
-
|
50
|
-
to_state = options.delete(:to)
|
51
|
-
from_states = Array(options.delete(:from))
|
60
|
+
transition = Transition.new(self, options)
|
52
61
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
owner_class.send("transition_bang_on_#{name}", callback, options)
|
65
|
-
|
66
|
-
transition
|
67
|
-
end
|
62
|
+
# Add the callback to the model. If the callback fails, then the next
|
63
|
+
# available callback for the event will run until one is successful.
|
64
|
+
callback = Proc.new {|record, *args| try_transition(transition, false, record, *args)}
|
65
|
+
owner_class.send("transition_on_#{name}", callback, callback_options)
|
66
|
+
|
67
|
+
# Add the callback! to the model similar to above
|
68
|
+
callback = Proc.new {|record, *args| try_transition(transition, true, record, *args)}
|
69
|
+
owner_class.send("transition_bang_on_#{name}", callback, callback_options)
|
70
|
+
|
71
|
+
transitions << transition
|
72
|
+
transition
|
68
73
|
end
|
69
74
|
|
70
|
-
# Attempts to perform one of the event's transitions for the given record
|
75
|
+
# Attempts to perform one of the event's transitions for the given record.
|
76
|
+
# Any additional arguments will be passed to the event's callbacks.
|
71
77
|
def fire(record, *args)
|
72
|
-
|
78
|
+
fire_with_optional_bang(record, false, *args) || false
|
73
79
|
end
|
74
80
|
|
75
81
|
# Attempts to perform one of the event's transitions for the given record.
|
76
82
|
# If the transition cannot be made, then a PluginAWeek::StateMachine::InvalidTransition
|
77
83
|
# error will be raised.
|
78
84
|
def fire!(record, *args)
|
79
|
-
|
85
|
+
fire_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition, "Cannot transition via :#{name} from #{record.send(machine.attribute).inspect}")
|
80
86
|
end
|
81
87
|
|
82
88
|
private
|
89
|
+
# Fires the event
|
90
|
+
def fire_with_optional_bang(record, bang, *args)
|
91
|
+
record.class.transaction do
|
92
|
+
invoke_transition_callbacks(record, bang, *args) || raise(ActiveRecord::Rollback)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
83
96
|
# Add the various instance methods that can transition the record using
|
84
97
|
# the current event
|
85
98
|
def add_transition_actions
|
@@ -2,19 +2,41 @@ require 'state_machine/event'
|
|
2
2
|
|
3
3
|
module PluginAWeek #:nodoc:
|
4
4
|
module StateMachine
|
5
|
-
# Represents a state machine for a particular attribute
|
5
|
+
# Represents a state machine for a particular attribute. State machines
|
6
|
+
# consist of events (a.k.a. actions) and a set of transitions that define
|
7
|
+
# how the state changes after a particular event is fired.
|
6
8
|
#
|
7
|
-
#
|
9
|
+
# A state machine may not necessarily know all of the possible states for
|
10
|
+
# an object since they can be any arbitrary value.
|
8
11
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# == Callbacks
|
13
|
+
#
|
14
|
+
# Callbacks are supported for hooking into event calls and state transitions.
|
15
|
+
# The order in which these callbacks are invoked is shown below:
|
16
|
+
# * (1) before_exit (from state)
|
17
|
+
# * (2) before_enter (to state)
|
18
|
+
# * (3) before (event)
|
19
|
+
# * (-) update state
|
20
|
+
# * (4) after_exit (from state)
|
21
|
+
# * (5) after_enter (to state)
|
22
|
+
# * (6) after (event)
|
23
|
+
#
|
24
|
+
# == Cancelling callbacks
|
25
|
+
#
|
26
|
+
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks
|
27
|
+
# and associated event are cancelled. If an <tt>after_*</tt> callback returns
|
28
|
+
# false, all the later callbacks are cancelled. Callbacks are run in the
|
29
|
+
# order in which they are defined.
|
30
|
+
#
|
31
|
+
# Note that if a <tt>before_*</tt> callback fails and the bang version of an
|
32
|
+
# event was invoked, an exception will be raised instaed of returning false.
|
14
33
|
class Machine
|
15
34
|
# The events that trigger transitions
|
16
35
|
attr_reader :events
|
17
36
|
|
37
|
+
# A list of the states defined in the transitions of all of the events
|
38
|
+
attr_reader :states
|
39
|
+
|
18
40
|
# The attribute for which the state machine is being defined
|
19
41
|
attr_accessor :attribute
|
20
42
|
|
@@ -27,29 +49,30 @@ module PluginAWeek #:nodoc:
|
|
27
49
|
# Creates a new state machine for the given attribute
|
28
50
|
#
|
29
51
|
# Configuration options:
|
30
|
-
# * +initial+ - The initial value to set the attribute to
|
52
|
+
# * +initial+ - The initial value to set the attribute to. This can be an actual value or a proc, which will be evaluated at runtime.
|
31
53
|
#
|
32
54
|
# == Scopes
|
33
55
|
#
|
34
|
-
# This will automatically
|
56
|
+
# This will automatically create a named scope called with_#{attribute}
|
35
57
|
# that will find all records that have the attribute set to a given value.
|
36
58
|
# For example,
|
37
59
|
#
|
38
60
|
# Switch.with_state('on') # => Finds all switches where the state is on
|
39
61
|
# Switch.with_states('on', 'off') # => Finds all switches where the state is either on or off
|
40
|
-
def initialize(owner_class, attribute, options = {})
|
62
|
+
def initialize(owner_class, attribute = 'state', options = {})
|
41
63
|
options.assert_valid_keys(:initial)
|
42
64
|
|
43
65
|
@owner_class = owner_class
|
44
66
|
@attribute = attribute.to_s
|
45
67
|
@initial_state = options[:initial]
|
46
68
|
@events = {}
|
69
|
+
@states = []
|
47
70
|
|
48
71
|
add_named_scopes
|
49
72
|
end
|
50
73
|
|
51
74
|
# Gets the initial state of the machine for the given record. The record
|
52
|
-
# is only used if a dynamic initial state
|
75
|
+
# is only used if a dynamic initial state was configured.
|
53
76
|
def initial_state(record)
|
54
77
|
@initial_state.is_a?(Proc) ? @initial_state.call(record) : @initial_state
|
55
78
|
end
|
@@ -60,25 +83,19 @@ module PluginAWeek #:nodoc:
|
|
60
83
|
end
|
61
84
|
|
62
85
|
# Defines an event of the system. This can take an optional hash that
|
63
|
-
# defines callbacks which will be invoked
|
64
|
-
# the
|
86
|
+
# defines callbacks which will be invoked before and after the event is
|
87
|
+
# invoked on the object.
|
65
88
|
#
|
66
89
|
# Configuration options:
|
67
|
-
# * +before+ -
|
68
|
-
# * +after+ -
|
69
|
-
#
|
70
|
-
# == Callback order
|
71
|
-
#
|
72
|
-
# These callbacks are invoked in the following order:
|
73
|
-
# 1. before
|
74
|
-
# 2. after
|
90
|
+
# * +before+ - One or more callbacks that will be invoked before the event has been fired
|
91
|
+
# * +after+ - One or more callbacks that will be invoked after the event has been fired
|
75
92
|
#
|
76
93
|
# == Instance methods
|
77
94
|
#
|
78
95
|
# The following instance methods are generated when a new event is defined
|
79
96
|
# (the "park" event is used as an example):
|
80
|
-
# * <tt>park(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional
|
81
|
-
# * <tt>park!(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional
|
97
|
+
# * <tt>park(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional list of arguments which are passed to the event callbacks.
|
98
|
+
# * <tt>park!(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional list of arguments which are passed to the event callbacks. If the transition cannot happen (for validation, database, etc. reasons), then an error will be raised
|
82
99
|
#
|
83
100
|
# == Defining transitions
|
84
101
|
#
|
@@ -96,6 +113,19 @@ module PluginAWeek #:nodoc:
|
|
96
113
|
# See PluginAWeek::StateMachine::Event#transition for more information on
|
97
114
|
# the possible options that can be passed in.
|
98
115
|
#
|
116
|
+
# *Note* that this block is executed within the context of the actual event
|
117
|
+
# object. As a result, you will not be able to reference any class methods
|
118
|
+
# on the model without referencing the class itself. For example,
|
119
|
+
#
|
120
|
+
# class Car < ActiveRecord::Base
|
121
|
+
# def self.safe_states
|
122
|
+
# %w(parked idling first_gear)
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# state_machine :state do
|
126
|
+
# event :park do
|
127
|
+
# transition :to
|
128
|
+
#
|
99
129
|
# == Example
|
100
130
|
#
|
101
131
|
# class Car < ActiveRecord::Base
|
@@ -110,6 +140,12 @@ module PluginAWeek #:nodoc:
|
|
110
140
|
name = name.to_s
|
111
141
|
event = events[name] = Event.new(self, name, options)
|
112
142
|
event.instance_eval(&block)
|
143
|
+
|
144
|
+
# Record the states
|
145
|
+
event.transitions.each do |transition|
|
146
|
+
@states |= ([transition.to_state] + transition.from_states)
|
147
|
+
end
|
148
|
+
|
113
149
|
event
|
114
150
|
end
|
115
151
|
|
@@ -7,51 +7,70 @@ module PluginAWeek #:nodoc:
|
|
7
7
|
# A transition indicates a state change and is described by a condition
|
8
8
|
# that would need to be fulfilled to enable the transition. Transitions
|
9
9
|
# consist of:
|
10
|
-
# * The starting state
|
10
|
+
# * The starting state(s)
|
11
11
|
# * The ending state
|
12
12
|
# * A guard to check if the transition is allowed
|
13
13
|
class Transition
|
14
|
-
# The state from which the transition is being made
|
15
|
-
attr_reader :from_state
|
16
|
-
|
17
14
|
# The state to which the transition is being made
|
18
15
|
attr_reader :to_state
|
19
16
|
|
17
|
+
# The states from which the transition can be made
|
18
|
+
attr_reader :from_states
|
19
|
+
|
20
20
|
# The event that caused the transition
|
21
21
|
attr_reader :event
|
22
22
|
|
23
23
|
delegate :machine,
|
24
24
|
:to => :event
|
25
25
|
|
26
|
-
|
26
|
+
# Creates a new transition within the context of the given event.
|
27
|
+
#
|
28
|
+
# Configuration options:
|
29
|
+
# * +to+ - The state being transitioned to
|
30
|
+
# * +from+ - One or more states being transitioned from. Default is nil (can transition from any state)
|
31
|
+
# * +except_from+ - One or more states that *can't* be transitioned from.
|
32
|
+
def initialize(event, options) #:nodoc:
|
27
33
|
@event = event
|
28
|
-
|
29
|
-
|
30
|
-
|
34
|
+
|
35
|
+
options.assert_valid_keys(:to, :from, :except_from)
|
36
|
+
raise ArgumentError, ':to state must be specified' unless options.include?(:to)
|
37
|
+
|
38
|
+
# Get the states involved in the transition
|
39
|
+
@to_state = options[:to]
|
40
|
+
@from_states = Array(options[:from] || options[:except_from])
|
41
|
+
|
42
|
+
# Should we be matching the from states?
|
43
|
+
@require_match = !options[:from].nil?
|
31
44
|
end
|
32
45
|
|
33
46
|
# Whether or not this is a loopback transition (i.e. from and to state are the same)
|
34
|
-
def loopback?(
|
35
|
-
|
47
|
+
def loopback?(from_state)
|
48
|
+
from_state == to_state
|
36
49
|
end
|
37
50
|
|
38
51
|
# Determines whether or not this transition can be performed on the given
|
39
|
-
#
|
52
|
+
# record. The transition can be performed if the record's state matches
|
53
|
+
# one of the states that are valid in this transition.
|
40
54
|
def can_perform_on?(record)
|
41
|
-
|
55
|
+
from_states.empty? || from_states.include?(record.send(machine.attribute)) == @require_match
|
42
56
|
end
|
43
57
|
|
44
58
|
# Runs the actual transition and any callbacks associated with entering
|
45
|
-
# and exiting the states
|
59
|
+
# and exiting the states. Any additional arguments are passed to the
|
60
|
+
# callbacks.
|
61
|
+
#
|
62
|
+
# *Note* that the caller should check <tt>can_perform_on?</tt> before calling
|
63
|
+
# perform. This will *not* check whether transition should be performed.
|
46
64
|
def perform(record, *args)
|
47
65
|
perform_with_optional_bang(record, false, *args)
|
48
66
|
end
|
49
67
|
|
50
68
|
# Runs the actual transition and any callbacks associated with entering
|
51
69
|
# and exiting the states. Any errors during validation or saving will be
|
52
|
-
# raised.
|
70
|
+
# raised. If any +before+ callbacks fail, a PluginAWeek::StateMachine::InvalidTransition
|
71
|
+
# error will be raised.
|
53
72
|
def perform!(record, *args)
|
54
|
-
perform_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition)
|
73
|
+
perform_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition, "Cannot transition via :#{event.name} from #{record.send(machine.attribute).inspect} to #{to_state.inspect}")
|
55
74
|
end
|
56
75
|
|
57
76
|
private
|
@@ -0,0 +1,8 @@
|
|
1
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
2
|
+
libs = " -r irb/completion"
|
3
|
+
libs << " -r test/app_root/script/rails_framework_root"
|
4
|
+
libs << " -r test/test_helper"
|
5
|
+
libs << " -r plugin_test_helper/console_with_fixtures"
|
6
|
+
libs << " -r console_app"
|
7
|
+
libs << " -r console_with_helpers"
|
8
|
+
exec "#{irb} #{libs} --simple-prompt"
|
@@ -0,0 +1 @@
|
|
1
|
+
RAILS_FRAMEWORK_ROOT = '/home/aaron/Projects/Vendor/rails'
|
data/test/factory.rb
CHANGED
@@ -37,12 +37,7 @@ module Factory
|
|
37
37
|
end
|
38
38
|
|
39
39
|
build Car do |attributes|
|
40
|
-
attributes
|
41
|
-
attributes[:auto_shop] = create_auto_shop unless attributes.include?(:auto_shop)
|
42
|
-
attributes.reverse_merge!(
|
43
|
-
:seatbelt_on => false,
|
44
|
-
:insurance_premium => 50
|
45
|
-
)
|
40
|
+
valid_vehicle_attributes(attributes)
|
46
41
|
end
|
47
42
|
|
48
43
|
build Highway do |attributes|
|
@@ -62,6 +57,11 @@ module Factory
|
|
62
57
|
end
|
63
58
|
|
64
59
|
build Vehicle do |attributes|
|
65
|
-
|
60
|
+
attributes[:highway] = create_highway unless attributes.include?(:highway)
|
61
|
+
attributes[:auto_shop] = create_auto_shop unless attributes.include?(:auto_shop)
|
62
|
+
attributes.reverse_merge!(
|
63
|
+
:seatbelt_on => false,
|
64
|
+
:insurance_premium => 50
|
65
|
+
)
|
66
66
|
end
|
67
67
|
end
|
data/test/unit/event_test.rb
CHANGED
@@ -14,6 +14,10 @@ class EventTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal 'turn_on', @event.name
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_should_not_have_any_transitions
|
18
|
+
assert @event.transitions.empty?
|
19
|
+
end
|
20
|
+
|
17
21
|
def test_should_define_an_event_action_on_the_owner_class
|
18
22
|
switch = new_switch
|
19
23
|
assert switch.respond_to?(:turn_on)
|
@@ -69,12 +73,21 @@ class EventWithTransitionsTest < Test::Unit::TestCase
|
|
69
73
|
assert_nothing_raised {@event.transition(:to => 'on')}
|
70
74
|
end
|
71
75
|
|
76
|
+
def test_should_allow_transitioning_without_a_state
|
77
|
+
assert @event.transition(:to => 'on')
|
78
|
+
end
|
79
|
+
|
72
80
|
def test_should_allow_transitioning_from_a_single_state
|
73
|
-
|
81
|
+
assert @event.transition(:to => 'on', :from => 'off')
|
74
82
|
end
|
75
83
|
|
76
84
|
def test_should_allow_transitioning_from_multiple_states
|
77
|
-
|
85
|
+
assert @event.transition(:to => 'on', :from => %w(off on))
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_have_transitions
|
89
|
+
@event.transition(:to => 'on')
|
90
|
+
assert @event.transitions.any?
|
78
91
|
end
|
79
92
|
|
80
93
|
def teardown
|
@@ -124,12 +137,24 @@ class EventAfterBeingFiredWithTransitionsTest < Test::Unit::TestCase
|
|
124
137
|
assert_equal 'off', @switch.state
|
125
138
|
end
|
126
139
|
|
127
|
-
def
|
140
|
+
def test_should_fire_if_transition_with_no_from_state_is_matched
|
141
|
+
@event.transition :to => 'on'
|
142
|
+
assert @event.fire(@switch)
|
143
|
+
assert_equal 'on', @switch.state
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_should_fire_if_transition_with_from_state_is_matched
|
128
147
|
@event.transition :to => 'on', :from => 'off'
|
129
148
|
assert @event.fire(@switch)
|
130
149
|
assert_equal 'on', @switch.state
|
131
150
|
end
|
132
151
|
|
152
|
+
def test_should_fire_if_transition_with_multiple_from_states_is_matched
|
153
|
+
@event.transition :to => 'on', :from => %w(off on)
|
154
|
+
assert @event.fire(@switch)
|
155
|
+
assert_equal 'on', @switch.state
|
156
|
+
end
|
157
|
+
|
133
158
|
def test_should_not_fire_if_validation_failed
|
134
159
|
@event.transition :to => 'on', :from => 'off'
|
135
160
|
@switch.fail_validation = true
|
@@ -178,36 +203,59 @@ class EventAfterBeingFiredWithConditionalTransitionsTest < Test::Unit::TestCase
|
|
178
203
|
def setup
|
179
204
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
180
205
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
181
|
-
|
182
|
-
# Verify that the callbacks are being invoked correctly; should not affect
|
183
|
-
# callback chain in tests
|
184
|
-
@event.transition :to => 'if_not_evaluated', :from => 'off', :if => Proc.new {false}
|
185
|
-
@event.transition :to => 'unless_not_evaluated', :from => 'off', :unless => Proc.new {true}
|
186
|
-
@event.transition :to => 'no_record', :from => 'off', :if => Proc.new {|record, value| record.nil?}
|
187
|
-
@event.transition :to => 'no_arguments', :from => 'off', :if => Proc.new {|record, value| value.nil?}
|
188
|
-
|
189
206
|
@switch = create_switch(:state => 'off')
|
190
207
|
end
|
191
208
|
|
192
|
-
def
|
193
|
-
|
194
|
-
|
209
|
+
def test_should_fire_if_if_is_true
|
210
|
+
@event.transition :to => 'on', :from => 'off', :if => Proc.new {true}
|
211
|
+
assert @event.fire(@switch)
|
195
212
|
end
|
196
213
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
214
|
+
def test_should_not_fire_if_if_is_false
|
215
|
+
@event.transition :to => 'on', :from => 'off', :if => Proc.new {false}
|
216
|
+
assert !@event.fire(@switch)
|
200
217
|
end
|
201
218
|
|
202
|
-
def
|
219
|
+
def test_should_fire_if_unless_is_false
|
220
|
+
@event.transition :to => 'on', :from => 'off', :unless => Proc.new {false}
|
221
|
+
assert @event.fire(@switch)
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_should_not_fire_if_unless_is_true
|
225
|
+
@event.transition :to => 'on', :from => 'off', :unless => Proc.new {true}
|
226
|
+
assert !@event.fire(@switch)
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_should_pass_in_record_as_argument
|
230
|
+
@event.transition :to => 'on', :from => 'off', :if => Proc.new {|record, value| !record.nil?}
|
231
|
+
assert @event.fire(@switch)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_should_pass_in_value_as_argument
|
203
235
|
@event.transition :to => 'on', :from => 'off', :if => Proc.new {|record, value| value == 1}
|
204
236
|
assert @event.fire(@switch, 1)
|
205
|
-
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_should_fire_if_method_evaluates_to_true
|
240
|
+
@switch.data = true
|
241
|
+
@event.transition :to => 'on', :from => 'off', :if => :data
|
242
|
+
assert @event.fire(@switch)
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_should_not_fire_if_method_evaluates_to_false
|
246
|
+
@switch.data = false
|
247
|
+
@event.transition :to => 'on', :from => 'off', :if => :data
|
248
|
+
assert !@event.fire(@switch)
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_should_raise_exception_if_no_transitions_are_matched
|
252
|
+
assert_raise(PluginAWeek::StateMachine::InvalidTransition) {@event.fire!(@switch, 1)}
|
253
|
+
assert_equal 'off', @switch.state
|
206
254
|
end
|
207
255
|
|
208
256
|
def test_should_not_raise_exception_if_transition_is_matched
|
209
|
-
@event.transition :to => 'on', :from => 'off', :if => Proc.new {
|
210
|
-
assert @event.fire!(@switch
|
257
|
+
@event.transition :to => 'on', :from => 'off', :if => Proc.new {true}
|
258
|
+
assert @event.fire!(@switch)
|
211
259
|
assert_equal 'on', @switch.state
|
212
260
|
end
|
213
261
|
|
data/test/unit/machine_test.rb
CHANGED
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
|
3
3
|
class MachineByDefaultTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
@machine = PluginAWeek::StateMachine::Machine.new(Switch
|
5
|
+
@machine = PluginAWeek::StateMachine::Machine.new(Switch)
|
6
6
|
end
|
7
7
|
|
8
8
|
def test_should_have_an_attribute
|
@@ -20,6 +20,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
|
|
20
20
|
def test_should_not_have_any_events
|
21
21
|
assert @machine.events.empty?
|
22
22
|
end
|
23
|
+
|
24
|
+
def test_should_not_have_any_states
|
25
|
+
assert @machine.states.empty?
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
class MachineWithInvalidOptionsTest < Test::Unit::TestCase
|
@@ -121,9 +125,27 @@ class MachineWithEventsTest < Test::Unit::TestCase
|
|
121
125
|
assert responded
|
122
126
|
end
|
123
127
|
|
124
|
-
def
|
128
|
+
def test_should_have_events
|
125
129
|
@machine.event(:turn_on) {}
|
126
|
-
assert_equal
|
130
|
+
assert_equal %w(turn_on), @machine.events.keys
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
|
135
|
+
def setup
|
136
|
+
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
|
137
|
+
@machine.event(:turn_on) do
|
138
|
+
transition :to => 'on', :from => 'off'
|
139
|
+
transition :to => 'error', :from => 'unknown'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_should_have_events
|
144
|
+
assert_equal %w(turn_on), @machine.events.keys
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_should_have_states
|
148
|
+
assert_equal %w(on off error unknown), @machine.states
|
127
149
|
end
|
128
150
|
end
|
129
151
|
|
@@ -4,34 +4,39 @@ class TransitionTest < Test::Unit::TestCase
|
|
4
4
|
def setup
|
5
5
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
6
6
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
7
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event,
|
7
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on')
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def test_should_not_have_any_from_states
|
11
|
+
assert @transition.from_states.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_not_be_a_loopback_if_from_state_is_different
|
15
|
+
assert !@transition.loopback?('off')
|
12
16
|
end
|
13
17
|
|
14
18
|
def test_should_have_a_to_state
|
15
19
|
assert_equal 'on', @transition.to_state
|
16
20
|
end
|
17
21
|
|
18
|
-
def
|
19
|
-
assert
|
22
|
+
def test_should_be_loopback_if_from_state_is_same
|
23
|
+
assert @transition.loopback?('on')
|
20
24
|
end
|
21
25
|
|
22
|
-
def
|
23
|
-
record = new_switch(:state => 'on')
|
24
|
-
assert !@transition.can_perform_on?(record)
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_should_be_able_to_perform_if_record_state_is_from_state
|
26
|
+
def test_should_be_able_to_perform_on_all_states
|
28
27
|
record = new_switch(:state => 'off')
|
29
28
|
assert @transition.can_perform_on?(record)
|
29
|
+
|
30
|
+
record = new_switch(:state => 'on')
|
31
|
+
assert @transition.can_perform_on?(record)
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def test_should_perform_for_all_states
|
33
35
|
record = new_switch(:state => 'off')
|
34
36
|
assert @transition.perform(record)
|
37
|
+
|
38
|
+
record = new_switch(:state => 'on')
|
39
|
+
assert @transition.perform(record)
|
35
40
|
end
|
36
41
|
|
37
42
|
def test_should_not_raise_exception_if_not_valid_during_perform
|
@@ -61,68 +66,131 @@ class TransitionTest < Test::Unit::TestCase
|
|
61
66
|
|
62
67
|
assert_raise(ActiveRecord::RecordNotSaved) {@transition.perform!(record)}
|
63
68
|
end
|
69
|
+
|
70
|
+
def test_should_raise_exception_if_invalid_option_specified
|
71
|
+
assert_raise(ArgumentError) {PluginAWeek::StateMachine::Transition.new(@event, :invalid => true)}
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_raise_exception_if_to_option_not_specified
|
75
|
+
assert_raise(ArgumentError) {PluginAWeek::StateMachine::Transition.new(@event, :from => 'off')}
|
76
|
+
end
|
64
77
|
end
|
65
78
|
|
66
|
-
class
|
79
|
+
class TransitionWithLoopbackTest < Test::Unit::TestCase
|
67
80
|
def setup
|
68
81
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
69
82
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
70
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event,
|
83
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'on')
|
71
84
|
end
|
72
85
|
|
73
|
-
def
|
74
|
-
|
86
|
+
def test_should_be_able_to_perform
|
87
|
+
record = new_switch(:state => 'on')
|
88
|
+
assert @transition.can_perform_on?(record)
|
75
89
|
end
|
76
90
|
|
77
|
-
def
|
78
|
-
record = new_switch(:state => 'off')
|
79
|
-
assert @transition.can_perform_on?(record)
|
80
|
-
|
91
|
+
def test_should_perform_for_valid_from_state
|
81
92
|
record = new_switch(:state => 'on')
|
82
|
-
assert @transition.
|
93
|
+
assert @transition.perform(record)
|
83
94
|
end
|
84
95
|
end
|
85
96
|
|
86
|
-
class
|
97
|
+
class TransitionWithFromStateTest < Test::Unit::TestCase
|
87
98
|
def setup
|
88
99
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
89
100
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
90
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event, 'on', '
|
101
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'off')
|
91
102
|
end
|
92
103
|
|
93
104
|
def test_should_have_a_from_state
|
94
|
-
assert_equal '
|
105
|
+
assert_equal ['off'], @transition.from_states
|
95
106
|
end
|
96
107
|
|
97
|
-
def
|
98
|
-
|
108
|
+
def test_should_not_be_able_to_perform_if_record_state_is_not_from_state
|
109
|
+
record = new_switch(:state => 'on')
|
110
|
+
assert !@transition.can_perform_on?(record)
|
99
111
|
end
|
100
112
|
|
101
|
-
def
|
102
|
-
|
113
|
+
def test_should_be_able_to_perform_if_record_state_is_from_state
|
114
|
+
record = new_switch(:state => 'off')
|
115
|
+
assert @transition.can_perform_on?(record)
|
103
116
|
end
|
104
117
|
|
105
|
-
def
|
118
|
+
def test_should_perform_for_valid_from_state
|
106
119
|
record = new_switch(:state => 'off')
|
120
|
+
assert @transition.perform(record)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class TransitionWithMultipleFromStatesTest < Test::Unit::TestCase
|
125
|
+
def setup
|
126
|
+
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
127
|
+
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
128
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => %w(off on))
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_should_have_multiple_from_states
|
132
|
+
assert_equal ['off', 'on'], @transition.from_states
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_should_not_be_able_to_perform_if_record_state_is_not_from_state
|
136
|
+
record = new_switch(:state => 'unknown')
|
107
137
|
assert !@transition.can_perform_on?(record)
|
108
138
|
end
|
109
139
|
|
110
|
-
def
|
140
|
+
def test_should_be_able_to_perform_if_record_state_is_any_from_state
|
141
|
+
record = new_switch(:state => 'off')
|
142
|
+
assert @transition.can_perform_on?(record)
|
143
|
+
|
111
144
|
record = new_switch(:state => 'on')
|
112
145
|
assert @transition.can_perform_on?(record)
|
113
146
|
end
|
114
147
|
|
115
|
-
def
|
148
|
+
def test_should_perform_for_any_valid_from_state
|
149
|
+
record = new_switch(:state => 'off')
|
150
|
+
assert @transition.perform(record)
|
151
|
+
|
116
152
|
record = new_switch(:state => 'on')
|
117
153
|
assert @transition.perform(record)
|
118
154
|
end
|
119
155
|
end
|
120
156
|
|
157
|
+
class TransitionWithMismatchedFromStatesRequiredTest < Test::Unit::TestCase
|
158
|
+
def setup
|
159
|
+
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
160
|
+
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
161
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :except_from => 'on')
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_should_have_a_from_state
|
165
|
+
assert_equal ['on'], @transition.from_states
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_should_be_able_to_perform_if_record_state_is_not_from_state
|
169
|
+
record = new_switch(:state => 'off')
|
170
|
+
assert @transition.can_perform_on?(record)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_should_not_be_able_to_perform_if_record_state_is_from_state
|
174
|
+
record = new_switch(:state => 'on')
|
175
|
+
assert !@transition.can_perform_on?(record)
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_should_perform_for_valid_from_state
|
179
|
+
record = new_switch(:state => 'off')
|
180
|
+
assert @transition.perform(record)
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_should_not_perform_for_invalid_from_state
|
184
|
+
record = new_switch(:state => 'on')
|
185
|
+
assert !@transition.can_perform_on?(record)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
121
189
|
class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
|
122
190
|
def setup
|
123
191
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
124
192
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
125
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event, '
|
193
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'off')
|
126
194
|
|
127
195
|
@record = create_switch(:state => 'off')
|
128
196
|
@transition.perform(@record)
|
@@ -142,7 +210,7 @@ class TransitionWithLoopbackAfterBeingPerformedTest < Test::Unit::TestCase
|
|
142
210
|
def setup
|
143
211
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
144
212
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
145
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event, 'on', 'on')
|
213
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'on')
|
146
214
|
|
147
215
|
@record = create_switch(:state => 'on')
|
148
216
|
@transition.perform(@record)
|
@@ -162,7 +230,7 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
|
|
162
230
|
def setup
|
163
231
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
164
232
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
165
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event, '
|
233
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'off')
|
166
234
|
@record = create_switch(:state => 'off')
|
167
235
|
|
168
236
|
Switch.define_callbacks :before_exit_state_off, :before_enter_state_on, :after_exit_state_off, :after_enter_state_on
|
@@ -295,7 +363,7 @@ class TransitionWithoutFromStateAndCallbacksTest < Test::Unit::TestCase
|
|
295
363
|
def setup
|
296
364
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
297
365
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
298
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event,
|
366
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on')
|
299
367
|
@record = create_switch(:state => 'off')
|
300
368
|
|
301
369
|
Switch.define_callbacks :before_exit_state_off, :before_enter_state_on, :after_exit_state_off, :after_enter_state_on
|
@@ -397,7 +465,7 @@ class TransitionWithLoopbackAndCallbacksTest < Test::Unit::TestCase
|
|
397
465
|
def setup
|
398
466
|
@machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
|
399
467
|
@event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
|
400
|
-
@transition = PluginAWeek::StateMachine::Transition.new(@event, 'on', 'on')
|
468
|
+
@transition = PluginAWeek::StateMachine::Transition.new(@event, :to => 'on', :from => 'on')
|
401
469
|
@record = create_switch(:state => 'on')
|
402
470
|
|
403
471
|
Switch.define_callbacks :before_exit_state_off, :before_enter_state_on, :after_exit_state_off, :after_enter_state_on
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Pfeifer
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-07-05 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -37,6 +37,9 @@ files:
|
|
37
37
|
- test/app_root/app/models/vehicle.rb
|
38
38
|
- test/app_root/app/models/car.rb
|
39
39
|
- test/app_root/app/models/auto_shop.rb
|
40
|
+
- test/app_root/script
|
41
|
+
- test/app_root/script/rails_framework_root.rb
|
42
|
+
- test/app_root/script/console
|
40
43
|
- test/app_root/db
|
41
44
|
- test/app_root/db/migrate
|
42
45
|
- test/app_root/db/migrate/002_create_auto_shops.rb
|