finite_machine 0.13.0 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  <div align="center">
2
- <a href="http://piotrmurach.github.io/finite_machine/"><img width="236" src="https://raw.githubusercontent.com/piotrmurach/finite_machine/master/assets/finite_machine_logo.png" alt="finite machine logo" /></a>
2
+ <a href="https://piotrmurach.github.io/finite_machine/"><img width="236" src="https://github.com/piotrmurach/finite_machine/raw/master/assets/finite_machine_logo.png" alt="finite machine logo" /></a>
3
3
  </div>
4
4
 
5
5
  # FiniteMachine
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/finite_machine.svg)][gem]
8
- [![Build Status](https://secure.travis-ci.org/piotrmurach/finite_machine.svg?branch=master)][travis]
8
+ [![Actions CI](https://github.com/piotrmurach/finite_machine/workflows/CI/badge.svg?branch=master)][gh_actions_ci]
9
9
  [![Build status](https://ci.appveyor.com/api/projects/status/8ho4ijacpr7b4f4t?svg=true)][appveyor]
10
10
  [![Code Climate](https://codeclimate.com/github/piotrmurach/finite_machine/badges/gpa.svg)][codeclimate]
11
11
  [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/finite_machine/badge.svg?branch=master)][coverage]
@@ -13,7 +13,7 @@
13
13
  [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
14
14
 
15
15
  [gem]: http://badge.fury.io/rb/finite_machine
16
- [travis]: http://travis-ci.org/piotrmurach/finite_machine
16
+ [gh_actions_ci]: https://github.com/piotrmurach/finite_machine/actions?query=workflow%3ACI
17
17
  [appveyor]: https://ci.appveyor.com/project/piotrmurach/finite-machine
18
18
  [codeclimate]: https://codeclimate.com/github/piotrmurach/finite_machine
19
19
  [coverage]: https://coveralls.io/github/piotrmurach/finite_machine?branch=master
@@ -38,7 +38,7 @@
38
38
 
39
39
  Add this line to your application's Gemfile:
40
40
 
41
- gem 'finite_machine'
41
+ gem "finite_machine"
42
42
 
43
43
  Then execute:
44
44
 
@@ -119,34 +119,35 @@ fm = FiniteMachine.new do
119
119
  event :stop, :green => :red
120
120
 
121
121
  on_before(:ready) { |event| ... }
122
- on_after(:go) { |event| ... }
123
- on_before(:stop) { |event| ... }
122
+ on_exit(:yellow) { |event| ... }
123
+ on_enter(:green) { |event| ... }
124
+ on_after(:stop) { |event| ... }
124
125
  end
125
126
  ```
126
127
 
127
- As the example demonstrates, by calling the `new` method on **FiniteMachine** you create an instance of finite state machine.
128
+ By calling the `new` method on **FiniteMachine**, you gain access to a powerful DSL for expressing transitions and registering callbacks.
128
129
 
129
- Having declared the states and transitions using `event` method, you can check current state:
130
+ Having declared the states and transitions, you can check current state:
130
131
 
131
132
  ```ruby
132
133
  fm.current # => :red
133
134
  ````
134
135
 
135
- And trigger transitions using the `trigger`:
136
+ And then trigger transitions using the `trigger`:
136
137
 
137
138
  ```ruby
138
139
  fm.trigger(:ready)
139
140
  ```
140
141
 
141
- or direct method calls:
142
+ Or you can use direct method calls:
142
143
 
143
- * `fm.ready`
144
- * `fm.go`
145
- * `fm.stop`
144
+ ```ruby
145
+ fm.ready
146
+ ```
146
147
 
147
- The `events` and `callbacks` scopes help to define the behaviour of the machine. Read [States and Transitions](#3-states-and-transitions) and [Callbacks](#4-callbacks) sections for more details.
148
+ Read [States and Transitions](#3-states-and-transitions) and [Callbacks](#4-callbacks) sections for more details.
148
149
 
149
- Alternatively, you can construct the state machine like a regular object without using the DSL methods. The same machine could be reimplemented as follows:
150
+ Alternatively, you can construct the state machine like a regular object using the same DSL methods. Similar machine could be reimplemented as follows:
150
151
 
151
152
  ```ruby
152
153
  fm = FiniteMachine.new(initial: :red)
@@ -154,8 +155,9 @@ fm.event(:ready, :red => :yellow)
154
155
  fm.event(:go, :yellow => :green)
155
156
  fm.event(:stop, :green => :red)
156
157
  fm.on_before(:ready) { |event| ... }
157
- fm.on_after(:go) { |event| ... }
158
- fm.on_before(:stop) { |event| ...}
158
+ fm.on_exit(:yellow) { |event| ... }
159
+ fm.on_enter(:green) { |event| ... }
160
+ fm.on_after(:stop) { |event| ... }
159
161
  ```
160
162
 
161
163
  ## 2. API
@@ -178,19 +180,19 @@ end
178
180
  Alternatively, you can skip block definition and instead call DSL methods directly on the state machine instance:
179
181
 
180
182
  ```ruby
181
- fsm = FiniteMachine.new
182
- fsm.initial(:green)
183
- fsm.event(:slow, :green => :yellow)
184
- fsm.event(:stop, :yellow => :red)
185
- fsm.event(:ready,:red => :yellow)
186
- fsm.event(:go, :yellow => :green)
183
+ fm = FiniteMachine.new
184
+ fm.initial(:green)
185
+ fm.event(:slow, :green => :yellow)
186
+ fm.event(:stop, :yellow => :red)
187
+ fm.event(:ready,:red => :yellow)
188
+ fm.event(:go, :yellow => :green)
187
189
  ```
188
190
 
189
191
  As a guiding rule, any method exposed via DSL is available as a regular method call on the state machine instance.
190
192
 
191
193
  ### 2.2 define
192
194
 
193
- To create a reusable definition for a state machine use `define` method. By calling `define` you're creating an anonymous class that can act as a factory for state machines. For example, below we create a 'TrafficLights' class that contains our state machine definition:
195
+ To create a reusable definition for a state machine use `define` method. By calling `define` you're creating an anonymous class that can act as a factory for state machines. For example, below we create a `TrafficLights` class that contains our state machine definition:
194
196
 
195
197
  ```ruby
196
198
  TrafficLights = FiniteMachine.define do
@@ -203,7 +205,7 @@ TrafficLights = FiniteMachine.define do
203
205
  end
204
206
  ```
205
207
 
206
- Then you can create however many instance of above class:
208
+ Then we can create however many instance of above class:
207
209
 
208
210
  ```ruby
209
211
  lights_fm_a = TrafficLights.new
@@ -217,7 +219,7 @@ lights_fm_a.current # => :green
217
219
  lights_fm_b.current # => :green
218
220
  ```
219
221
 
220
- You can then trigger event in one instance and not the other:
222
+ We can then trigger event for one instance and not the other:
221
223
 
222
224
  ```ruby
223
225
  lights_fm_a.slow
@@ -267,7 +269,7 @@ fm.current # => :green
267
269
  Or by passing named argument `:initial` like so:
268
270
 
269
271
  ```ruby
270
- fm = FiniteMachine.new initial: :green do
272
+ fm = FiniteMachine.new(initial: :green) do
271
273
  ...
272
274
  end
273
275
  ```
@@ -286,7 +288,7 @@ fm.init # execute initial transition
286
288
  fm.current # => :green
287
289
  ```
288
290
 
289
- If your target object already has `init` method or one of the events names renews `init`, you can use different name by passing `:event` option to `initial` helper.
291
+ If your target object already has `init` method or one of the events names redefines `init`, you can use different name by passing `:event` option to `initial` helper.
290
292
 
291
293
  ```ruby
292
294
  fm = FiniteMachine.new do
@@ -301,7 +303,7 @@ fm.start # => call the renamed event
301
303
  fm.current # => :green
302
304
  ```
303
305
 
304
- By default the `initial` does not trigger any callbacks. If you need to fire callbacks and any event associated actions on initial transition, pass the `silent` option set to `false` like so
306
+ By default the `initial` does not trigger any callbacks. If you need to fire callbacks and any event associated actions on initial transition, pass the `silent` option set to `false` like so:
305
307
 
306
308
  ```ruby
307
309
  fm = FiniteMachine.new do
@@ -355,7 +357,7 @@ And the terminal state can be checked using `terminated?`:
355
357
 
356
358
  ```ruby
357
359
  fm.decline
358
- fm.terminated?
360
+ fm.terminated? # => true
359
361
  ```
360
362
 
361
363
  ### 2.6 is?
@@ -376,18 +378,18 @@ fm.yellow? # => false
376
378
 
377
379
  ### 2.7 trigger
378
380
 
379
- Transitions events can be fired by calling the `trigger` method with the event name and remaining arguments as data. The return value is either `true` or `false` depending whether the transition succeeded or not:
381
+ Transition events can be fired by calling the `trigger` method with the event name and remaining arguments as data. The return value is either `true` or `false` depending whether the transition succeeded or not:
380
382
 
381
383
  ```ruby
382
384
  fm.trigger(:ready) # => true
383
- fm.trigger(:ready, 'one', 'two', 'three') # => true
385
+ fm.trigger(:ready, "one", "two", "three") # => true
384
386
  ```
385
387
 
386
- By default the **FiniteMachine** automatically converts all the transition event names into methods:
388
+ By default, the **FiniteMachine** automatically converts all the transition event names into methods:
387
389
 
388
390
  ```ruby
389
391
  fm.ready # => true
390
- fm.ready('one', 'two', 'three') # => true
392
+ fm.ready("one", "two", "three") # => true
391
393
  ```
392
394
 
393
395
  Please see [States and Transitions](#3-states-and-transitions) for in-depth treatment of firing transitions.
@@ -395,8 +397,7 @@ Please see [States and Transitions](#3-states-and-transitions) for in-depth trea
395
397
 
396
398
  #### 2.7.1 `:auto_methods`
397
399
 
398
- By default all event names will be converted by **FiniteMachine** into method names. This also means that you won't be able to use event names such as `:fail` or `:trigger` as these are already defined on the machine instance. In situations when you wish to use any event name for your event names use `:auto_methods` keyword to disable automatic methods generation. For example, to define `:fail` event:
399
-
400
+ By default, all event names will be converted by **FiniteMachine** into method names. This also means that you won't be able to use event names such as `:fail` or `:trigger` as these are already defined on the machine instance. In situations when you wish to use any event name for your event names use `:auto_methods` keyword to disable automatic methods generation. For example, to define `:fail` event:
400
401
 
401
402
  ```ruby
402
403
  fm = FiniteMachine.new(auto_methods: false) do
@@ -431,7 +432,7 @@ fm = FiniteMachine.new do
431
432
  initial :green
432
433
 
433
434
  event :slow, :green => :yellow
434
- event :stop, :yellow => :red, if: proc { |_, param| :breaks == param }
435
+ event :stop, :yellow => :red, if: ->(_, param) { :breaks == param }
435
436
  end
436
437
 
437
438
  fm.can?(:slow) # => true
@@ -446,45 +447,45 @@ fm.can?(:stop, :no_breaks) # => false
446
447
 
447
448
  If you need to execute some external code in the context of the current state machine, pass that object as a first argument to `new` method.
448
449
 
449
- Assuming we have a simple `Car` class that holds an internal state whether the car's engine is on or off:
450
+ Assuming we have a simple `Engine` class that holds an internal state whether the car's engine is on or off:
450
451
 
451
452
  ```ruby
452
- class Car
453
+ class Engine
453
454
  def initialize
454
- @engine_on = false
455
+ @engine = false
455
456
  end
456
457
 
457
- def turn_engine_on
458
- @engine_on = true
458
+ def turn_on
459
+ @engine = true
459
460
  end
460
461
 
461
- def turn_engine_off
462
- @engine_on = false
462
+ def turn_off
463
+ @engine = false
463
464
  end
464
465
 
465
466
  def engine_on?
466
- @engine_on
467
+ @engine
467
468
  end
468
469
  end
469
470
  ```
470
471
 
471
- And given an instance of `Car` class:
472
+ And given an instance of `Engine` class:
472
473
 
473
474
  ```ruby
474
- car = Car.new
475
+ engine = Engine.new
475
476
  ```
476
477
 
477
478
  You can provide a context to a state machine by passing it as a first argument to a `new` call. You can then reference this context inside the callbacks by calling the `target` helper:
478
479
 
479
480
  ```ruby
480
- fm = FiniteMachine.new(car) do
481
+ fm = FiniteMachine.new(engine) do
481
482
  initial :neutral
482
483
 
483
- event :start, :neutral => :one, if: "engine_on?"
484
+ event :start, :neutral => :one, unless: "engine_on?"
484
485
  event :stop, :one => :neutral
485
486
 
486
- on_enter_start do |event| target.turn_engine_on end
487
- on_exit_start do |event| target.turn_engine_off end
487
+ on_before_start { |event| target.turn_on }
488
+ on_after_stop { |event| target.turn_off }
488
489
  end
489
490
  ```
490
491
 
@@ -495,18 +496,52 @@ For more complex example see [Integration](#7-integration) section.
495
496
  If you wish to better express the intention behind the context object, in particular when calling actions in callbacks, you can use the `:alias_target` option:
496
497
 
497
498
  ```ruby
498
- car = Car.new
499
+ engine = Engine.new
500
+
501
+ fm = FiniteMachine.new(engine, alias_target: :engine) do
502
+ initial :neutral
503
+
504
+ event :start, :neutral => :one, unless: "engine_on?"
505
+ event :stop, :none => :neutral, if: "engine_on?"
506
+
507
+ on_before_start { |event| engine.turn_on }
508
+ on_after_stop { |event| engine.turn_off }
509
+ end
510
+ ```
511
+
512
+ Alternatively, you can use the `alias_target` helper method:
513
+
514
+ ```ruby
515
+ engine = Engine.new
516
+
517
+ Car = FiniteMachine.define do
518
+ alias_target :engine
499
519
 
500
- fm = FiniteMachine.new(car, alias_target: :car) do
501
520
  initial :neutral
502
521
 
503
522
  event :start, :neutral => :one, if: "engine_on?"
523
+ event :stop, :none => :neutral, if: "engine_on?"
504
524
 
505
- on_enter_start do |event| car.turn_engine_on end
506
- on_exit_start do |event| car.turn_engine_off end
525
+ on_before_start { |event| engine.turn_on }
526
+ on_after_stop { |event| engine.turn_off }
507
527
  end
508
528
  ```
509
529
 
530
+ Then to link `Car` definition with `Engine` instance, pass the `Engine` instance as a first argument:
531
+
532
+ ```ruby
533
+ car = Car.new(engine)
534
+ ```
535
+
536
+ Triggering `start` event will change `Engine` instance state from `false` to `true`:
537
+
538
+ ```ruby
539
+ engine.engine_on? # => false
540
+ car.start
541
+ car.current # => :one
542
+ engine.engine_on? # => true
543
+ ```
544
+
510
545
  ### 2.10 restore!
511
546
 
512
547
  In order to set the machine to a given state and thus skip triggering callbacks use the `restore!` method:
@@ -543,7 +578,7 @@ in the form of `:from` and `:to` hash keys or by using the state names themselve
543
578
 
544
579
  ```ruby
545
580
  event :start, from: :neutral, to: :first
546
- or
581
+ # or
547
582
  event :start, :neutral => :first
548
583
  ```
549
584
 
@@ -574,7 +609,7 @@ fm.trigger(:ready) # => true
574
609
  Furthermore, you can pass additional parameters with the method call that will be available in the triggered callback as well as used by any present guarding conditions.
575
610
 
576
611
  ```ruby
577
- fm.go('Piotr!') # => true
612
+ fm.go("Piotr!") # => true
578
613
  fm.current # => :green
579
614
  ```
580
615
 
@@ -619,9 +654,7 @@ You can use `any_state` as the name for a given state, for instance:
619
654
 
620
655
  ```ruby
621
656
  event :run, from: any_state, to: :green
622
-
623
- or
624
-
657
+ # or
625
658
  event :run, any_state => :green
626
659
  ```
627
660
 
@@ -638,12 +671,12 @@ All the above `run` event definitions will always transition the state machine i
638
671
  Another way to specify state transitions under single event name is to group all your state transitions into a single hash like so:
639
672
 
640
673
  ```ruby
641
- fm = FiniteMachine.define do
674
+ fm = FiniteMachine.new do
642
675
  initial :initial
643
676
 
644
677
  event :bump, :initial => :low,
645
- :low => :medium,
646
- :medium => :high
678
+ :low => :medium,
679
+ :medium => :high
647
680
  end
648
681
  ```
649
682
 
@@ -671,8 +704,8 @@ fm = FiniteMachine.new do
671
704
  event :stop, :green => :red
672
705
  end
673
706
 
674
- fsm.go # no callbacks
675
- fms.stop # callbacks are fired
707
+ fm.go # no callbacks
708
+ fm.stop # callbacks are fired
676
709
  ```
677
710
 
678
711
  ### 3.7 Logging transitions
@@ -680,7 +713,7 @@ fms.stop # callbacks are fired
680
713
  To help debug your state machine, **FiniteMachine** provides `:log_transitions` option.
681
714
 
682
715
  ```ruby
683
- FiniteMachine.new log_transitions: true do
716
+ FiniteMachine.new(log_transitions: true) do
684
717
  ...
685
718
  end
686
719
  ```
@@ -707,11 +740,11 @@ fm.current # => :green
707
740
  Condition by default receives the current context, which is the current state machine instance, followed by extra arguments.
708
741
 
709
742
  ```ruby
710
- fsm = FiniteMachine.new do
743
+ fm = FiniteMachine.new do
711
744
  initial :red
712
745
 
713
746
  event :go, :red => :green,
714
- if: -> (context, a) { context.current == a }
747
+ if: ->(context, a) { context.current == a }
715
748
  end
716
749
 
717
750
  fm.go(:yellow) # doesn't transition
@@ -721,46 +754,48 @@ fm.go # raises ArgumentError
721
754
  **Note** If you specify condition with a given number of arguments then you need to call an event with the exact number of arguments, otherwise you will get `ArgumentError`. Thus in above scenario to prevent errors specify condition like so:
722
755
 
723
756
  ```ruby
724
- if: -> (context, *args) { ... }
757
+ if: ->(context, *args) { ... }
725
758
  ```
726
759
 
727
760
  Provided your **FiniteMachine** is associated with another object through `target` helper. Then the target object together with event arguments will be passed to the `:if` or `:unless` condition scope.
728
761
 
729
762
  ```ruby
730
- class Car
731
- attr_accessor :engine_on
763
+ class Engine
764
+ def initialize
765
+ @engine = false
766
+ end
732
767
 
733
- def turn_engine_on
734
- @engine_on = true
768
+ def turn_on
769
+ @engine = true
735
770
  end
736
771
 
737
- def turn_engine_off
738
- @engine_on = false
772
+ def turn_off
773
+ @engine = false
739
774
  end
740
775
 
741
776
  def engine_on?
742
- @engine_on
777
+ @engine
743
778
  end
744
779
  end
745
780
 
746
- car = Car.new
747
- car.turn_engine_on
781
+ engine = Engine.new
782
+ engine.turn_on
748
783
 
749
- fm = FiniteMachine.new do
784
+ car = FiniteMachine.new(engine) do
750
785
  initial :neutral
751
786
 
752
- target car
753
-
754
- event :start, :neutral => :one, if: -> (target, state) {
755
- target.engine_on = state
756
- target.engine_on?
757
- }
787
+ event :start, :neutral => :one, if: ->(target, state) do
788
+ state ? target.engine_on : target.engine_off
789
+ end
758
790
  end
759
791
 
760
792
  fm.start(false)
761
- fm.current # => :neutral
793
+ fm.current # => :neutral
794
+ engine.engine_on? # => false
795
+
762
796
  fm.start(true)
763
- fm.current # => :one
797
+ fm.current # => :one
798
+ engine.engine_on? # => true
764
799
  ```
765
800
 
766
801
  When the one-liner conditions are not enough for your needs, you can perform conditional logic inside the callbacks. See [4.9 Cancelling callbacks](#49-cancelling-inside-callbacks)
@@ -770,11 +805,9 @@ When the one-liner conditions are not enough for your needs, you can perform con
770
805
  You can also use a symbol corresponding to the name of a method that will get called right before transition happens.
771
806
 
772
807
  ```ruby
773
- fsm = FiniteMachine.new do
808
+ fm = FiniteMachine.new(engine) do
774
809
  initial :neutral
775
810
 
776
- target car
777
-
778
811
  event :start, :neutral => :one, if: :engine_on?
779
812
  end
780
813
  ```
@@ -784,11 +817,9 @@ end
784
817
  Finally, it's possible to use string that will be evaluated using `eval` and needs to contain valid Ruby code. It should only be used when the string represents a short condition.
785
818
 
786
819
  ```ruby
787
- fsm = FiniteMachine.new do
820
+ fm = FiniteMachine.new(engine) do
788
821
  initial :neutral
789
822
 
790
- target car
791
-
792
823
  event :start, :neutral => :one, if: "engine_on?"
793
824
  end
794
825
  ```
@@ -798,7 +829,7 @@ end
798
829
  When multiple conditions define whether or not a transition should happen, an Array can be used. Furthermore, you can apply both `:if` and `:unless` to the same transition.
799
830
 
800
831
  ```ruby
801
- fsm = FiniteMachine.new do
832
+ fm = FiniteMachine.new do
802
833
  initial :green
803
834
 
804
835
  event :slow, :green => :yellow,
@@ -812,27 +843,27 @@ The transition only runs when all the `:if` conditions and none of the `unless`
812
843
 
813
844
  ### 3.9 Choice pseudostates
814
845
 
815
- Choice pseudostate allows you to implement conditional branch. The conditions of an event's transitions are evaluated in order to to select only one outgoing transition.
846
+ Choice pseudostate allows you to implement conditional branch. The conditions of an event's transitions are evaluated in order to select only one outgoing transition.
816
847
 
817
848
  You can implement the conditional branch as ordinary events grouped under the same name and use familiar `:if/:unless` conditions:
818
849
 
819
850
  ```ruby
820
- fsm = FiniteMachine.define do
851
+ fm = FiniteMachine.define do
821
852
  initial :green
822
853
 
823
854
  event :next, :green => :yellow, if: -> { false }
824
855
  event :next, :green => :red, if: -> { true }
825
856
  end
826
857
 
827
- fsm.current # => :green
828
- fsm.next
829
- fsm.current # => :red
858
+ fm.current # => :green
859
+ fm.next
860
+ fm.current # => :red
830
861
  ```
831
862
 
832
863
  The same conditional logic can be implemented using much shorter and more descriptive style using `choice` method:
833
864
 
834
865
  ```ruby
835
- fsm = FiniteMachine.new do
866
+ fm = FiniteMachine.new do
836
867
  initial :green
837
868
 
838
869
  event :next, from: :green do
@@ -841,9 +872,9 @@ fsm = FiniteMachine.new do
841
872
  end
842
873
  end
843
874
 
844
- fsm.current # => :green
845
- fsm.next
846
- fsm.current # => :red
875
+ fm.current # => :green
876
+ fm.next
877
+ fm.current # => :red
847
878
  ```
848
879
 
849
880
  #### 3.9.1 Dynamic choice conditions
@@ -851,19 +882,19 @@ fsm.current # => :red
851
882
  Just as with event conditions you can make conditional logic dynamic and dependent on parameters passed in:
852
883
 
853
884
  ```ruby
854
- fsm = FiniteMachine.new do
885
+ fm = FiniteMachine.new do
855
886
  initial :green
856
887
 
857
888
  event :next, from: :green do
858
- choice :yellow, if: -> (context, a) { a < 1 }
859
- choice :red, if: -> (context, a) { a > 1 }
889
+ choice :yellow, if: ->(context, a) { a < 1 }
890
+ choice :red, if: ->(context, a) { a > 1 }
860
891
  default :red
861
892
  end
862
893
  end
863
894
 
864
- fsm.current # => :green
865
- fsm.next(0)
866
- fsm.current # => :yellow
895
+ fm.current # => :green
896
+ fm.next(0)
897
+ fm.current # => :yellow
867
898
  ```
868
899
 
869
900
  If more than one of the conditions evaluates to true, a first matching one is chosen. If none of the conditions evaluate to true, then the `default` state is matched. However if default state is not present and non of the conditions match, no transition is performed. To avoid such situation always specify `default` choice.
@@ -883,7 +914,7 @@ FiniteMachine.new do
883
914
  end
884
915
  ```
885
916
 
886
- or from any state using the `:any` state name like so:
917
+ Or from any state using the `:any` state name like so:
887
918
 
888
919
  ```ruby
889
920
  FiniteMachine.new do
@@ -909,7 +940,7 @@ You can register a callback to listen for state transitions and events triggered
909
940
  Use the state or event name as a first parameter to the callback helper followed by block with event argument and a list arguments that you expect to receive like so:
910
941
 
911
942
  ```ruby
912
- on_enter :green { |event, a, b, c| ... }
943
+ on_enter(:green) { |event, a, b, c| ... }
913
944
  ```
914
945
 
915
946
  When you subscribe to the `:green` state change, the callback will be called whenever someone triggers event that transitions in or out of that state. The same will happen on subscription to event `ready`, namely, the callback will be called each time the state transition method is triggered regardless of the states it transitions from or to.
@@ -922,13 +953,17 @@ fm = FiniteMachine.new do
922
953
  event :go, :yellow => :green
923
954
  event :stop, :green => :red
924
955
 
925
- on_before :ready { |event, time1, time2, time3| puts "#{time1} #{time2} #{time3} Go!" }
926
- on_before :go { |event, name| puts "Going fast #{name}" }
927
- on_before :stop { |event| ... }
956
+ on_before :ready do |event, time1, time2, time3|
957
+ puts "#{time1} #{time2} #{time3} Go!" }
958
+ end
959
+ on_before :go do |event, name|
960
+ puts "Going fast #{name}"
961
+ end
962
+ on_before(:stop) { |event| ... }
928
963
  end
929
964
 
930
965
  fm.ready(1, 2, 3)
931
- fm.go('Piotr!')
966
+ fm.go("Piotr!")
932
967
  ```
933
968
 
934
969
  **Note** Regardless of how the state is entered or exited, all the associated callbacks will be executed. This provides means for guaranteed initialization and cleanup.
@@ -982,9 +1017,9 @@ Then by calling `go` event the following callbacks sequence will be executed:
982
1017
 
983
1018
  All callbacks as a first argument yielded to a block receive the `TransitionEvent` object with the following attributes:
984
1019
 
985
- * `name # the event name`
986
- * `from # the state transitioning from`
987
- * `to # the state transitioning to`
1020
+ * `name` - the event name`
1021
+ * `from` - the state transitioning from`
1022
+ * `to` - the state transitioning to`
988
1023
 
989
1024
  followed by the rest of arguments that were passed to the event method.
990
1025
 
@@ -994,33 +1029,43 @@ fm = FiniteMachine.new do
994
1029
 
995
1030
  event :ready, :red => :yellow
996
1031
 
997
- on_enter_ready { |event, time|
1032
+ on_before_ready do |event, time|
998
1033
  puts "lights switching from #{event.from} to #{event.to} in #{time} seconds"
999
- }
1034
+ end
1000
1035
  end
1001
1036
 
1002
- fm.ready(3) # => 'lights switching from red to yellow in 3 seconds'
1037
+ fm.ready(3)
1038
+ # => "lights switching from red to yellow in 3 seconds"
1003
1039
  ```
1004
1040
 
1005
1041
  ### 4.6 Duplicate callbacks
1006
1042
 
1007
1043
  You can define any number of the same kind of callback. These callbacks will be executed in the order they are specified.
1008
1044
 
1045
+ Given the following state machine instance:
1046
+
1009
1047
  ```ruby
1010
1048
  fm = FiniteMachine.new do
1011
1049
  initial :green
1012
1050
 
1013
1051
  event :slow, :green => :yellow
1014
1052
 
1015
- on_enter(:yellow) { this_is_run_first }
1016
- on_enter(:yellow) { then_this }
1053
+ on_enter(:yellow) { puts "this is run first" }
1054
+ on_enter(:yellow) { puts "then this is run" }
1017
1055
  end
1018
- fm.slow # => will invoke both callbacks
1056
+ ```
1057
+
1058
+ Triggerring the `:slow` event results in:
1059
+
1060
+ ```ruby
1061
+ fm.slow
1062
+ # => "this is run first"
1063
+ # => "then this is run"
1019
1064
  ```
1020
1065
 
1021
1066
  ### 4.7 Fluid callbacks
1022
1067
 
1023
- Callbacks can also be specified as full method calls.
1068
+ Callbacks can also be specified as full method calls separated with underscores:
1024
1069
 
1025
1070
  ```ruby
1026
1071
  fm = FiniteMachine.define do
@@ -1083,10 +1128,15 @@ fm = FiniteMachine.new do
1083
1128
  event :forward, [:reverse, :neutral] => :one
1084
1129
  event :back, [:neutral, :one] => :reverse
1085
1130
 
1086
- on_enter_reverse { |event| forward('Piotr!') }
1131
+ on_enter_reverse { |event| forward("Piotr!") }
1087
1132
  on_exit_reverse { |event, name| puts "Go #{name}" }
1088
1133
  end
1089
- fm.back # => Go Piotr!
1134
+ ```
1135
+
1136
+ Then triggerring `:back` event gives:
1137
+
1138
+ ```ruby
1139
+ fm.back # => Go Piotr!
1090
1140
  ```
1091
1141
 
1092
1142
  For more complex example see [Integration](#7-integration) section.
@@ -1097,7 +1147,7 @@ A simple way to prevent transitions is to use [3 Conditional transitions](#3-con
1097
1147
 
1098
1148
  There are times when you want to cancel transition in a callback. For example, you have logic which allows transition to happen only under certain complex conditions. Using `cancel_event` inside the `on_(enter|transition|exit)` or `on_(before|after)` callbacks will stop all the callbacks from firing and prevent current transition from happening.
1099
1149
 
1100
- For example, firing any event will not move the current state:
1150
+ For example, the following state machine cancels any event leaving `:red` state:
1101
1151
 
1102
1152
  ```ruby
1103
1153
  fm = FiniteMachine.new do
@@ -1112,7 +1162,12 @@ fm = FiniteMachine.new do
1112
1162
  cancel_event
1113
1163
  end
1114
1164
  end
1165
+ ```
1166
+
1167
+ Then firing `:ready` event will not transition out of the current `:red` state:
1115
1168
 
1169
+ ```ruby
1170
+ fm.current # => :red
1116
1171
  fm.ready
1117
1172
  fm.current # => :red
1118
1173
  ```
@@ -1122,21 +1177,18 @@ fm.current # => :red
1122
1177
  By default all callbacks are run synchronously. In order to add a callback that runs asynchronously, you need to pass second `:async` argument like so:
1123
1178
 
1124
1179
  ```ruby
1125
- on_enter :green, :async do |event| ... end
1126
- ```
1127
-
1128
- Or
1129
-
1130
- ```ruby
1131
- on_enter_green(:async) { |event| }
1180
+ on_enter(:green, :async) do |event| ... end
1181
+ # or
1182
+ on_enter_green(:async) { |event| }
1132
1183
  ```
1133
1184
 
1134
1185
  This will ensure that when the callback is fired it will run in separate thread outside of the main execution thread.
1135
1186
 
1136
-
1137
1187
  ### 4.11 Instance callbacks
1138
1188
 
1139
- When defining callbacks you are not limited to the `callbacks` helper. After **FiniteMachine** instance is created you can register callbacks the same way as before by calling `on` and supplying the type of notification and state/event you are interested in.
1189
+ When defining callbacks you are not limited to the **FiniteMachine** block definition. After creating an instance, you can register callbacks the same way as before by calling `on` and supplying the type of notification and state/event you are interested in.
1190
+
1191
+ For example, given the following state machine:
1140
1192
 
1141
1193
  ```ruby
1142
1194
  fm = FiniteMachine.new do
@@ -1146,10 +1198,14 @@ fm = FiniteMachine.new do
1146
1198
  event :go, :yellow => :green
1147
1199
  event :stop, :green => :red
1148
1200
  end
1201
+ ```
1149
1202
 
1150
- fm.on_enter_yellow do |event|
1151
- ...
1152
- end
1203
+ We can add callbacks as follows:
1204
+
1205
+ ```ruby
1206
+ fm.on_enter(:yellow) { |event| ... }
1207
+ # or
1208
+ fm.en_enter_yellow { |event| ... }
1153
1209
  ```
1154
1210
 
1155
1211
  ## 5. Error Handling
@@ -1174,7 +1230,7 @@ fm = FiniteMachine.new do
1174
1230
  raise exception
1175
1231
  end
1176
1232
 
1177
- handle FiniteMachine::TransitionError, with: proc { |exception| ... }
1233
+ handle FiniteMachine::TransitionError, with: -> { |exception| ... }
1178
1234
  end
1179
1235
  ```
1180
1236
 
@@ -1195,7 +1251,7 @@ fm = FiniteMachine.new(logger) do
1195
1251
  event :slow, :green => :yellow
1196
1252
  event :stop, :yellow => :red
1197
1253
 
1198
- handle 'InvalidStateError', with: :log_error
1254
+ handle "InvalidStateError", with: :log_error
1199
1255
  end
1200
1256
  ```
1201
1257
 
@@ -1223,7 +1279,9 @@ class Engine < FiniteMachine::Definition
1223
1279
  target.turn_reverse_lights_off
1224
1280
  end
1225
1281
 
1226
- handle FiniteMachine::InvalidStateError do |exception| ... end
1282
+ handle FiniteMachine::InvalidStateError do |exception|
1283
+ ...
1284
+ end
1227
1285
  end
1228
1286
  ```
1229
1287
 
@@ -1260,7 +1318,7 @@ engine.back
1260
1318
  car.reverse_lights? # => true
1261
1319
  ```
1262
1320
 
1263
- Alternatively, create method inside the `Car` that will do the integration like so
1321
+ Alternatively, create method inside the `Car` that will do the integration like so:
1264
1322
 
1265
1323
  ```ruby
1266
1324
  class Car
@@ -1394,7 +1452,7 @@ class Account < ActiveRecord::Base
1394
1452
  event :authorize, :pending => :access
1395
1453
 
1396
1454
  on_enter do |event|
1397
- target.state = state.to
1455
+ target.state = event.to
1398
1456
  end
1399
1457
  end
1400
1458
  end
@@ -1436,6 +1494,10 @@ Creating a standalone **FiniteMachine** brings a number of benefits, one of them
1436
1494
  4. Push to the branch (`git push origin my-new-feature`)
1437
1495
  5. Create new Pull Request
1438
1496
 
1497
+ ## Code of Conduct
1498
+
1499
+ Everyone interacting in the FiniteMachine project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/finite_machine/blob/master/CODE_OF_CONDUCT.md).
1500
+
1439
1501
  ## Copyright
1440
1502
 
1441
1503
  Copyright (c) 2014 Piotr Murach. See LICENSE for further details.