davidlee-state-fu 0.11.0 → 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/lib/{state_fu/binding.rb → binding.rb} +31 -33
- data/lib/{state_fu/event.rb → event.rb} +12 -17
- data/lib/{state_fu/executioner.rb → executioner.rb} +8 -19
- data/lib/{state_fu/hooks.rb → hooks.rb} +13 -10
- data/lib/{state_fu/interface.rb → interface.rb} +0 -0
- data/lib/{state_fu/lathe.rb → lathe.rb} +12 -2
- data/lib/{state_fu/machine.rb → machine.rb} +0 -0
- data/lib/{state_fu/method_factory.rb → method_factory.rb} +0 -0
- data/lib/{state_fu/persistence.rb → persistence.rb} +0 -0
- data/lib/{state_fu/persistence → persistence}/active_record.rb +0 -0
- data/lib/{state_fu/persistence → persistence}/attribute.rb +0 -0
- data/lib/{state_fu/persistence → persistence}/base.rb +0 -0
- data/lib/{state_fu/persistence → persistence}/relaxdb.rb +0 -0
- data/lib/{state_fu/persistence → persistence}/session.rb +0 -0
- data/lib/{state_fu/sprocket.rb → sprocket.rb} +0 -0
- data/lib/state-fu.rb +11 -52
- data/lib/{state_fu/state.rb → state.rb} +0 -0
- data/lib/{state_fu → support}/active_support_lite/array.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/access.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/conversions.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/extract_options.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/grouping.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/random_access.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/array/wrapper.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/blank.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/cattr_reader.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/keys.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/misc.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/module.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/module/delegation.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/object.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/string.rb +0 -0
- data/lib/{state_fu → support}/active_support_lite/symbol.rb +0 -0
- data/lib/{state_fu → support}/applicable.rb +0 -0
- data/lib/{state_fu → support}/arrays.rb +0 -0
- data/lib/{state_fu → support}/core_ext.rb +1 -0
- data/lib/{state_fu → support}/exceptions.rb +0 -0
- data/lib/{state_fu → support}/has_options.rb +0 -0
- data/lib/{state_fu → support}/logger.rb +0 -0
- data/lib/{state_fu → support}/methodical.rb +0 -0
- data/lib/{no_stdout.rb → support/no_stdout.rb} +0 -0
- data/lib/{state_fu → support}/plotter.rb +1 -1
- data/lib/{vizier.rb → support/vizier.rb} +0 -0
- data/lib/{state_fu/transition.rb → transition.rb} +5 -4
- data/lib/{state_fu/transition_query.rb → transition_query.rb} +44 -67
- data/spec/integration/state_definition_spec.rb +0 -20
- data/spec/integration/transition_spec.rb +1 -1
- data/spec/state_fu_spec.rb +127 -127
- metadata +47 -48
- data/lib/state_fu.rb +0 -1
- data/lib/state_fu/nil_transition.rb +0 -49
|
@@ -14,6 +14,9 @@ module StateFu
|
|
|
14
14
|
result.each *a, &b
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
# calling result() will cause the set of transitions to be calculated -
|
|
18
|
+
# the cat will then be either dead or alive; until then it's a litte from
|
|
19
|
+
# column A, a little from column B.
|
|
17
20
|
def method_missing(method_name, *args, &block)
|
|
18
21
|
if result.respond_to?(method_name, true)
|
|
19
22
|
result.__send__(method_name, *args, &block)
|
|
@@ -21,46 +24,27 @@ module StateFu
|
|
|
21
24
|
super(method_name, *args, &block)
|
|
22
25
|
end
|
|
23
26
|
end
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
# def search(destination=nil, &block)
|
|
32
|
-
# # use the prepared event & target if none are supplied
|
|
33
|
-
# event, target = destination.nil? ? [options[:event], options[:target]] : parse_destination(destination)
|
|
34
|
-
# query = for_event(event).to(target)
|
|
35
|
-
# query.find || query.valid.singular || NilTransition.new
|
|
36
|
-
# end
|
|
37
|
-
|
|
38
|
-
# find a transition by event and optionally (optional if it can be inferred) target.
|
|
39
|
-
def find(destination=nil, &block)
|
|
40
|
-
# use the prepared event & target if none are supplied
|
|
41
|
-
event, target = destination.nil? ? [options[:event], options[:target]] : parse_destination(destination)
|
|
42
|
-
_args, _block = @args, @block
|
|
43
|
-
returning binding.new_transition(event, target) do |transition|
|
|
44
|
-
# return NilTransition.new if transition.nil?
|
|
45
|
-
transition.apply!(&_block) if _block
|
|
46
|
-
if _args
|
|
47
|
-
transition.args = _args
|
|
48
|
-
end
|
|
49
|
-
end
|
|
27
|
+
|
|
28
|
+
# prepare the query with arguments / block
|
|
29
|
+
# so that they can be applied to the transition once one is selected
|
|
30
|
+
def with(*args, &block)
|
|
31
|
+
@args = args
|
|
32
|
+
@block = block if block_given?
|
|
33
|
+
self
|
|
50
34
|
end
|
|
51
35
|
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
36
|
+
# build a list of possible transition destinations ([event, target])
|
|
37
|
+
# without actually constructing any transition objects
|
|
38
|
+
# def all_destinations
|
|
39
|
+
# binding.events.inject([]){ |arr, evt| arr += evt.targets.map{|tgt| [evt,tgt] }; arr}.uniq
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# def all_destination_names
|
|
43
|
+
# all_destinations.map {|tuple| tuple.map(&:to_sym) }
|
|
60
44
|
# end
|
|
61
45
|
|
|
62
46
|
#
|
|
63
|
-
#
|
|
47
|
+
# Chainable Filters
|
|
64
48
|
#
|
|
65
49
|
|
|
66
50
|
def cyclic
|
|
@@ -100,15 +84,26 @@ module StateFu
|
|
|
100
84
|
end
|
|
101
85
|
|
|
102
86
|
#
|
|
103
|
-
#
|
|
87
|
+
# Means to an outcome
|
|
104
88
|
#
|
|
105
89
|
|
|
106
|
-
|
|
90
|
+
# find a transition by event and optionally (optional if it can be inferred) target.
|
|
91
|
+
def find(destination=nil, &block)
|
|
92
|
+
# use the prepared event & target, and block, if none are supplied
|
|
93
|
+
event, target = destination.nil? ? [options[:event], options[:target]] : parse_destination(destination)
|
|
94
|
+
block ||= @block
|
|
95
|
+
returning Transition.new(binding, event, target, &block) do |transition|
|
|
96
|
+
if @args
|
|
97
|
+
transition.args = @args
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def singular
|
|
107
103
|
result.first if result.length == 1
|
|
108
104
|
end
|
|
109
|
-
alias_method :singular, :only_one
|
|
110
105
|
|
|
111
|
-
def
|
|
106
|
+
def singular?
|
|
112
107
|
!!singular
|
|
113
108
|
end
|
|
114
109
|
|
|
@@ -131,10 +126,6 @@ module StateFu
|
|
|
131
126
|
end
|
|
132
127
|
end
|
|
133
128
|
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
|
|
138
129
|
def events
|
|
139
130
|
map {|t| t.event }
|
|
140
131
|
end
|
|
@@ -143,30 +134,9 @@ module StateFu
|
|
|
143
134
|
map {|t| t.target }
|
|
144
135
|
end
|
|
145
136
|
|
|
146
|
-
def apply! # (&block
|
|
147
|
-
result.each { |t| t.apply &block if block }
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def with(*args, &block)
|
|
151
|
-
@args = args
|
|
152
|
-
@block = block if block_given?
|
|
153
|
-
self
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def all_destinations
|
|
157
|
-
binding.events.inject([]){ |arr, evt| arr += evt.targets.map{|tgt| [evt,tgt] }; arr}.uniq
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def all_destination_names
|
|
161
|
-
all_destinations.map {|tuple| tuple.map(&:to_sym) }
|
|
162
|
-
end
|
|
163
|
-
|
|
164
137
|
private
|
|
165
138
|
|
|
166
|
-
#
|
|
167
|
-
# Result
|
|
168
|
-
#
|
|
169
|
-
|
|
139
|
+
# extend result with this to provide a few conveniences
|
|
170
140
|
module Result
|
|
171
141
|
def states
|
|
172
142
|
map(&:target).uniq.extend StateArray
|
|
@@ -179,6 +149,11 @@ module StateFu
|
|
|
179
149
|
end
|
|
180
150
|
end # Result
|
|
181
151
|
|
|
152
|
+
# looks a little complex because of all the places that previously set
|
|
153
|
+
# options can filter the set of transitions - but all it's doing is
|
|
154
|
+
# looping over each event, and each event's possible targets, and building
|
|
155
|
+
# a list of transitions.
|
|
156
|
+
|
|
182
157
|
def result
|
|
183
158
|
@result = binding.events.select do |e|
|
|
184
159
|
case options[:cyclic]
|
|
@@ -193,18 +168,19 @@ module StateFu
|
|
|
193
168
|
next if options[:event] and event != options[:event]
|
|
194
169
|
returning [] do |ts|
|
|
195
170
|
|
|
196
|
-
# TODO hmm ... "sequences" ...
|
|
171
|
+
# TODO hmm ... "sequences" ... undecided on these. see Event / Lathe for more detail
|
|
197
172
|
if options[:sequences]
|
|
198
173
|
if target = event.target_for_origin(current_state)
|
|
199
174
|
ts << binding.transition([event,target], *args) unless options[:cyclic]
|
|
200
175
|
end
|
|
201
176
|
end
|
|
202
177
|
|
|
178
|
+
# build a list of transitions from the possible events and their targets
|
|
203
179
|
if event.targets
|
|
204
180
|
next unless event.target if options[:simple]
|
|
205
181
|
event.targets.flatten.each do |target|
|
|
206
182
|
next if options[:target] and target != options[:target]
|
|
207
|
-
t =
|
|
183
|
+
t = Transition.new(binding, event, target, *args)
|
|
208
184
|
ts << t if (t.valid? or !options[:valid])
|
|
209
185
|
end
|
|
210
186
|
end
|
|
@@ -245,3 +221,4 @@ module StateFu
|
|
|
245
221
|
|
|
246
222
|
end
|
|
247
223
|
end
|
|
224
|
+
|
|
@@ -161,23 +161,3 @@ describe "Adding states to a Machine" do
|
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
-
describe "adding events inside a state block" do
|
|
165
|
-
before do
|
|
166
|
-
@lambda = lambda{ Klass.state_fu_machine(){ state(:egg){ event(:hatch, :to => :chick) }}}
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it "should not throw an error" do
|
|
170
|
-
@lambda.should_not raise_error
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
describe "Klass.state_fu_machine(){ state(:egg){ event(:hatch, :to => :chick) }}}" do
|
|
174
|
-
before() do
|
|
175
|
-
Klass.state_fu_machine(){ state(:egg){ event(:hatch, :to => :chick) }}
|
|
176
|
-
end
|
|
177
|
-
it "should add an event :hatch to the machine" do
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
@@ -19,7 +19,7 @@ describe StateFu::Transition do
|
|
|
19
19
|
@abc = Alphabet.new
|
|
20
20
|
evt = Alphabet.machine.events[:a_to_b]
|
|
21
21
|
tgt = Alphabet.machine.states[:b]
|
|
22
|
-
@t = @abc.stfu
|
|
22
|
+
@t = StateFu::Transition.new(@abc.stfu, evt, tgt,
|
|
23
23
|
:a, :b, 'c' => 'cat')
|
|
24
24
|
end
|
|
25
25
|
|
data/spec/state_fu_spec.rb
CHANGED
|
@@ -10,17 +10,17 @@ describe "A door which opens and shuts:" do
|
|
|
10
10
|
# class Door
|
|
11
11
|
make_pristine_class('Door') do
|
|
12
12
|
include StateFu
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
attr_accessor :locked
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def shut
|
|
17
17
|
"I don't know how to shut!"
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def locked?
|
|
21
21
|
!!locked
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def method_missing(method_name, *args, &block)
|
|
25
25
|
raise NoMethodError.new("I'm just a door!" )
|
|
26
26
|
end
|
|
@@ -28,7 +28,7 @@ describe "A door which opens and shuts:" do
|
|
|
28
28
|
machine do
|
|
29
29
|
event :shut, :transitions_from => :open, :to => :closed
|
|
30
30
|
event :slam, :transitions_from => :open, :to => :closed
|
|
31
|
-
event :open, :transitions_from => :closed, :to => :open,
|
|
31
|
+
event :open, :transitions_from => :closed, :to => :open,
|
|
32
32
|
:requires => :not_locked?,
|
|
33
33
|
:message => "Sorry, it's locked."
|
|
34
34
|
end
|
|
@@ -51,7 +51,7 @@ describe "A door which opens and shuts:" do
|
|
|
51
51
|
it "have an initial state of :open, the first state defined" do
|
|
52
52
|
Door.machine.initial_state.name.should == :open
|
|
53
53
|
end
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
it "have a requirement :not_locked? for the :open event" do
|
|
56
56
|
Door.machine.events[:open].requirements.should == [:not_locked?]
|
|
57
57
|
end
|
|
@@ -76,9 +76,9 @@ describe "A door which opens and shuts:" do
|
|
|
76
76
|
before do
|
|
77
77
|
@door = Door.new
|
|
78
78
|
end
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
describe "magic event methods" do
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
it "doesn't normally have a method #shut!" do
|
|
83
83
|
@door.respond_to?(:shut!).should == false
|
|
84
84
|
end
|
|
@@ -86,11 +86,11 @@ describe "A door which opens and shuts:" do
|
|
|
86
86
|
it "will define #shut! when method_missing is called for the first time" do
|
|
87
87
|
begin
|
|
88
88
|
@door.play_the_ukelele
|
|
89
|
-
rescue NoMethodError
|
|
89
|
+
rescue NoMethodError
|
|
90
90
|
end
|
|
91
|
-
@door.respond_to?(:shut!).should == true
|
|
91
|
+
@door.respond_to?(:shut!).should == true
|
|
92
92
|
end
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
it "will keep any existing methods when method_missing is triggered" do
|
|
95
95
|
@door.respond_to?(:shut).should == true
|
|
96
96
|
@door.respond_to?(:can_shut?).should == false
|
|
@@ -100,7 +100,7 @@ describe "A door which opens and shuts:" do
|
|
|
100
100
|
@door.respond_to?(:can_shut?).should == true # new methods defined
|
|
101
101
|
@door.shut.should == "I don't know how to shut!" # old method retained
|
|
102
102
|
end
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
it "gets a set of new methods when any magic method is called" do
|
|
105
105
|
@door.respond_to?(:shut).should == true # already defined
|
|
106
106
|
@door.respond_to?(:open).should == false
|
|
@@ -109,36 +109,36 @@ describe "A door which opens and shuts:" do
|
|
|
109
109
|
@door.respond_to?(:shut!).should == false
|
|
110
110
|
@door.respond_to?(:open!).should == false
|
|
111
111
|
@door.can_shut?.should == true # call one of them (triggers method_missing)
|
|
112
|
-
@door.respond_to?(:open).should == false # a private method: Kernel#open
|
|
112
|
+
@door.respond_to?(:open).should == false # a private method: Kernel#open
|
|
113
113
|
@door.respond_to?(:can_shut?).should == true # but these are all newly defined public methods
|
|
114
|
-
@door.respond_to?(:can_open?).should == true
|
|
115
|
-
@door.respond_to?(:shut!).should == true
|
|
116
|
-
@door.respond_to?(:open!).should == true
|
|
114
|
+
@door.respond_to?(:can_open?).should == true
|
|
115
|
+
@door.respond_to?(:shut!).should == true
|
|
116
|
+
@door.respond_to?(:open!).should == true
|
|
117
117
|
end
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
it "retains any previously defined method_missing" do
|
|
120
120
|
begin
|
|
121
121
|
@door.hug_me
|
|
122
122
|
rescue NoMethodError => e
|
|
123
123
|
e.message.should == "I'm just a door!"
|
|
124
|
-
end
|
|
124
|
+
end
|
|
125
125
|
end
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
describe "for :slam - " do
|
|
128
128
|
describe "#slam" do
|
|
129
129
|
it "should return an unfired StateFu::Transition" do
|
|
130
130
|
t = @door.slam
|
|
131
131
|
t.should be_kind_of(StateFu::Transition)
|
|
132
132
|
t.fired?.should == false
|
|
133
|
-
end
|
|
133
|
+
end
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
describe "#can_slam?" do
|
|
137
137
|
it "should be true if the transition is valid for the current state" do
|
|
138
138
|
@door.current_state.should == :open
|
|
139
139
|
@door.can_slam?.should == true
|
|
140
|
-
end
|
|
141
|
-
|
|
140
|
+
end
|
|
141
|
+
|
|
142
142
|
it "should be false when the transition has unmet requirements" do
|
|
143
143
|
Door.machine.events[:slam].lathe do
|
|
144
144
|
requires :some_impossible_condition do
|
|
@@ -152,21 +152,21 @@ describe "A door which opens and shuts:" do
|
|
|
152
152
|
@door.shut!
|
|
153
153
|
@door.current_state.should == :closed
|
|
154
154
|
@door.can_slam?.should == nil
|
|
155
|
-
end
|
|
155
|
+
end
|
|
156
156
|
end
|
|
157
157
|
end
|
|
158
|
-
|
|
159
|
-
end # magic event methods
|
|
160
|
-
|
|
158
|
+
|
|
159
|
+
end # magic event methods
|
|
160
|
+
|
|
161
161
|
describe "magic state methods" do
|
|
162
162
|
it "should be defined for each state by method_missing voodoo" do
|
|
163
163
|
@door.should_not respond_to(:closed?)
|
|
164
164
|
@door.should_not respond_to(:open?)
|
|
165
165
|
@door.open?.should == true
|
|
166
166
|
@door.should respond_to(:closed?)
|
|
167
|
-
@door.should respond_to(:open?)
|
|
167
|
+
@door.should respond_to(:open?)
|
|
168
168
|
end
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
describe "for :closed - " do
|
|
171
171
|
describe "#closed?" do
|
|
172
172
|
it "should be true when the current_state is :closed" do
|
|
@@ -194,28 +194,28 @@ describe "A door which opens and shuts:" do
|
|
|
194
194
|
shut_result.should be_complete
|
|
195
195
|
@door.current_state.should == :closed
|
|
196
196
|
end
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
it "raises a StateFu::IllegalTransition if #shut! is called when already :closed" do
|
|
199
199
|
@door.current_state.should == :open
|
|
200
200
|
@door.shut!.should be_true
|
|
201
201
|
@door.current_state.should == :closed
|
|
202
|
-
lambda do
|
|
202
|
+
lambda do
|
|
203
203
|
t = @door.shut!
|
|
204
204
|
t.origin.should == :open
|
|
205
205
|
end.should raise_error(StateFu::IllegalTransition)
|
|
206
206
|
end
|
|
207
|
-
|
|
207
|
+
|
|
208
208
|
it "raises StateFu::RequirementError if #open! is called when it is locked" do
|
|
209
209
|
@door.shut!
|
|
210
210
|
@door.locked = true
|
|
211
211
|
lambda { @door.open! }.should raise_error(StateFu::RequirementError)
|
|
212
212
|
end
|
|
213
|
-
|
|
213
|
+
|
|
214
214
|
it "tells you why it won't open if you ask nicely" do
|
|
215
215
|
@door.shut!
|
|
216
216
|
@door.locked = true
|
|
217
217
|
@door.locked?.should be_true
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
transition = @door.state_fu.transition :open
|
|
220
220
|
transition.requirement_errors.should == {:not_locked? => "Sorry, it's locked."}
|
|
221
221
|
end
|
|
@@ -238,27 +238,27 @@ describe "A door which opens and shuts:" do
|
|
|
238
238
|
end
|
|
239
239
|
end
|
|
240
240
|
end
|
|
241
|
-
|
|
241
|
+
|
|
242
242
|
describe "Transition objects" do
|
|
243
|
-
|
|
244
|
-
# TODO refactor me
|
|
243
|
+
|
|
244
|
+
# TODO refactor me
|
|
245
245
|
def should_be_an_unfired_transition_with_the_event_slam_from_open_to_closed(transition)
|
|
246
246
|
transition.should be_kind_of(StateFu::Transition)
|
|
247
247
|
transition.fired?.should == false
|
|
248
248
|
transition.current_state.should == :open
|
|
249
249
|
transition.event.should == :slam
|
|
250
250
|
transition.origin.should == :open
|
|
251
|
-
transition.target.should == :closed
|
|
251
|
+
transition.target.should == :closed
|
|
252
252
|
end
|
|
253
|
-
|
|
254
|
-
it "returns a Transition on #slam" do
|
|
253
|
+
|
|
254
|
+
it "returns a Transition on #slam" do
|
|
255
255
|
@door.slam do |transition|
|
|
256
256
|
transition.should be_kind_of(StateFu::Transition)
|
|
257
257
|
transition.fired?.should == false
|
|
258
258
|
transition.current_state.should == :open
|
|
259
259
|
transition.event.should == :slam
|
|
260
260
|
transition.origin.should == :open
|
|
261
|
-
transition.target.should == :closed
|
|
261
|
+
transition.target.should == :closed
|
|
262
262
|
end
|
|
263
263
|
end
|
|
264
264
|
|
|
@@ -276,15 +276,15 @@ describe "A door which opens and shuts:" do
|
|
|
276
276
|
transition = @door.state_fu.transition [:slam, :closed]
|
|
277
277
|
should_be_an_unfired_transition_with_the_event_slam_from_open_to_closed( transition )
|
|
278
278
|
end
|
|
279
|
-
|
|
279
|
+
|
|
280
280
|
it "changes the door's state when you #fire! the transition" do
|
|
281
281
|
transition = @door.slam
|
|
282
282
|
transition.fire!
|
|
283
283
|
transition.fired?.should == true
|
|
284
284
|
transition.complete?.should == true
|
|
285
|
-
@door.current_state.should == :closed
|
|
285
|
+
@door.current_state.should == :closed
|
|
286
286
|
end
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
it "can tell you its #origin and #target states" do
|
|
289
289
|
transition = @door.state_fu.transition :shut
|
|
290
290
|
transition.origin.should be_kind_of(StateFu::State)
|
|
@@ -299,12 +299,12 @@ describe "A door which opens and shuts:" do
|
|
|
299
299
|
transition = @door.state_fu.transition :open
|
|
300
300
|
transition.valid?.should == false
|
|
301
301
|
transition.unmet_requirements.should == [:not_locked?]
|
|
302
|
-
transition.unmet_requirement_messages.should == ["Sorry, it's locked."]
|
|
302
|
+
transition.unmet_requirement_messages.should == ["Sorry, it's locked."]
|
|
303
303
|
transition.requirement_errors.should == {:not_locked? => "Sorry, it's locked."}
|
|
304
304
|
transition.first_unmet_requirement.should == :not_locked?
|
|
305
305
|
transition.first_unmet_requirement_message.should == "Sorry, it's locked."
|
|
306
306
|
end
|
|
307
|
-
end
|
|
307
|
+
end
|
|
308
308
|
|
|
309
309
|
# TODO save this for later ...............
|
|
310
310
|
describe "#state_fu_binding" do
|
|
@@ -323,14 +323,14 @@ describe "A door which opens and shuts:" do
|
|
|
323
323
|
it "have a list of #valid_transitions" do
|
|
324
324
|
@door.state_fu_binding.valid_transitions.should be_kind_of(StateFu::TransitionQuery)
|
|
325
325
|
@door.state_fu_binding.valid_transitions.length.should == 2
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
326
|
+
transition = @door.state_fu_binding.valid_transitions.first
|
|
327
|
+
transition.event.name.should == :shut
|
|
328
|
+
transition.origin.name.should == :open
|
|
329
|
+
transition.target.name.should == :closed
|
|
330
|
+
transition = @door.state_fu_binding.valid_transitions.last
|
|
331
|
+
transition.event.name.should == :slam
|
|
332
|
+
transition.origin.name.should == :open
|
|
333
|
+
transition.target.name.should == :closed
|
|
334
334
|
end
|
|
335
335
|
end
|
|
336
336
|
|
|
@@ -551,7 +551,7 @@ describe "arguments given to different method signatures" do
|
|
|
551
551
|
def b2(t,a=nil) received[:b2] = [t,a] end
|
|
552
552
|
def c2(t,*a) received[:c2] = [t,a] end
|
|
553
553
|
|
|
554
|
-
# these method signatures get a transition, a list of arguments,
|
|
554
|
+
# these method signatures get a transition, a list of arguments,
|
|
555
555
|
# and the object which owns the machine
|
|
556
556
|
def a3(t,a,o) received[:a3] = [t,a,o] end
|
|
557
557
|
def b3(t,a,o=nil) received[:b3] = [t,a,o] end
|
|
@@ -565,12 +565,12 @@ describe "arguments given to different method signatures" do
|
|
|
565
565
|
|
|
566
566
|
end
|
|
567
567
|
end # before
|
|
568
|
-
|
|
568
|
+
|
|
569
569
|
describe "the machine" do
|
|
570
570
|
it "have an event :observe which is a #cycle?" do
|
|
571
|
-
Recorder.machine.events[:observe].cycle?.should be_true
|
|
571
|
+
Recorder.machine.events[:observe].cycle?.should be_true
|
|
572
572
|
end
|
|
573
|
-
|
|
573
|
+
|
|
574
574
|
it "have a list of execute hooks" do
|
|
575
575
|
Recorder.machine.events[:observe].hooks[:execute].should == [:a1, :b1, :c1, :a2, :b2, :c2, :a3, :b3, :c3]
|
|
576
576
|
end
|
|
@@ -587,17 +587,17 @@ describe "arguments given to different method signatures" do
|
|
|
587
587
|
t.should be_kind_of(StateFu::Transition)
|
|
588
588
|
t.should be_complete
|
|
589
589
|
end
|
|
590
|
-
|
|
590
|
+
|
|
591
591
|
describe "observing method calls on #observe!" do
|
|
592
592
|
before do
|
|
593
593
|
@t = @recorder.observe!
|
|
594
594
|
@results = @recorder.received
|
|
595
595
|
end
|
|
596
|
-
|
|
596
|
+
|
|
597
597
|
it "call the event's :execute hooks on #observe!" do
|
|
598
598
|
@results.keys.should =~ [:a1, :b1, :c1, :a2, :b2, :c2, :a3, :b3, :c3]
|
|
599
599
|
end
|
|
600
|
-
|
|
600
|
+
|
|
601
601
|
describe "methods which expect one argument" do
|
|
602
602
|
it "receive a StateFu::Transition" do
|
|
603
603
|
@results[:a1].should == [@t]
|
|
@@ -618,7 +618,7 @@ describe "arguments given to different method signatures" do
|
|
|
618
618
|
it "receive a StateFu::Transition, an argument list and the recorder object" do
|
|
619
619
|
@results[:a3].should == [@t, @t.args, @recorder]
|
|
620
620
|
@results[:b3].should == [@t, @t.args, @recorder]
|
|
621
|
-
@results[:c3].should == [@t, @t.args, [@recorder]]
|
|
621
|
+
@results[:c3].should == [@t, @t.args, [@recorder]]
|
|
622
622
|
end
|
|
623
623
|
end
|
|
624
624
|
end
|
|
@@ -630,12 +630,12 @@ end
|
|
|
630
630
|
#
|
|
631
631
|
|
|
632
632
|
describe "sitting at a poker machine" do
|
|
633
|
-
|
|
633
|
+
|
|
634
634
|
before :all do
|
|
635
635
|
make_pristine_class('PokerMachine') do
|
|
636
|
-
|
|
636
|
+
|
|
637
637
|
attr_accessor :silly_noises_inflicted
|
|
638
|
-
|
|
638
|
+
|
|
639
639
|
def insert_coins n
|
|
640
640
|
@credits = n * PokerMachine::CREDITS_PER_COIN
|
|
641
641
|
end
|
|
@@ -644,32 +644,32 @@ describe "sitting at a poker machine" do
|
|
|
644
644
|
def refund_coins
|
|
645
645
|
(self.credits, x = 0, self.credits / PokerMachine::CREDITS_PER_COIN)[1]
|
|
646
646
|
end
|
|
647
|
-
|
|
647
|
+
|
|
648
648
|
def play_a_silly_noise
|
|
649
649
|
@silly_noises_inflicted << [:silly_noise]
|
|
650
650
|
end
|
|
651
|
-
|
|
652
|
-
# an array with the accessors (StateFu::Bindings)
|
|
651
|
+
|
|
652
|
+
# an array with the accessors (StateFu::Bindings)
|
|
653
653
|
# for each of the wheels' state machines, for convenience
|
|
654
654
|
def wheels
|
|
655
|
-
[wheel_one, wheel_two, wheel_three]
|
|
655
|
+
[wheel_one, wheel_two, wheel_three]
|
|
656
656
|
end
|
|
657
657
|
|
|
658
658
|
def wheels_spinning?
|
|
659
659
|
wheels.any?(&:spinning?)
|
|
660
660
|
end
|
|
661
|
-
|
|
661
|
+
|
|
662
662
|
def display
|
|
663
663
|
wheels.map(&:current_state_name)
|
|
664
664
|
end
|
|
665
|
-
|
|
665
|
+
|
|
666
666
|
def wait
|
|
667
667
|
while wheels_spinning?
|
|
668
|
-
spin_wheels!
|
|
669
|
-
end
|
|
670
|
-
stop_spinning!
|
|
668
|
+
spin_wheels!
|
|
669
|
+
end
|
|
670
|
+
stop_spinning!
|
|
671
671
|
end
|
|
672
|
-
|
|
672
|
+
|
|
673
673
|
PokerMachine::CREDITS_TO_PLAY = 5
|
|
674
674
|
PokerMachine::CREDITS_PER_COIN = 5
|
|
675
675
|
|
|
@@ -679,32 +679,32 @@ describe "sitting at a poker machine" do
|
|
|
679
679
|
@credits = 0
|
|
680
680
|
@silly_noises_inflicted = []
|
|
681
681
|
end
|
|
682
|
-
|
|
682
|
+
|
|
683
683
|
machine do
|
|
684
|
-
# adds a hook to the machine's global after slot
|
|
684
|
+
# adds a hook to the machine's global after slot
|
|
685
685
|
after_everything :play_a_silly_noise
|
|
686
|
-
|
|
686
|
+
|
|
687
687
|
# Define helper methods with 'proc' or its alias 'define'. This is
|
|
688
688
|
# implicit when you supply a block and a symbol for an event or state
|
|
689
|
-
# hook, a requirement, or a requirement failure message.
|
|
689
|
+
# hook, a requirement, or a requirement failure message.
|
|
690
690
|
#
|
|
691
691
|
# Named procs are "machine-local": they are available in any other
|
|
692
692
|
# block evaluated by StateFu for a given machine, but are not defined
|
|
693
693
|
# on the stateful class itself.
|
|
694
|
-
#
|
|
694
|
+
#
|
|
695
695
|
# Use them to extend the state machine DSL without cluttering up your
|
|
696
696
|
# classes themselves.
|
|
697
|
-
#
|
|
697
|
+
#
|
|
698
698
|
# If you want a method which spans multiple machines (eg 'wheels',
|
|
699
699
|
# above) or which is available to your object in any context, define
|
|
700
700
|
# it as a standard method. You will then be able to access it in any
|
|
701
701
|
# of your state machines.
|
|
702
|
-
named_proc(:wheel_states) { wheels.map(&:current_state) }
|
|
702
|
+
named_proc(:wheel_states) { wheels.map(&:current_state) }
|
|
703
703
|
named_proc(:wheels_stopped?) do
|
|
704
704
|
!wheels.any?(&:spinning?)
|
|
705
705
|
end
|
|
706
|
-
|
|
707
|
-
state :ready do
|
|
706
|
+
|
|
707
|
+
state :ready do
|
|
708
708
|
|
|
709
709
|
event :pull_lever, :transitions_to => :spinning do
|
|
710
710
|
# The execution context always provides handy access to all the
|
|
@@ -712,91 +712,91 @@ describe "sitting at a poker machine" do
|
|
|
712
712
|
# still be qualified.
|
|
713
713
|
requires(:enough_credits) { self.credits >= PokerMachine::CREDITS_TO_PLAY }
|
|
714
714
|
triggers(:deduct_credits) { self.credits -= PokerMachine::CREDITS_TO_PLAY }
|
|
715
|
-
triggers(:spin_wheels) { [wheel_one, wheel_two,wheel_three].each(&:start!) }
|
|
715
|
+
triggers(:spin_wheels) { [wheel_one, wheel_two,wheel_three].each(&:start!) }
|
|
716
716
|
# if we enable this line, the machine will #wait automatically
|
|
717
717
|
# so that merely pulling the lever causes it to return to the ready state:
|
|
718
718
|
#
|
|
719
|
-
# after :wait
|
|
720
|
-
end # :pull_lever event
|
|
719
|
+
# after :wait
|
|
720
|
+
end # :pull_lever event
|
|
721
721
|
end # :ready state
|
|
722
|
-
|
|
723
|
-
state :spinning do
|
|
722
|
+
|
|
723
|
+
state :spinning do
|
|
724
724
|
cycle :spin_wheels do
|
|
725
725
|
# executes after the transition has been accepted
|
|
726
726
|
after do
|
|
727
|
-
wheels.each do |wheel|
|
|
727
|
+
wheels.each do |wheel|
|
|
728
728
|
if wheel.spinning?
|
|
729
729
|
wheel.spin!
|
|
730
730
|
end
|
|
731
|
-
end
|
|
731
|
+
end
|
|
732
732
|
end # execute
|
|
733
733
|
end # :spinning state
|
|
734
734
|
|
|
735
|
-
event :stop_spinning, :to => :ready do
|
|
736
|
-
requires :wheels_stopped?
|
|
735
|
+
event :stop_spinning, :to => :ready do
|
|
736
|
+
requires :wheels_stopped?
|
|
737
737
|
execute :payout do
|
|
738
738
|
if wheel_states == wheel_states.uniq
|
|
739
739
|
self.credits += wheel_states.first[:value]
|
|
740
|
-
end
|
|
740
|
+
end
|
|
741
741
|
end
|
|
742
|
-
end # :stop_spinning event
|
|
743
|
-
end # spinning state
|
|
744
|
-
end # default machine
|
|
745
|
-
|
|
742
|
+
end # :stop_spinning event
|
|
743
|
+
end # spinning state
|
|
744
|
+
end # default machine
|
|
745
|
+
|
|
746
746
|
[:one, :two, :three].each do |wheel|
|
|
747
747
|
machine "wheel_#{wheel}" do
|
|
748
|
-
|
|
748
|
+
|
|
749
749
|
state :bomb, :value => -5
|
|
750
750
|
state :cherry, :value => 5
|
|
751
751
|
state :smiley, :value => 10
|
|
752
|
-
state :gold, :value => 15
|
|
752
|
+
state :gold, :value => 15
|
|
753
753
|
|
|
754
754
|
state :spinning do
|
|
755
755
|
cycle :spin do
|
|
756
756
|
execute do
|
|
757
|
-
silly_noises_inflicted << :spinning_noise
|
|
757
|
+
silly_noises_inflicted << :spinning_noise
|
|
758
758
|
end
|
|
759
759
|
after do
|
|
760
|
-
if rand(3) == 0
|
|
760
|
+
if rand(3) == 0
|
|
761
761
|
# we use binding.stop! rather than self.stop! here
|
|
762
762
|
# to disambiguate which machine we're sending the event to.
|
|
763
763
|
#
|
|
764
764
|
# .binding yields a StateFu::Binding, which has all the same
|
|
765
|
-
# magic methods as @pokie, but is explicitly for one machine,
|
|
765
|
+
# magic methods as @pokie, but is explicitly for one machine,
|
|
766
766
|
# and one @pokie.
|
|
767
|
-
#
|
|
768
|
-
# @pokie.stop! would always cause the same wheel to stop
|
|
769
|
-
# (the first one, becuase it was defined first, and automatically
|
|
767
|
+
#
|
|
768
|
+
# @pokie.stop! would always cause the same wheel to stop
|
|
769
|
+
# (the first one, becuase it was defined first, and automatically
|
|
770
770
|
# defined methods never clobber any pre-existing methods) -
|
|
771
771
|
# which isn't what we want here.
|
|
772
|
-
binding.stop!([:bomb, :cherry, :smiley, :gold].rand)
|
|
772
|
+
binding.stop!([:bomb, :cherry, :smiley, :gold].rand)
|
|
773
773
|
end
|
|
774
774
|
end
|
|
775
775
|
end
|
|
776
776
|
end
|
|
777
|
-
|
|
777
|
+
|
|
778
778
|
initial_state states.except(:spinning).rand
|
|
779
|
-
|
|
779
|
+
|
|
780
780
|
event :start, :from => states.except(:spinning), :to => :spinning
|
|
781
781
|
event :stop, :from => :spinning, :to => states.except(:spinning)
|
|
782
|
-
|
|
782
|
+
|
|
783
783
|
end # machine :cell_#{cell}
|
|
784
784
|
end # each cell
|
|
785
|
-
end # PokerMachine
|
|
786
|
-
end # before
|
|
787
|
-
|
|
785
|
+
end # PokerMachine
|
|
786
|
+
end # before
|
|
787
|
+
|
|
788
788
|
describe "the state machine" do
|
|
789
789
|
end
|
|
790
|
-
|
|
790
|
+
|
|
791
791
|
before :each do
|
|
792
792
|
@pokie = PokerMachine.new
|
|
793
793
|
end
|
|
794
|
-
|
|
794
|
+
|
|
795
795
|
# just a sanity check for method_missing
|
|
796
796
|
it "doesn't talk to you" do
|
|
797
797
|
lambda { @pokie.talk_to_me }.should raise_error(NoMethodError)
|
|
798
798
|
end
|
|
799
|
-
|
|
799
|
+
|
|
800
800
|
it "you need credits to pull the lever" do
|
|
801
801
|
@pokie.state_fu!
|
|
802
802
|
@pokie.credits.should == 0
|
|
@@ -808,13 +808,13 @@ describe "sitting at a poker machine" do
|
|
|
808
808
|
it "has three wheels" do
|
|
809
809
|
@pokie.wheels.length.should == 3
|
|
810
810
|
end
|
|
811
|
-
|
|
811
|
+
|
|
812
812
|
it "displays three icons" do
|
|
813
813
|
@pokie.display.should be_kind_of(Array)
|
|
814
814
|
@pokie.display.map(&:class).should == [Symbol, Symbol, Symbol]
|
|
815
815
|
(@pokie.display - [:bomb, :cherry, :smiley, :gold]).should be_empty
|
|
816
816
|
end
|
|
817
|
-
|
|
817
|
+
|
|
818
818
|
describe "putting in 20 coins" do
|
|
819
819
|
before do
|
|
820
820
|
@pokie.insert_coins(20)
|
|
@@ -823,7 +823,7 @@ describe "sitting at a poker machine" do
|
|
|
823
823
|
it "gives you 100 credits" do
|
|
824
824
|
@pokie.credits.should == 100
|
|
825
825
|
end
|
|
826
|
-
|
|
826
|
+
|
|
827
827
|
describe "then pulling the lever" do
|
|
828
828
|
|
|
829
829
|
it "spins the icons" do
|
|
@@ -840,27 +840,27 @@ describe "sitting at a poker machine" do
|
|
|
840
840
|
it "makes a silly noise" do
|
|
841
841
|
lambda { @pokie.pull_lever! }.should change(@pokie.silly_noises_inflicted, :length)
|
|
842
842
|
end
|
|
843
|
-
|
|
843
|
+
|
|
844
844
|
it "wont let you pull it again while it's still spinning" do
|
|
845
845
|
@pokie.pull_lever!
|
|
846
846
|
@pokie.spinning?.should be_true
|
|
847
847
|
@pokie.can_pull_lever?.should == nil
|
|
848
848
|
lambda{ @pokie.pull_lever! }.should raise_error(StateFu::IllegalTransition)
|
|
849
|
-
end
|
|
850
|
-
|
|
849
|
+
end
|
|
850
|
+
|
|
851
851
|
it "makes a spinning sound while you wait" do
|
|
852
852
|
@pokie.pull_lever!
|
|
853
853
|
noises_before = @pokie.silly_noises_inflicted
|
|
854
854
|
@pokie.wait
|
|
855
855
|
(@pokie.silly_noises_inflicted).should include(:spinning_noise)
|
|
856
856
|
end
|
|
857
|
-
|
|
857
|
+
|
|
858
858
|
it "it stops spinning after a little #wait" do
|
|
859
|
-
@pokie.pull_lever!
|
|
859
|
+
@pokie.pull_lever!
|
|
860
860
|
@pokie.wait
|
|
861
861
|
@pokie.spinning?.should be_false
|
|
862
862
|
end
|
|
863
|
-
|
|
863
|
+
|
|
864
864
|
it "gives you more credits if all the icons are the same" do
|
|
865
865
|
@pokie.pull_lever!
|
|
866
866
|
@pokie.wheel_one.stop! :smiley
|
|
@@ -868,9 +868,9 @@ describe "sitting at a poker machine" do
|
|
|
868
868
|
@pokie.wheel_three.stop! :smiley
|
|
869
869
|
@pokie.wait
|
|
870
870
|
@pokie.credits.should == 105
|
|
871
|
-
end
|
|
872
|
-
end
|
|
873
|
-
end
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
874
|
end
|
|
875
875
|
|
|
876
876
|
|