finite_machine 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -1
  4. data/README.md +73 -35
  5. data/assets/finite_machine_logo.png +0 -0
  6. data/lib/finite_machine.rb +0 -7
  7. data/lib/finite_machine/async_proxy.rb +1 -2
  8. data/lib/finite_machine/dsl.rb +13 -14
  9. data/lib/finite_machine/event_definition.rb +32 -35
  10. data/lib/finite_machine/events_chain.rb +183 -37
  11. data/lib/finite_machine/hook_event.rb +47 -42
  12. data/lib/finite_machine/logger.rb +3 -4
  13. data/lib/finite_machine/observer.rb +27 -11
  14. data/lib/finite_machine/state_definition.rb +66 -0
  15. data/lib/finite_machine/state_machine.rb +177 -99
  16. data/lib/finite_machine/subscribers.rb +17 -6
  17. data/lib/finite_machine/thread_context.rb +1 -1
  18. data/lib/finite_machine/transition.rb +45 -173
  19. data/lib/finite_machine/transition_builder.rb +24 -6
  20. data/lib/finite_machine/transition_event.rb +5 -4
  21. data/lib/finite_machine/undefined_transition.rb +32 -0
  22. data/lib/finite_machine/version.rb +1 -1
  23. data/spec/spec_helper.rb +1 -0
  24. data/spec/unit/async_events_spec.rb +24 -18
  25. data/spec/unit/callbacks_spec.rb +0 -19
  26. data/spec/unit/event_names_spec.rb +19 -0
  27. data/spec/unit/events_chain/add_spec.rb +25 -0
  28. data/spec/unit/events_chain/cancel_transitions_spec.rb +22 -0
  29. data/spec/unit/events_chain/choice_transition_spec.rb +28 -0
  30. data/spec/unit/events_chain/clear_spec.rb +7 -18
  31. data/spec/unit/events_chain/events_spec.rb +18 -0
  32. data/spec/unit/events_chain/inspect_spec.rb +14 -17
  33. data/spec/unit/events_chain/match_transition_spec.rb +37 -0
  34. data/spec/unit/events_chain/move_to_spec.rb +48 -0
  35. data/spec/unit/events_chain/states_for_spec.rb +17 -0
  36. data/spec/unit/events_spec.rb +119 -27
  37. data/spec/unit/hook_event/build_spec.rb +15 -0
  38. data/spec/unit/hook_event/eql_spec.rb +3 -4
  39. data/spec/unit/hook_event/initialize_spec.rb +14 -11
  40. data/spec/unit/hook_event/notify_spec.rb +14 -0
  41. data/spec/unit/{initialize_spec.rb → initial_spec.rb} +1 -1
  42. data/spec/unit/inspect_spec.rb +1 -1
  43. data/spec/unit/logger_spec.rb +4 -5
  44. data/spec/unit/subscribers_spec.rb +20 -9
  45. data/spec/unit/transition/check_conditions_spec.rb +54 -0
  46. data/spec/unit/transition/inspect_spec.rb +2 -2
  47. data/spec/unit/transition/matches_spec.rb +23 -0
  48. data/spec/unit/transition/states_spec.rb +31 -0
  49. data/spec/unit/transition/to_state_spec.rb +27 -0
  50. data/spec/unit/trigger_spec.rb +22 -0
  51. data/spec/unit/undefined_transition/eql_spec.rb +17 -0
  52. data/tasks/console.rake +1 -0
  53. metadata +39 -23
  54. data/lib/finite_machine/event.rb +0 -146
  55. data/spec/unit/event/add_spec.rb +0 -16
  56. data/spec/unit/event/eql_spec.rb +0 -37
  57. data/spec/unit/event/initialize_spec.rb +0 -38
  58. data/spec/unit/event/inspect_spec.rb +0 -21
  59. data/spec/unit/event/next_transition_spec.rb +0 -35
  60. data/spec/unit/events_chain/check_choice_conditions_spec.rb +0 -20
  61. data/spec/unit/events_chain/insert_spec.rb +0 -26
  62. data/spec/unit/events_chain/select_transition_spec.rb +0 -23
  63. data/spec/unit/transition/parse_states_spec.rb +0 -42
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module FiniteMachine
4
- VERSION = "0.10.2"
4
+ VERSION = "0.11.0"
5
5
  end
