Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 201 lines (148 sloc) 5.232 kb
b3df5b6 @soveran Updated README and changed micromachine API
authored
1 MicroMachine
2 ============
3
4 Minimal Finite State Machine.
5
6 Description
7 -----------
8
9 There are many finite state machine implementations for Ruby, and they
10 all provide a nice DSL for declaring events, exceptions, callbacks,
11 and all kinds of niceties in general.
12
ddeffe0 @soveran Formatting changes for README.
authored
13 But if all you want is a finite state machine, look no further: this
a8815e6 @soveran Add when and events methods.
authored
14 has less than 50 lines of code and provides everything a finite state
ddeffe0 @soveran Formatting changes for README.
authored
15 machine must have, and nothing more.
b3df5b6 @soveran Updated README and changed micromachine API
authored
16
17 Usage
18 -----
19
bf2caab @soveran Update examples.
authored
20 ``` ruby
21 require 'micromachine'
b3df5b6 @soveran Updated README and changed micromachine API
authored
22
bf2caab @soveran Update examples.
authored
23 machine = MicroMachine.new(:new) # Initial state.
b3df5b6 @soveran Updated README and changed micromachine API
authored
24
a8815e6 @soveran Add when and events methods.
authored
25 # Define the possible transitions for each event.
26 machine.when(:confirm, :new => :confirmed)
27 machine.when(:ignore, :new => :ignored)
28 machine.when(:reset, :confirmed => :new, :ignored => :new)
b3df5b6 @soveran Updated README and changed micromachine API
authored
29
bf2caab @soveran Update examples.
authored
30 machine.trigger(:confirm) #=> true
f2f807f @soveran Add information about predicates.
authored
31 machine.state #=> :confirmed
32
bf2caab @soveran Update examples.
authored
33 machine.trigger(:ignore) #=> false
f2f807f @soveran Add information about predicates.
authored
34 machine.state #=> :confirmed
35
bf2caab @soveran Update examples.
authored
36 machine.trigger(:reset) #=> true
f2f807f @soveran Add information about predicates.
authored
37 machine.state #=> :new
38
bf2caab @soveran Update examples.
authored
39 machine.trigger(:ignore) #=> true
f2f807f @soveran Add information about predicates.
authored
40 machine.state #=> :ignored
41 ```
42
a8815e6 @soveran Add when and events methods.
authored
43 The `when` helper is syntactic sugar for assigning to the
44 `transitions_for` hash. This code is equivalent:
45
46 ``` ruby
47 machine.transitions_for[:confirm] = { :new => :confirmed }
48 machine.transitions_for[:ignore] = { :new => :ignored }
49 machine.transitions_for[:reset] = { :confirmed => :new, :ignored => :new }
50 ```
51
f2f807f @soveran Add information about predicates.
authored
52 You can also ask if an event will trigger a change in state. Following
53 the example above:
54
55 ``` ruby
56 machine.state #=> :ignored
57
58 machine.trigger?(:ignore) #=> false
59 machine.trigger?(:reset) #=> true
60
61 # And the state is preserved, because you were only asking.
62 machine.state #=> :ignored
bf2caab @soveran Update examples.
authored
63 ```
b3df5b6 @soveran Updated README and changed micromachine API
authored
64
b4b3bba @soveran Added callbacks
authored
65 It can also have callbacks when entering some state:
66
bf2caab @soveran Update examples.
authored
67 ``` ruby
68 machine.on(:confirmed) do
69 puts "Confirmed"
70 end
71 ```
b4b3bba @soveran Added callbacks
authored
72
a878065 documented :any callback
Tony Hillerson authored
73 Or callbacks on any transition:
74
bf2caab @soveran Update examples.
authored
75 ``` ruby
76 machine.on(:any) do
77 puts "Transitioned..."
78 end
79 ```
a878065 documented :any callback
Tony Hillerson authored
80
659bd1a @soveran Add keyword :any to add callbacks for any transition
authored
81 Note that `:any` is a special key. Using it as a state when declaring
82 transitions will give you unexpected results.
83
f2f807f @soveran Add information about predicates.
authored
84 Check the examples directory for more information.
85
da6d94b @soveran Updated documentation and gemspec
authored
86 Adding MicroMachine to your models
87 ----------------------------------
88
89 The most popular pattern among Ruby libraries that tackle this problem
90 is to extend the model and transform it into a finite state machine.
91 Instead of working as a mixin, MicroMachine's implementation is by
92 composition: you instantiate a finite state machine (or many!) inside
93 your model and you are in charge of querying and persisting the state.
94 Here's an example of how to use it with an ActiveRecord model:
95
bf2caab @soveran Update examples.
authored
96 ``` ruby
97 class Event < ActiveRecord::Base
98 before_save :persist_confirmation
da6d94b @soveran Updated documentation and gemspec
authored
99
bf2caab @soveran Update examples.
authored
100 def confirm!
101 confirmation.trigger(:confirm)
102 end
da6d94b @soveran Updated documentation and gemspec
authored
103
bf2caab @soveran Update examples.
authored
104 def cancel!
105 confirmation.trigger(:cancel)
106 end
da6d94b @soveran Updated documentation and gemspec
authored
107
bf2caab @soveran Update examples.
authored
108 def reset!
109 confirmation.trigger(:reset)
110 end
da6d94b @soveran Updated documentation and gemspec
authored
111
bf2caab @soveran Update examples.
authored
112 def confirmation
113 @confirmation ||= begin
114 fsm = MicroMachine.new(confirmation_state || "pending")
da6d94b @soveran Updated documentation and gemspec
authored
115
a8815e6 @soveran Add when and events methods.
authored
116 fsm.when(:confirm, "pending" => "confirmed")
117 fsm.when(:cancel, "confirmed" => "cancelled")
118 fsm.when(:reset, "confirmed" => "pending", "cancelled" => "pending")
da6d94b @soveran Updated documentation and gemspec
authored
119
bf2caab @soveran Update examples.
authored
120 fsm
da6d94b @soveran Updated documentation and gemspec
authored
121 end
bf2caab @soveran Update examples.
authored
122 end
123
124 private
da6d94b @soveran Updated documentation and gemspec
authored
125
bf2caab @soveran Update examples.
authored
126 def persist_confirmation
127 self.confirmation_state = confirmation.state
128 end
129 end
130 ```
131
132 This example asumes you have a `:confirmation_state` attribute in your
da6d94b @soveran Updated documentation and gemspec
authored
133 model. This may look like a very verbose implementation, but you gain a
134 lot in flexibility.
135
5ba65e4 @soveran Update documentation
authored
136 An alternative approach, using callbacks:
137
bf2caab @soveran Update examples.
authored
138 ``` ruby
139 class Event < ActiveRecord::Base
140 def confirm!
141 confirmation.trigger(:confirm)
142 end
143
144 def cancel!
145 confirmation.trigger(:cancel)
146 end
147
148 def reset!
149 confirmation.trigger(:reset)
150 end
151
152 def confirmation
153 @confirmation ||= begin
154 fsm = MicroMachine.new(confirmation_state || "pending")
155
a8815e6 @soveran Add when and events methods.
authored
156 fsm.when(:confirm, "pending" => "confirmed")
157 fsm.when(:cancel, "confirmed" => "cancelled")
158 fsm.when(:reset, "confirmed" => "pending", "cancelled" => "pending")
bf2caab @soveran Update examples.
authored
159
160 fsm.on(:any) { self.confirmation_state = confirmation.state }
161
162 fsm
5ba65e4 @soveran Update documentation
authored
163 end
bf2caab @soveran Update examples.
authored
164 end
165 end
166 ```
5ba65e4 @soveran Update documentation
authored
167
ddeffe0 @soveran Formatting changes for README.
authored
168 Now, on any transition the `confirmation_state` attribute in the model
169 will be updated.
5ba65e4 @soveran Update documentation
authored
170
b3df5b6 @soveran Updated README and changed micromachine API
authored
171 Installation
172 ------------
173
da6d94b @soveran Updated documentation and gemspec
authored
174 $ sudo gem install micromachine
b3df5b6 @soveran Updated README and changed micromachine API
authored
175
176 License
177 -------
178
bf2caab @soveran Update examples.
authored
179 Copyright (c) 2009 Michel Martens
b3df5b6 @soveran Updated README and changed micromachine API
authored
180
181 Permission is hereby granted, free of charge, to any person
182 obtaining a copy of this software and associated documentation
183 files (the "Software"), to deal in the Software without
184 restriction, including without limitation the rights to use,
185 copy, modify, merge, publish, distribute, sublicense, and/or sell
186 copies of the Software, and to permit persons to whom the
187 Software is furnished to do so, subject to the following
188 conditions:
189
190 The above copyright notice and this permission notice shall be
191 included in all copies or substantial portions of the Software.
192
193 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
194 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
195 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
196 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
197 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
198 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
199 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
200 OTHER DEALINGS IN THE SOFTWARE.
Something went wrong with that request. Please try again.