anthonyw-simple_state 0.1.3 → 0.2.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.
@@ -10,38 +10,49 @@ There are several existing implementations of state machines in Ruby, notably
10
10
  cumbersome, when all I really needed was a lightweight means for setting the
11
11
  state of a class instance, and transitioning from one state to another.
12
12
 
13
- There is no support for adding your own code to customise transitions, nor is
14
- is there any support for callbacks or event guards. It's called
15
- **Simple**State for a reason! The library adds some helper methods to your
16
- class, keeps track of the valid states, makes sure that a transition is
17
- permitted, and that's about it.
13
+ There is no explicit support for adding your own code to customise
14
+ transitions, nor is there any support for callbacks or event guards (although
15
+ you can still do similar things fairly trivially). It's called **Simple**State
16
+ for a reason! The library adds some helper methods to your class, keeps track
17
+ of the valid states, makes sure that a transition is permitted, and that's
18
+ about it.
18
19
 
19
20
  ## Why use SimpleState?
20
21
 
21
- * Lightweight.
22
- * method_missing isn't used. ;)
23
- * No dependencies.
24
- * No extensions to core classes.
25
- * Tested on Ruby 1.8.6 (p287), 1.8.7 (p72), and 1.9.1 (p0).
26
- * Uses an API similar to Workflow, which I find to be more logical than that
27
- in the acts\_as\_state\_machine family.
22
+ <ul style="margin-top: 1em">
23
+ <li>Lightweight.</li>
24
+ <li>method_missing isn't used. ;)</li>
25
+ <li>No dependencies.</li>
26
+ <li>No extensions to core classes.</li>
27
+ <li>Tested on Ruby 1.8.6 (p287), 1.8.7 (p72), and 1.9.1 (p0).</li>
28
+ <li>Uses an API similar to Workflow, which I find to be more logical than
29
+ that in the acts_as_state_machine family.</li>
30
+ </ul>
28
31
 
29
32
  ## Why use something else?
30
33
 
31
- * SimpleState has no support for customising transitions with your own code.
32
- * No support for callbacks.
33
- * No support for guard conditions.
34
- * SimpleState forces you to use an attribute called `state` - other libraries
35
- let you choose whatever name you want.
36
- * Uses a class variable to keep track of transitions - doesn't lend itself
37
- all that well to subclassing your state machines.
38
-
39
- The three libraries mentioned above have support for callbacks and guard
40
- conditions: if you need these features then you'd be better off choosing one
41
- of those libraries instead of SimpleState.
34
+ <ul style="margin-top: 1em">
35
+ <li>The three libraries mentioned above make available, as part of their
36
+ DSL, a means of customising events/transitions with your own code.
37
+ SimpleState makes no such provision, however you can mimic the behaviour
38
+ quite easily as documented in example 3, below.</li>
39
+ <li>Similarly, some other libraries provide the ability to add guard
40
+ conditions -- a condition which must be satisfied before a transition
41
+ can take place. SimpleState also does explicitly support this, however it
42
+ is possible by adapting example 3.
43
+ <li>SimpleState forces you to use an attribute called `state` - other libraries
44
+ let you choose whatever name you want.</li>
45
+ <li>Uses a class variable to keep track of transitions - doesn't lend itself
46
+ all that well to subclassing your state machines.</li>
47
+ </ul>
48
+
49
+ If SimpleState's limitations are too much for you, then you are probably
50
+ better off choosing one of the other libraries instead.
42
51
 
43
52
  ## Examples
44
53
 
54
+ ### Example 1: Basic usage
55
+
45
56
  require 'rubygems'
46
57
  require 'simple_state'
47
58
 
@@ -96,13 +107,15 @@ state of the instance.
96
107
 
97
108
  # etc...
98
109
 
110
+ ### Example 2: Events in multiple states
111
+
99
112
  It is possible for the same event to be used in multiple states:
100
113
 
101
114
  state :not_started do
102
115
  event :start, :transitions_to => :started
103
116
  event :cancel, :transitions_to => :cancelled # <--
104
117
  end
105
-
118
+
106
119
  state :started do
107
120
  event :finish, :transitions_to => :finished
108
121
  event :cancel, :transitions_to => :cancelled # <--
@@ -125,6 +138,73 @@ current state:
125
138
  state :cancelled
126
139
  state :cancelled_before_start
127
140
 