data/spec/spec_helper.rb CHANGED
@@ -16,6 +16,7 @@ if RUBY_VERSION > '1.9' and (ENV['COVERAGE'] || ENV['TRAVIS'])
16
16
  end
17
17
 
18
18
  require 'finite_machine'
19
+ require 'thwait'
19
20
 
20
21
  RSpec.configure do |config|
21
22
  config.run_all_when_everything_filtered = true
@@ -72,26 +72,32 @@ RSpec.describe FiniteMachine, 'async_events' do
72
72
 
73
73
  it "ensure queue per thread" do
74
74
  called = []
75
- fsmFoo = FiniteMachine.define do
76
- initial :green
77
- events { event :slow, :green => :yellow }
75
+ fsmFoo = nil
76
+ fsmBar = nil
77
+ foo_thread = Thread.new {
78
+ fsmFoo = FiniteMachine.define do
79
+ initial :green
80
+ events { event :slow, :green => :yellow }
78
81
 
79
- callbacks {
80
- on_enter :yellow do |event, a| called << "(foo)on_enter_yellow_#{a}" end
81
- }
82
- end
83
- fsmBar = FiniteMachine.define do
84
- initial :green
85
- events { event :slow, :green => :yellow }
82
+ callbacks {
83
+ on_enter :yellow do |event, a| called << "(foo)on_enter_yellow_#{a}" end
84
+ }
85
+ end
86
+ fsmFoo.async.slow(:foo)
87
+ }
88
+ bar_thread = Thread.new {
89
+ fsmBar = FiniteMachine.define do
90
+ initial :green
91
+ events { event :slow, :green => :yellow }
86
92
 
87
- callbacks {
88
- on_enter :yellow do |event, a| called << "(bar)on_enter_yellow_#{a}" end
89
- }
90
- end
91
- foo_thread = Thread.new { fsmFoo.async.slow(:foo) }
92
- bar_thread = Thread.new { fsmBar.async.slow(:bar) }
93
- [foo_thread, bar_thread].each(&:join)
94
- [fsmFoo, fsmBar].each { |fsm| fsm.event_queue.join 0.02 }
93
+ callbacks {
94
+ on_enter :yellow do |event, a| called << "(bar)on_enter_yellow_#{a}" end
95
+ }
96
+ end
97
+ fsmBar.async.slow(:bar)
98
+ }
99
+ ThreadsWait.all_waits(foo_thread, bar_thread)
100
+ sleep 0.01
95
101
  expect(called).to match_array([
96
102
  '(foo)on_enter_yellow_foo',
97
103
  '(bar)on_enter_yellow_bar'
@@ -687,25 +687,6 @@ RSpec.describe FiniteMachine, 'callbacks' do
687
687
  ])
688
688
  end
689
689
 
690
- it "cancels transition on state callback" do
691
- fsm = FiniteMachine.define do
692
- initial :green
693
-
694
- events {
695
- event :slow, :green => :yellow
696
- event :go, :yellow => :green
697
- }
698
-
699
- callbacks {
700
- on_exit :green do |event| FiniteMachine::CANCELLED end
701
- }
702
- end
703
-
704
- expect(fsm.current).to eql(:green)
705
- fsm.slow
706
- expect(fsm.current).to eql(:green)
707
- end
708
-
709
690
  it "cancels transition on event callback" do
710
691
  fsm = FiniteMachine.define do
