state-fu 0.11.1
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/LICENSE +40 -0
- data/README.textile +293 -0
- data/Rakefile +114 -0
- data/lib/binding.rb +292 -0
- data/lib/event.rb +192 -0
- data/lib/executioner.rb +120 -0
- data/lib/hooks.rb +39 -0
- data/lib/interface.rb +132 -0
- data/lib/lathe.rb +538 -0
- data/lib/machine.rb +184 -0
- data/lib/method_factory.rb +243 -0
- data/lib/persistence.rb +116 -0
- data/lib/persistence/active_record.rb +34 -0
- data/lib/persistence/attribute.rb +47 -0
- data/lib/persistence/base.rb +100 -0
- data/lib/persistence/relaxdb.rb +23 -0
- data/lib/persistence/session.rb +7 -0
- data/lib/sprocket.rb +58 -0
- data/lib/state-fu.rb +56 -0
- data/lib/state.rb +48 -0
- data/lib/support/active_support_lite/array.rb +9 -0
- data/lib/support/active_support_lite/array/access.rb +60 -0
- data/lib/support/active_support_lite/array/conversions.rb +202 -0
- data/lib/support/active_support_lite/array/extract_options.rb +21 -0
- data/lib/support/active_support_lite/array/grouping.rb +109 -0
- data/lib/support/active_support_lite/array/random_access.rb +13 -0
- data/lib/support/active_support_lite/array/wrapper.rb +25 -0
- data/lib/support/active_support_lite/blank.rb +67 -0
- data/lib/support/active_support_lite/cattr_reader.rb +57 -0
- data/lib/support/active_support_lite/keys.rb +57 -0
- data/lib/support/active_support_lite/misc.rb +59 -0
- data/lib/support/active_support_lite/module.rb +1 -0
- data/lib/support/active_support_lite/module/delegation.rb +130 -0
- data/lib/support/active_support_lite/object.rb +9 -0
- data/lib/support/active_support_lite/string.rb +38 -0
- data/lib/support/active_support_lite/symbol.rb +16 -0
- data/lib/support/applicable.rb +41 -0
- data/lib/support/arrays.rb +197 -0
- data/lib/support/core_ext.rb +90 -0
- data/lib/support/exceptions.rb +106 -0
- data/lib/support/has_options.rb +16 -0
- data/lib/support/logger.rb +165 -0
- data/lib/support/methodical.rb +17 -0
- data/lib/support/no_stdout.rb +55 -0
- data/lib/support/plotter.rb +62 -0
- data/lib/support/vizier.rb +300 -0
- data/lib/tasks/spec_last.rake +55 -0
- data/lib/tasks/state_fu.rake +57 -0
- data/lib/transition.rb +338 -0
- data/lib/transition_query.rb +224 -0
- data/spec/custom_formatter.rb +49 -0
- data/spec/features/binding_and_transition_helper_mixin_spec.rb +111 -0
- data/spec/features/method_missing_only_once_spec.rb +28 -0
- data/spec/features/not_requirements_spec.rb +118 -0
- data/spec/features/plotter_spec.rb +97 -0
- data/spec/features/shared_log_spec.rb +7 -0
- data/spec/features/singleton_machine_spec.rb +39 -0
- data/spec/features/state_and_array_options_accessor_spec.rb +47 -0
- data/spec/features/transition_boolean_comparison_spec.rb +101 -0
- data/spec/helper.rb +13 -0
- data/spec/integration/active_record_persistence_spec.rb +202 -0
- data/spec/integration/binding_extension_spec.rb +41 -0
- data/spec/integration/class_accessor_spec.rb +117 -0
- data/spec/integration/event_definition_spec.rb +74 -0
- data/spec/integration/example_01_document_spec.rb +133 -0
- data/spec/integration/example_02_string_spec.rb +88 -0
- data/spec/integration/instance_accessor_spec.rb +97 -0
- data/spec/integration/lathe_extension_spec.rb +67 -0
- data/spec/integration/machine_duplication_spec.rb +101 -0
- data/spec/integration/relaxdb_persistence_spec.rb +97 -0
- data/spec/integration/requirement_reflection_spec.rb +270 -0
- data/spec/integration/state_definition_spec.rb +163 -0
- data/spec/integration/transition_spec.rb +1033 -0
- data/spec/spec.opts +9 -0
- data/spec/spec_helper.rb +132 -0
- data/spec/state_fu_spec.rb +948 -0
- data/spec/units/binding_spec.rb +192 -0
- data/spec/units/event_spec.rb +214 -0
- data/spec/units/exceptions_spec.rb +82 -0
- data/spec/units/lathe_spec.rb +570 -0
- data/spec/units/machine_spec.rb +229 -0
- data/spec/units/method_factory_spec.rb +366 -0
- data/spec/units/sprocket_spec.rb +69 -0
- data/spec/units/state_spec.rb +59 -0
- metadata +171 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
2
|
+
|
3
|
+
## See state_and_event_common_spec.rb for behaviour shared between
|
4
|
+
## StateFu::State and StateFu::Event
|
5
|
+
##
|
6
|
+
|
7
|
+
describe StateFu::Machine do
|
8
|
+
include MySpecHelper
|
9
|
+
|
10
|
+
before do
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "class methods" do
|
14
|
+
before do
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "Machine.for_class" do
|
19
|
+
describe "when there's no machine defined for the class" do
|
20
|
+
before do
|
21
|
+
reset!
|
22
|
+
make_pristine_class 'Klass'
|
23
|
+
# mock( Klass ).machines() { {} }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should create a new machine and bind! it" do
|
27
|
+
@machine = Object.new
|
28
|
+
mock( @machine ).bind!( Klass, :moose, nil )
|
29
|
+
mock( StateFu::Machine ).new( {} ) { @machine }
|
30
|
+
StateFu::Machine.for_class( Klass, :moose )
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should apply the block (via lathe) if one is given" do
|
34
|
+
@m = StateFu::Machine.for_class( Klass, :snoo ) do
|
35
|
+
state :porpoise
|
36
|
+
end
|
37
|
+
# mock( Klass ).machines() { {} }
|
38
|
+
# @m.states.map(&:name).should == [:porpoise]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "attributes" do
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "instance methods" do
|
49
|
+
before do
|
50
|
+
reset!
|
51
|
+
make_pristine_class 'Klass'
|
52
|
+
@m = StateFu::Machine.new
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "helper" do
|
56
|
+
it "should add its arguments to the @@helpers array" do
|
57
|
+
module Foo; FOO = :foo; end
|
58
|
+
module Bar; BAR = :bar; end
|
59
|
+
@m.helper Foo, Bar
|
60
|
+
@m.helpers.should == [Foo, Bar]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".initialize" do
|
66
|
+
it "should apply options to the machine" do
|
67
|
+
@m = StateFu::Machine.new( :colour => "blue")
|
68
|
+
@m.options.should == {:colour => "blue" }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe ".apply!" do
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe ".bind!" do
|
77
|
+
it "should call StateFu::Machine.bind! with itself and its arguments" do
|
78
|
+
field_name = :my_field_name
|
79
|
+
mock( StateFu::Machine ).bind!( @m, Klass, :newname, field_name ) {}
|
80
|
+
@m.bind!( Klass, :newname, field_name )
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should generate a field name if none is given" do
|
84
|
+
klass = Klass
|
85
|
+
name = :StinkJuice
|
86
|
+
field_name = 'stink_juice_field'
|
87
|
+
@m.bind!( Klass, name )
|
88
|
+
Klass.state_fu_field_names[name].should == 'stink_juice_field'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".initial_state=" do
|
93
|
+
|
94
|
+
it "should set @initial_state given a String, Symbol or State for an existing state" do
|
95
|
+
state = StateFu::State.new( @m, :wizzle )
|
96
|
+
@m.states << state
|
97
|
+
@m.initial_state = state
|
98
|
+
@m.initial_state.should == state
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should create the state if it doesnt exist" do
|
102
|
+
@m.initial_state = :snoo
|
103
|
+
@m.initial_state.should be_kind_of( StateFu::State )
|
104
|
+
@m.initial_state.name.should == :snoo
|
105
|
+
@m.states.should include( @m.initial_state )
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should raise an ArgumentError given a number or an Array" do
|
109
|
+
lambda do @m.initial_state = 6
|
110
|
+
end.should raise_error( ArgumentError )
|
111
|
+
|
112
|
+
lambda do @m.initial_state = [:ping]
|
113
|
+
end.should raise_error( ArgumentError )
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
describe ".initial_state" do
|
119
|
+
it "should return nil if there are no states and initial_state= has not been called" do
|
120
|
+
@m.states.should == []
|
121
|
+
@m.initial_state.should == nil
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return the first state if one exists" do
|
125
|
+
stub( @m ).states() { [:a, :b, :c] }
|
126
|
+
@m.initial_state.should == :a
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
describe ".states" do
|
132
|
+
it "should return an array extended with StateFu::StateArray" do
|
133
|
+
@m.states.should be_kind_of( Array )
|
134
|
+
@m.states.extended_by.should include( StateFu::StateArray )
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe ".state_names" do
|
139
|
+
it "should return a list of symbols of state names" do
|
140
|
+
@m.states << StateFu::State.new( @m, :a )
|
141
|
+
@m.states << StateFu::State.new( @m, :b )
|
142
|
+
@m.state_names.should == [:a, :b ]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe ".events" do
|
147
|
+
it "should return an array extended with StateFu::EventArray" do
|
148
|
+
@m.events.should be_kind_of( Array )
|
149
|
+
@m.events.extended_by.should include( StateFu::EventArray )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe ".event_names" do
|
154
|
+
it "should return a list of symbols of event names" do
|
155
|
+
@m.events << StateFu::Event.new( @m, :a )
|
156
|
+
@m.events << StateFu::Event.new( @m, :b )
|
157
|
+
@m.event_names.should == [:a, :b ]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe ".find_or_create_states_by_name" do
|
162
|
+
describe "given an array of symbols" do
|
163
|
+
it "should return the states named by the symbols if they exist" do
|
164
|
+
a = StateFu::State.new( @m, :a )
|
165
|
+
b = StateFu::State.new( @m, :b )
|
166
|
+
@m.states << a
|
167
|
+
@m.states << b
|
168
|
+
@m.find_or_create_states_by_name( :a, :b ).should == [a, b]
|
169
|
+
@m.find_or_create_states_by_name( [:a, :b] ).should == [a, b]
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should return the states named by the symbols and create them if they don't exist" do
|
173
|
+
@m.states.should == []
|
174
|
+
res = @m.find_or_create_states_by_name( :a, :b )
|
175
|
+
res.should be_kind_of( Array )
|
176
|
+
res.length.should == 2
|
177
|
+
res.all? { |e| e.class == StateFu::State }.should be_true
|
178
|
+
res.map(&:name).should == [ :a, :b ]
|
179
|
+
@m.find_or_create_states_by_name( :a, :b ).should == res
|
180
|
+
end
|
181
|
+
end # arr symbols
|
182
|
+
|
183
|
+
describe "given an array of states" do
|
184
|
+
it "should return the states if they're in the machine's states array" do
|
185
|
+
a = StateFu::State.new( @m, :a )
|
186
|
+
b = StateFu::State.new( @m, :b )
|
187
|
+
@m.states << a
|
188
|
+
@m.states << b
|
189
|
+
@m.find_or_create_states_by_name( a, b ).should == [a, b]
|
190
|
+
@m.find_or_create_states_by_name( [a, b] ).should == [a, b]
|
191
|
+
@m.find_or_create_states_by_name( [[a, b]] ).should == [a, b]
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should add the states to the machine's states array if they're absent" do
|
195
|
+
a = StateFu::State.new( @m, :a )
|
196
|
+
b = StateFu::State.new( @m, :b )
|
197
|
+
@m.find_or_create_states_by_name( a, b ).should == [a, b]
|
198
|
+
@m.find_or_create_states_by_name( [a, b] ).should == [a, b]
|
199
|
+
@m.find_or_create_states_by_name( [[a, b]] ).should == [a, b]
|
200
|
+
end
|
201
|
+
end # arr states
|
202
|
+
end # find_or_create_states_by_name
|
203
|
+
|
204
|
+
describe "requirement_messages" do
|
205
|
+
it "should be a hash" do
|
206
|
+
@m.should respond_to(:requirement_messages)
|
207
|
+
@m.requirement_messages.should be_kind_of( Hash )
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should be empty by default" do
|
211
|
+
@m.requirement_messages.should be_empty
|
212
|
+
end
|
213
|
+
|
214
|
+
end # requirement_messages
|
215
|
+
|
216
|
+
describe "named_procs" do
|
217
|
+
it "should be a hash" do
|
218
|
+
@m.should respond_to(:named_procs)
|
219
|
+
@m.named_procs.should be_kind_of( Hash )
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should be empty by default" do
|
223
|
+
@m.named_procs.should be_empty
|
224
|
+
end
|
225
|
+
|
226
|
+
end # named_procs
|
227
|
+
|
228
|
+
end # instance methods
|
229
|
+
end
|
@@ -0,0 +1,366 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
2
|
+
|
3
|
+
describe StateFu::MethodFactory do
|
4
|
+
include MySpecHelper
|
5
|
+
|
6
|
+
# TODO - move to eg method_factory integration spec
|
7
|
+
describe "event_methods" do
|
8
|
+
|
9
|
+
before do
|
10
|
+
make_pristine_class('Klass')
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "defined on the stateful instance / object before state_fu has been called" do
|
14
|
+
|
15
|
+
before do
|
16
|
+
@machine = Klass.state_fu_machine do
|
17
|
+
event( :simple_event,
|
18
|
+
:from => { [:a, :b] => :targ } )
|
19
|
+
state( :a ) { cycle }
|
20
|
+
end # machine
|
21
|
+
@obj = Klass.new
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "when there is a method_missing already defined for the class" do
|
25
|
+
before do
|
26
|
+
reset!
|
27
|
+
make_pristine_class('Klass')
|
28
|
+
Klass.class_eval do
|
29
|
+
def method_missing method_name, *args
|
30
|
+
callme
|
31
|
+
end
|
32
|
+
end
|
33
|
+
Klass.state_fu_machine(){}
|
34
|
+
end
|
35
|
+
it "should call the original method_missing on an unexpected method call" do
|
36
|
+
@k = Klass.new
|
37
|
+
mock(@k).callme
|
38
|
+
@k.whut?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "event creation methods" do
|
43
|
+
it "should call method_missing" do
|
44
|
+
mock( @obj ).method_missing( :simple_event! )
|
45
|
+
@obj.simple_event!
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should call state_fu!" do
|
49
|
+
mock.proxy( StateFu::Binding ).new( Klass.state_fu_machine, @obj, StateFu::DEFAULT )
|
50
|
+
@obj
|
51
|
+
@obj.private_methods.map(&:to_sym).should include(:state_fu_field)
|
52
|
+
#@obj.should respond_to StateFu::DEFAULT_FIELD
|
53
|
+
@obj.state_fu.machine.events.should_not be_empty
|
54
|
+
@obj.simple_event!
|
55
|
+
|
56
|
+
# @obj.should_have_received( :state_fu! )
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not raise a NoMethodError" do
|
60
|
+
lambda { @obj.simple_event! }.should_not raise_error( NoMethodError )
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should call binding.fire!( :simple_event ... ) with any specified args" do
|
64
|
+
mock.instance_of( StateFu::Binding ).fire_transition!( is_a(StateFu::Event), is_a(StateFu::State), :aa, :bb, {:cc => "dd"} )
|
65
|
+
t = @obj.simple_event!( :aa, :bb, :cc => "dd" )
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should fire the transition" do
|
69
|
+
@obj.send(:state_fu_field).should == nil
|
70
|
+
t = @obj.simple_event!
|
71
|
+
t.should be_accepted
|
72
|
+
@obj.send(:state_fu_field).should == 'targ'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
describe "defined on the binding" do
|
79
|
+
describe "when the event is simple (has only one possible target)" do
|
80
|
+
before do
|
81
|
+
@machine = Klass.state_fu_machine do
|
82
|
+
event( :simple_event,
|
83
|
+
:from => { [:a, :b] => :targ } )
|
84
|
+
end # machine
|
85
|
+
@obj = Klass.new
|
86
|
+
@binding = @obj.state_fu
|
87
|
+
end # before
|
88
|
+
|
89
|
+
it "should be simple?" do
|
90
|
+
e = @machine.events[:simple_event]
|
91
|
+
e.origins.length.should == 2
|
92
|
+
e.targets.length.should == 1
|
93
|
+
e.should be_simple
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "method which returns an unfired transition" do
|
97
|
+
it "should have the same name as the event" do
|
98
|
+
@binding.should respond_to(:simple_event)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should return a new transition if called without any arguments" do
|
102
|
+
t = @binding.simple_event()
|
103
|
+
t.should be_kind_of( StateFu::Transition )
|
104
|
+
t.target.should == @machine.states[:targ]
|
105
|
+
t.event.should == @machine.events[:simple_event]
|
106
|
+
t.should_not be_fired
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should add any arguments / options it is called with to the transition" do
|
110
|
+
t = @binding.simple_event :a, :b, :c, {'d' => 'e'}
|
111
|
+
#t.should be_kind_of( StateFu::Transition )
|
112
|
+
#t.target.should == @machine.states[:targ]
|
113
|
+
#t.event.should == @machine.events[:simple_event]
|
114
|
+
t.args.should == [:a,:b,:c,{'d' => 'e'}]
|
115
|
+
t.options.should == {:d => 'e'}
|
116
|
+
end
|
117
|
+
end # transition builder
|
118
|
+
|
119
|
+
describe "method which tests if the event is can_transition?" do
|
120
|
+
it "should have the name of the event suffixed with ?" do
|
121
|
+
@binding.should respond_to(:can_simple_event?)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be true when the binding says it\'s can_transition?" do
|
125
|
+
@binding.can_transition?( :simple_event ).should == true
|
126
|
+
@binding.can_simple_event?.should == true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should be false when the binding says it\'s not can_transition?" do
|
130
|
+
mock( @binding ).can_transition?( is_a(StateFu::Event), is_a(StateFu::State) ) { false }
|
131
|
+
@binding.can_simple_event?.should == false
|
132
|
+
end
|
133
|
+
end # can_transition?
|
134
|
+
|
135
|
+
describe "bang (!) method which creates, fires and returns a transition" do
|
136
|
+
it "should have the name of the event suffixed with a bang (!)" do
|
137
|
+
@binding.should respond_to(:simple_event!)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should return a fired transition" do
|
141
|
+
t = @binding.simple_event!
|
142
|
+
t.should be_kind_of( StateFu::Transition )
|
143
|
+
t.should be_fired
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should pass any arguments to the transition as args / options" do
|
147
|
+
t = @binding.simple_event!( :a, :b, {'c' => :d } )
|
148
|
+
t.should be_kind_of( StateFu::Transition )
|
149
|
+
t.args.should == [:a, :b, {'c' => :d} ]
|
150
|
+
t.options.should == { :c => :d }
|
151
|
+
end
|
152
|
+
end # bang!
|
153
|
+
end # simple
|
154
|
+
|
155
|
+
describe "when the event is complex (has more than one possible target)" do
|
156
|
+
before do
|
157
|
+
@machine = Klass.state_fu_machine do
|
158
|
+
state :orphan
|
159
|
+
event( :complex_event,
|
160
|
+
:from => :home,
|
161
|
+
:to => [ :x, :y, :z ] )
|
162
|
+
initial_state :home
|
163
|
+
end # machine
|
164
|
+
@obj = Klass.new
|
165
|
+
@binding = @obj.state_fu
|
166
|
+
end # before
|
167
|
+
|
168
|
+
it "should not be simple?" do
|
169
|
+
e = @machine.events[:complex_event]
|
170
|
+
e.origins.length.should == 1
|
171
|
+
e.targets.length.should == 3
|
172
|
+
e.should_not be_simple
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "method which returns an unfired transition" do
|
176
|
+
it "should have the same name as the event" do
|
177
|
+
@binding.should respond_to(:complex_event)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should raise an error if called without any arguments" do
|
181
|
+
lambda { @binding.complex_event() }.should raise_error( ArgumentError )
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should raise an ArgumentError if called with a nonexistent target state" do
|
185
|
+
lambda { @binding.complex_event(:nonexistent) }.should raise_error( StateFu::UnknownTarget )
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should raise an IllegalTransition if called with an invalid target state" do
|
189
|
+
lambda { @binding.complex_event(:orphan) }.should raise_error( StateFu::IllegalTransition )
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should return a transition to the specified state if supplied a valid state" do
|
193
|
+
t = @binding.complex_event( :x )
|
194
|
+
t.should be_kind_of( StateFu::Transition )
|
195
|
+
t.target.name.should == :x
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should add any arguments / options it is called with to the transition" do
|
199
|
+
t = @binding.complex_event(:x,
|
200
|
+
:a, :b, :c, {'d' => 'e'})
|
201
|
+
t.should be_kind_of( StateFu::Transition )
|
202
|
+
t.args.should == [:a,:b,:c,{'d' =>'e'}]
|
203
|
+
t.options.should == {:d => 'e'}
|
204
|
+
end
|
205
|
+
end # transition builder
|
206
|
+
|
207
|
+
describe "method which tests if the event is can_transition?" do
|
208
|
+
it "should have the name of the event suffixed with ?" do
|
209
|
+
@binding.should respond_to(:can_complex_event?)
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should require a valid state name" do
|
213
|
+
lambda { @binding.can_complex_event?(:nonexistent) }.should raise_error( StateFu::UnknownTarget )
|
214
|
+
lambda { @binding.can_complex_event?(:orphan) }.should_not raise_error()
|
215
|
+
@binding.can_complex_event?(:orphan).should == false
|
216
|
+
lambda { @binding.can_complex_event?(:x) }.should_not raise_error
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should be true when the binding says the event is can_transition? " do
|
220
|
+
@binding.can_transition?( :complex_event, :x ).should == true
|
221
|
+
@binding.can_complex_event?(:x).should == true
|
222
|
+
end
|
223
|
+
end # can_transition?
|
224
|
+
|
225
|
+
describe "bang (!) method which creates, fires and returns a transition" do
|
226
|
+
it "should have the name of the event suffixed with a bang (!)" do
|
227
|
+
@binding.should respond_to(:complex_event!)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should require a valid state name" do
|
231
|
+
lambda { @binding.complex_event!(:nonexistent) }.should raise_error( StateFu::UnknownTarget )
|
232
|
+
lambda { @binding.complex_event!(:orphan) }.should raise_error( StateFu::IllegalTransition )
|
233
|
+
lambda { @binding.complex_event!(:x) }.should_not raise_error
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should return a fired transition given a valid state name" do
|
237
|
+
t = @binding.complex_event!( :x )
|
238
|
+
t.should be_kind_of( StateFu::Transition )
|
239
|
+
t.target.should == @machine.states[:x]
|
240
|
+
t.should be_fired
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should pass any arguments to the transition as args / options" do
|
244
|
+
t = @binding.complex_event!( :x,
|
245
|
+
:a, :b, {'c' => :d } )
|
246
|
+
t.should be_kind_of( StateFu::Transition )
|
247
|
+
t.target.should == @machine.states[:x]
|
248
|
+
t.args.should == [:a, :b,{'c' =>:d} ]
|
249
|
+
t.options.should == { :c => :d }
|
250
|
+
end
|
251
|
+
end # bang!
|
252
|
+
end # complex_event
|
253
|
+
|
254
|
+
# TODO move these to binding spec
|
255
|
+
describe "cycle and next_state methods" do
|
256
|
+
describe "when there is a valid transition available for cycle and next_state" do
|
257
|
+
before do
|
258
|
+
@machine = Klass.state_fu_machine do
|
259
|
+
initial_state :groundhog_day
|
260
|
+
|
261
|
+
state(:groundhog_day) do
|
262
|
+
cycle
|
263
|
+
end
|
264
|
+
|
265
|
+
event(:end_movie, :from => :groundhog_day, :to => :happy_ending)
|
266
|
+
end # machine
|
267
|
+
@obj = Klass.new
|
268
|
+
@binding = @obj.state_fu
|
269
|
+
end # before
|
270
|
+
|
271
|
+
describe "cycle methods:" do
|
272
|
+
describe "cycle" do
|
273
|
+
it "should return a transition for the cyclical event" do
|
274
|
+
t = @binding.cycle
|
275
|
+
t.should be_kind_of( StateFu::Transition )
|
276
|
+
t.origin.name.should == :groundhog_day
|
277
|
+
t.target.name.should == :groundhog_day
|
278
|
+
t.should_not be_fired
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "cycle?" do
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "cycle!" do
|
286
|
+
end
|
287
|
+
end # cycle
|
288
|
+
|
289
|
+
describe "next_state methods:" do
|
290
|
+
describe "next_state" do
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "next_state?" do
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "next_state!" do
|
297
|
+
end
|
298
|
+
end # next_state
|
299
|
+
end # with valid transitions
|
300
|
+
|
301
|
+
describe "when the machine is empty" do
|
302
|
+
before do
|
303
|
+
@machine = Klass.state_fu_machine() {}
|
304
|
+
@obj = Klass.new
|
305
|
+
@binding = @obj.state_fu
|
306
|
+
end
|
307
|
+
|
308
|
+
describe "current_state" do
|
309
|
+
it "should be nil" do
|
310
|
+
@binding.current_state.should == nil
|
311
|
+
end
|
312
|
+
end
|
313
|
+
describe "cycle methods:" do
|
314
|
+
describe "cycle" do
|
315
|
+
it "should return nil" do
|
316
|
+
@binding.cycle.should == nil
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
describe "cycle?" do
|
321
|
+
it "should return nil" do
|
322
|
+
@binding.cycle?.should == nil
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe "cycle!" do
|
327
|
+
it "should raise_error( TransitionNotFound )" do
|
328
|
+
lambda { @binding.cycle!.should == nil }.should raise_error( StateFu::TransitionNotFound )
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end # cycle
|
332
|
+
|
333
|
+
describe "next_state methods:" do
|
334
|
+
describe "next_state" do
|
335
|
+
it "should return nil" do
|
336
|
+
@binding.next_state.should == nil
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "next_state?" do
|
341
|
+
it "should return nil" do
|
342
|
+
pending
|
343
|
+
@binding.next_state?.should == nil
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe "next_state!" do
|
348
|
+
it "should raise_error( IllegalTransition )" do
|
349
|
+
lambda { @binding.next_state! }.should raise_error( StateFu::TransitionNotFound )
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end # next_state
|
353
|
+
|
354
|
+
end # empty machine
|
355
|
+
|
356
|
+
describe "when there is more than one candidate event / state" do
|
357
|
+
end # too many candidates
|
358
|
+
|
359
|
+
end # cycle & next_state
|
360
|
+
end # defined on binding
|
361
|
+
|
362
|
+
describe "methods defined on the object" do
|
363
|
+
end
|
364
|
+
|
365
|
+
end # event methods
|
366
|
+
end
|