141
+ ### Example 3: Customising event transitions
142
+
143
+ If the built in event methods aren't sufficient and you need to do extra stuff
144
+ to your class during a particular event, you can simply override the method;
145
+ the original method is available via `super`:
146
+
147
+ class OverriddenEvent
148
+ extend SimpleState
149
+
150
+ state_machine do
151
+ state :start do
152
+ event :start, :transitions_to => :started
153
+ end
154
+
155
+ state :started
156
+ end
157
+
158
+ def start!
159
+ puts "Before super() : state=#{self.state}"
160
+ ret = super
161
+ puts "After super() : state=#{self.state}"
162
+ ret
163
+ end
164
+ end
165
+
166
+ OverriddenEvent.new.start!
167
+ # => Before super() : state=start
168
+ # => After super() : state=finished
169
+ # => :started
170
+
171
+ If the event transition isn't valid, super will simply return false, otherwise
172
+ it will return the symbol representing the new state.
173
+
174
+ def start!
175
+ if new_state = super
176
+ puts "Started! The new state is #{self.state}"
177
+ else
178
+ puts "Could not start!"
179
+ end
180
+
181
+ new_state
182
+ end
183
+
184
+ machine = OverriddenEvent.new
185
+ machine.start!
186
+ => Started! The new state is finished
187
+ => :started
188
+
189
+ machine.start!
190
+ => Could not start!
191
+ => false
192
+
193
+ If you need to know whether a transition will be permitted before you call
194
+ super(), SimpleState provides `#event_permitted?`, expecting you to provide a
195
+ symbol representing the event.
196
+
197
+ machine.event_permitted?(:start)
198
+ # => true|false
199
+
200
+ This also provides an easy means for creating guard conditions:
201
+
202
+ def start!
203
+ if event_permitted?(:start) && SomeExternalService.can_start?(self)
204
+ super
205
+ end
206
+ end
207
+
128
208
  ## ORM Integration
129
209
 
130
210
  SimpleState should play nicely with your ORM of choice. When an object's state
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
2
+ :minor: 2
3
+ :patch: 0
3
4
  :major: 0
4
- :minor: 1
@@ -32,11 +32,31 @@ module SimpleState
32
32
 
33
33
  # Create an anonymous module which will be added to the state machine
34
34
  # class's inheritance chain.
35
- mod = @mod = Module.new do
35
+ mod = @mod = Module.new
36
+ mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
37
  def self.inspect
37
- "SimpleState::#{@klass}StateMachine"
38
+ "SimpleState::#{@klass}AnonMixin"
38
39
  end
39
- end
40
+
41
+ # Handles the change of state.
42
+ # @api private
43
+ def _change_state_using_event!(event)
44
+ self.state = self.class._determine_new_state(self.state, event)
45
+ end
46
+
47
+ # Returns if the passed event is permitted with the instance in it's
48
+ # current state.
49
+ # @api public
50
+ def event_permitted?(event)
51
+ self.class._event_permitted?(self.state, event)
52
+ end
53
+
54
+ # Returns true if the given symbol matches the current state.
55
+ # @api public
56
+ def in_state?(state)
57
+ self.state == state
58
+ end
59
+ RUBY
40
60
 
41
61
  # Declare the state machine rules.
42
62
  instance_eval(&blk)
@@ -61,9 +81,9 @@ module SimpleState
61
81
  @klass.initial_state ||= name
62
82
 
63
83
  @mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
64
- def #{name}? # def prepared?
65
- self.state == :#{name} # self.state == :prepared
66
- end # end
84
+ def #{name}? # def prepared?
85
+ in_state?(:#{name}) # self.state == :prepared
86
+ end # end
67
87
  RUBY
68
88
 
69
89
  # Define transitions for this state.
@@ -107,7 +127,7 @@ module SimpleState
107
127
  # Example:
108
128
  #
109
129
  # def process!
110
- # if self.class._valid_transition?(self.state, :process)
130
+ # if self.class._transition_permitted?(self.state, :process)
111
131
  # self.state =
112
132
  # self.class._determine_new_state(self.state, :process)
113
133
  # else
@@ -116,9 +136,8 @@ module SimpleState
116
136
  # end
117
137
  @module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
118
138
  def #{event}!
119
- if self.class._valid_transition?(self.state, :#{event})
120
- self.state =
121
- self.class._determine_new_state(self.state, :#{event})
139
+ if event_permitted?(:#{event})
140
+ _change_state_using_event!(:#{event})
122
141
  else
123
142
  false
124
143
  end
@@ -42,7 +42,7 @@ module SimpleState
42
42
  end
43
43
 
44
44
  # @api private
45
- def _valid_transition?(current, to)
45
+ def _event_permitted?(current, to)
46
46
  states[current] and not states[current].assoc(to).nil?
47
47
  end
48
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anthonyw-simple_state
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anthony Williams
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-10 00:00:00 -07:00
12
+ date: 2009-03-11 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15