711
692
  initial :green
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine, '.event_names' do
6
+ it "retrieves all event names" do
7
+ fsm = FiniteMachine.define do
8
+ initial :green
9
+
10
+ events {
11
+ event :start, :red => :green
12
+ event :stop, :green => :red
13
+ }
14
+ end
15
+
16
+ expect(fsm.current).to eql(:green)
17
+ expect(fsm.event_names).to eql([:init, :start, :stop])
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.add' do
6
+ it "adds transitions" do
7
+ transition = double(:transition)
8
+ events_chain = described_class.new
9
+
10
+ events_chain.add(:validated, transition)
11
+ expect(events_chain[:validated]).to eq([transition])
12
+
13
+ events_chain.add(:validated, transition)
14
+ expect(events_chain[:validated]).to eq([transition, transition])
15
+ end
16
+
17
+ it "allows to chain add operations" do
18
+ events_chain = described_class.new
19
+ transition = double(:transition)
20
+
21
+ events_chain.add(:go, transition).add(:start, transition)
22
+
23
+ expect(events_chain.size).to eq(2)
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.cancel_transitions' do
6
+ it "sets cancel status for chosen transitions" do
7
+ events_chain = described_class.new
8
+ transition_a = spy(:transition_a, cancelled: false)
9
+ transition_b = spy(:transition_b, cancelled: false)
10
+ transition_c = spy(:transition_c, cancelled: false)
11
+
12
+ events_chain.add(:start, transition_a)
13
+ events_chain.add(:start, transition_b)
14
+ events_chain.add(:finish, transition_c)
15
+
16
+ events_chain.cancel_transitions(:start, true)
17
+
18
+ expect(transition_a).to have_received(:cancelled=).with(true)
19
+ expect(transition_b).to have_received(:cancelled=).with(true)
20
+ expect(transition_c).not_to have_received(:cancelled=)
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.choice_transition?' do
6
+
7
+ it "checks if transition has many branches" do
8
+ transition_a = double(:transition_a, matches?: true)
9
+ transition_b = double(:transition_b, matches?: true)
10
+
11
+ events_chain = described_class.new
12
+ events_chain.add(:go, transition_a)
13
+ events_chain.add(:go, transition_b)
14
+
15
+ expect(events_chain.choice_transition?(:go, :green)).to eq(true)
16
+ end
17
+
18
+ it "checks that transition has no branches" do
19
+ transition_a = double(:transition_a, matches?: false)
20
+ transition_b = double(:transition_b, matches?: true)
21
+
22
+ events_chain = described_class.new
23
+ events_chain.add(:go, transition_a)
24
+ events_chain.add(:go, transition_b)
25
+
26
+ expect(events_chain.choice_transition?(:go, :green)).to eq(false)
27
+ end
28
+ end
@@ -2,25 +2,14 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe FiniteMachine::EventsChain, '#insert' do
6
- let(:object) { described_class }
7
-
8
- let(:machine) { double(:machine) }
9
-
10
- let(:transition) { double(:transition) }
11
-
12
- subject(:chain) { object.new(machine) }
13
-
14
- it "inserts transition" do
5
+ RSpec.describe FiniteMachine::EventsChain, '#clear' do
6
+ it "clears chain events" do
15
7
  event = double(:event)
16
- chain.add(:validated, event)
17
- expect(chain[:validated]).to eq(event)
18
-
19
- expect(event).to receive(:<<).with(transition)
20
- chain.insert(:validated, transition)
21
- end
8
+ events_chain = described_class.new
9
+ events_chain.add(:validated, event)
10
+ expect(events_chain.empty?).to be(false)
22
11
 
23
- it "fails to insert transition" do
24
- expect(chain.insert(:validated, transition)).to be(false)
12
+ events_chain.clear
13
+ expect(events_chain.empty?).to be(true)
25
14
  end
26
15
  end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.events' do
6
+ it "has no event names" do
7
+ events_chain = described_class.new
8
+ expect(events_chain.events).to eq([])
9
+ end
10
+
11
+ it "returns all event names" do
12
+ events_chain = described_class.new
13
+ transition = double(:transition)
14
+ events_chain.add(:ready, transition)
15
+ events_chain.add(:go, transition)
16
+ expect(events_chain.events).to match_array([:ready, :go])
17
+ end
18
+ end
@@ -2,26 +2,23 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe FiniteMachine::EventsChain, '#insert' do
6
- let(:object) { described_class }
7
-
8
- let(:machine) { double(:machine) }
9
-
10
- subject(:chain) { object.new(machine) }
11
-
12
- it "inspects empty chain" do
13
- expect(chain.inspect).to eq("<#FiniteMachine::EventsChain @chain={}>")
5
+ RSpec.describe FiniteMachine::EventsChain, '#inspect' do
6
+ it "inspects empty events chain" do
7
+ events_chain = described_class.new
8
+ expect(events_chain.inspect).to eq("<#FiniteMachine::EventsChain @chain={}>")
14
9
  end
15
10
 
16
- it "inspect chain" do
17
- event = double(:event)
18
- chain.add(:validated, event)
19
- expect(chain.inspect).to eq("<#FiniteMachine::EventsChain @chain=#{{validated: event}}>")
11
+ it "inspect events chain" do
12
+ transition = double(:transition)
13
+ events_chain = described_class.new
14
+ events_chain.add(:validated, transition)
15
+ expect(events_chain.inspect).to eq("<#FiniteMachine::EventsChain @chain=#{{validated: [transition]}}>")
20
16
  end
21
17
 
22
- it "prints chain" do
23
- event = double(:event)
24
- chain.add(:validated, event)
25
- expect(chain.to_s).to eq("#{{validated: event}}")
18
+ it "prints events chain" do
19
+ transition = double(:transition)
20
+ events_chain = described_class.new
21
+ events_chain.add(:validated, transition)
22
+ expect(events_chain.to_s).to eq("#{{validated: [transition]}}")
26
23
  end
27
24
  end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.match_transition' do
6
+ it "matches transition without conditions" do
7
+ transition_a = double(:transition_a, matches?: false)
8
+ transition_b = double(:transition_b, matches?: true)
9
+ events_chain = described_class.new
10
+
11
+ events_chain.add(:a, transition_a)
12
+ events_chain.add(:a, transition_b)
13
+
14
+ expect(events_chain.match_transition(:a, :green)).to eq(transition_b)
15
+ end
16
+
17
+ it "fails to match any transition" do
18
+ events_chain = described_class.new
19
+
20
+ expect(events_chain.match_transition(:a, :green)).to eq(nil)
21
+ end
22
+
23
+ it "matches transition with conditions" do
24
+ transition_a = double(:transition_a, matches?: true)
25
+ transition_b = double(:transition_b, matches?: true)
26
+ events_chain = described_class.new
27
+
28
+ events_chain.add(:a, transition_a)
29
+ events_chain.add(:a, transition_b)
30
+
31
+ allow(transition_a).to receive(:check_conditions).and_return(false)
32
+ allow(transition_b).to receive(:check_conditions).and_return(true)
33
+
34
+ expect(events_chain.match_transition_with(:a, :green, 'Piotr')).to eq(transition_b)
35
+ expect(transition_a).to have_received(:check_conditions).with('Piotr')
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe FiniteMachine::EventsChain, '.move_to' do
6
+
7
+ it "moves to state by matching individual transition" do
8
+ transition_a = double(:transition_a, matches?: false)
9
+ transition_b = double(:transition_b, matches?: true)
10
+
11
+ events_chain = described_class.new
12
+ events_chain.add(:go, transition_a)
13
+ events_chain.add(:go, transition_b)
14
+
15
+ allow(transition_b).to receive(:to_state).with(:yellow).and_return(:red)
16
+
17
+ expect(events_chain.move_to(:go, :yellow)).to eq(:red)
18
+ expect(transition_b).to have_received(:to_state).with(:yellow)
19
+ end
20
+
21
+ it "moves to state by matching choice transition" do
22
+ transition_a = double(:transition_a, matches?: true)
23
+ transition_b = double(:transition_b, matches?: true)
24
+
25
+ events_chain = described_class.new
26
+ events_chain.add(:go, transition_a)
27
+ events_chain.add(:go, transition_b)
28
+
29
+ allow(transition_a).to receive(:check_conditions).and_return(false)
30
+ allow(transition_b).to receive(:check_conditions).and_return(true)
31
+
32
+ allow(transition_b).to receive(:to_state).with(:green).and_return(:red)
33
+
34
+ expect(events_chain.move_to(:go, :green)).to eq(:red)
35
+ expect(transition_b).to have_received(:to_state).with(:green)
36
+ end
37
+
38
+ it "moves to from state if no transition available" do
39
+ transition_a = double(:transition_a, matches?: false)
40
+ transition_b = double(:transition_b, matches?: false)
41
+
42
+ events_chain = described_class.new
43
+ events_chain.add(:go, transition_a)
44
+ events_chain.add(:go, transition_b)
45
+
46
+ expect(events_chain.move_to(:go, :green)).to eq(:green)
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe FiniteMachine::EventsChain do
4
+ it "finds current states for event name" do
5
+ transition = spy(:transition, states: {:red => :yellow, :yellow => :green})
6
+ events_chain = described_class.new
7
+ events_chain.add(:start, transition)
8
+
9
+ expect(events_chain.states_for(:start)).to eq([:red, :yellow])
10
+ end
11
+
12
+ it "fails to find any states for event name" do
13
+ events_chain = described_class.new
14
+
15
+ expect(events_chain.states_for(:start)).to eq([])
16
+ end
17
+ end
@@ -169,37 +169,109 @@ RSpec.describe FiniteMachine, 'events' do
169
169
  expect(fsm.current).to eql(:green)
170
170
  end
171
171
 
172
- it "raises error on invalid transition" do
172
+ it "doesn't raise error on invalid transition for non-dangerous version" do
173
+ called = []
173
174
  fsm = FiniteMachine.define do
174
175
  initial :green
175
176
 
176
177
  events {
177
- event :slow, from: :green, to: :yellow
178
- event :stop, from: :yellow, to: :red
178
+ event :stop, from: :yellow, to: :red
179
+ }
180
+ callbacks {
181
+ on_before :stop do |event| called << 'on_before_stop' end
182
+ on_after :stop do |event| called << 'on_before_stop' end
179
183
  }
180
184
  end
181
185
 
182
- expect(fsm.current).to eql(:green)
186
+ expect(fsm.current).to eq(:green)
187
+ expect(fsm.stop).to eq(false)
188
+ expect(fsm.current).to eq(:green)
189
+ expect(called).to match_array(['on_before_stop'])
190
+ end
183
191
 
184
- expect {
185
- fsm.stop
186
- }.to raise_error(FiniteMachine::InvalidStateError,
187
- /inappropriate current state 'green'/)
192
+ context 'for non-dangerous version' do
193
+ it "doesn't raise error on invalid transition and fires callbacks" do
194
+ called = []
195
+ fsm = FiniteMachine.define do
196
+ initial :green
197
+
198
+ events {
199
+ event :stop, from: :yellow, to: :red
200
+ }
201
+ callbacks {
202
+ on_before :stop do |event| called << 'on_before_stop' end
203
+ on_after :stop do |event| called << 'on_before_stop' end
204
+ }
205
+ end
206
+
207
+ expect(fsm.current).to eq(:green)
208
+ expect(fsm.stop).to eq(false)
209
+ expect(fsm.current).to eq(:green)
210
+ expect(called).to match_array(['on_before_stop'])
211
+ end
212
+
213
+ it "raises error on invalid transition for dangerous version" do
214
+ called = []
215
+ fsm = FiniteMachine.define do
216
+ initial :green
217
+
218
+ events {
219
+ event :slow, from: :green, to: :yellow
220
+ event :stop, from: :yellow, to: :red, silent: true
221
+ }
222
+ callbacks {
223
+ on_before :stop do |event| called << 'on_before_stop' end
224
+ on_after :stop do |event| called << 'on_before_stop' end
225
+ }
226
+ end
227
+
228
+ expect(fsm.current).to eql(:green)
229
+ expect(fsm.stop).to eq(false)
230
+ expect(called).to match_array([])
231
+ end
188
232
  end
189
233
 
190
- it "allows to transition to any state" do
191
- fsm = FiniteMachine.define do
192
- initial :green
234
+ context 'for dangerous version' do
235
+ it "raises error on invalid transition without callbacks" do
236
+ called = []
237
+ fsm = FiniteMachine.define do
238
+ initial :green
193
239
 
194
- events {
195
- event :slow, from: :green, to: :yellow
196
- event :stop, from: :yellow, to: :red
197
- }
240
+ events {
241
+ event :start, :red => :yellow, silent: true
242
+ }
243
+ callbacks {
244
+ on_before :start do |event| called << 'on_before_start' end
245
+ on_after :start do |event| called << 'on_after_start' end
246
+ }
247
+ end
248
+
249
+ expect(fsm.current).to eq(:green)
250
+ expect { fsm.start! }.to raise_error(FiniteMachine::InvalidStateError)
251
+ expect(called).to eq([])
252
+ expect(fsm.current).to eq(:green)
253
+ end
254
+
255
+ it "raises error on invalid transition with callbacks fired" do
256
+ called = []
257
+ fsm = FiniteMachine.define do
258
+ initial :green
259
+
260
+ events {
261
+ event :start, :red => :yellow
262
+ }
263
+ callbacks {
264
+ on_before :start do |event| called << 'on_before_start' end
265
+ on_after :start do |event| called << 'on_after_start' end
266
+ }
267
+ end
268
+
269
+ expect(fsm.current).to eq(:green)
270
+ expect { fsm.start! }.to raise_error(FiniteMachine::InvalidStateError,
271
+ /inappropriate current state 'green'/)
272
+ expect(called).to eq(['on_before_start'])
273
+ expect(fsm.current).to eq(:green)
198
274
  end
199
- expect(fsm.current).to eql(:green)
200
- expect(fsm.can?(:stop)).to be false
201
- fsm.stop!
202
- expect(fsm.current).to eql(:red)
203
275
  end
204
276
 
205
277
  context 'when multiple from states' do
@@ -314,16 +386,16 @@ RSpec.describe FiniteMachine, 'events' do
314
386
  }
315
387
 
316
388
  callbacks {
317
- on_before(:drive) { }
389
+ on_before(:drive) { FiniteMachine::CANCELLED }
318
390
  on_after(:stop) { }
319
391
  }
320
392
  end
321
393
 
322
394
  expect(fsm.current).to eql(:neutral)
323
- expect(fsm.start).to eql(FiniteMachine::SUCCEEDED)
324
- expect(fsm.drive).to eql(FiniteMachine::CANCELLED)
325
- expect(fsm.stop).to eql(FiniteMachine::SUCCEEDED)
326
- expect(fsm.stop).to eql(FiniteMachine::NOTRANSITION)
395
+ expect(fsm.start).to eql(true)
396
+ expect(fsm.drive).to eql(false)
397
+ expect(fsm.stop).to eql(true)
398
+ expect(fsm.stop).to eql(true)
327
399
  end
328
400
 
329
401
  it "allows for self transition events" do
@@ -358,10 +430,30 @@ RSpec.describe FiniteMachine, 'events' do
358
430
  end
359
431
 
360
432
  it "detects dangerous event names" do
361
- expect { FiniteMachine.define do
433
+ expect {
434
+ FiniteMachine.define do
435
+ events {
436
+ event :trigger, :a => :b
437
+ }
438
+ end
439
+ }.to raise_error(FiniteMachine::AlreadyDefinedError)
440
+ end
441
+
442
+ it "executes event block" do
443
+ fsm = FiniteMachine.define do
444
+ initial :red
445
+
362
446
  events {
363
- event :transition, :a => :b
447
+ event :start, :red => :green
448
+ event :stop, :green => :red
364
449
  }
365
- end }.to raise_error(FiniteMachine::AlreadyDefinedError)
450
+ end
451
+
452
+ expect(fsm.current).to eq(:red)
453
+ called = []
454
+ fsm.start do |from, to|
455
+ called << "execute_start_#{from}_#{to}"
456
+ end
457
+ expect(called).to eq(['execute_start_red_green'])
366
458
  end
367
459
  end