simple_state_machine 0.5.2 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +46 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +17 -0
  5. data/Changelog.rdoc +16 -0
  6. data/Gemfile +7 -0
  7. data/README.rdoc +154 -99
  8. data/examples/conversation.rb +5 -5
  9. data/examples/lamp.rb +5 -5
  10. data/examples/relationship.rb +8 -8
  11. data/examples/traffic_light.rb +3 -3
  12. data/examples/user.rb +8 -4
  13. data/gemfiles/Gemfile.activerecord-5.2.x +9 -0
  14. data/gemfiles/Gemfile.activerecord-6.0.x +9 -0
  15. data/gemfiles/Gemfile.activerecord-6.1.x +8 -0
  16. data/gemfiles/Gemfile.activerecord-main.x +9 -0
  17. data/gemfiles/Gemfile.basic +6 -0
  18. data/lib/simple_state_machine/.DS_Store +0 -0
  19. data/lib/simple_state_machine/active_record.rb +2 -64
  20. data/lib/simple_state_machine/decorator/active_record.rb +68 -0
  21. data/lib/simple_state_machine/decorator/default.rb +91 -0
  22. data/lib/simple_state_machine/railtie.rb +1 -1
  23. data/lib/simple_state_machine/simple_state_machine.rb +8 -251
  24. data/lib/simple_state_machine/state_machine.rb +88 -0
  25. data/lib/simple_state_machine/state_machine_definition.rb +72 -0
  26. data/lib/simple_state_machine/tools/graphviz.rb +21 -0
  27. data/lib/simple_state_machine/tools/inspector.rb +44 -0
  28. data/lib/simple_state_machine/transition.rb +40 -0
  29. data/lib/simple_state_machine/version.rb +1 -1
  30. data/lib/simple_state_machine.rb +13 -3
  31. data/lib/tasks/graphviz.rake +31 -0
  32. data/simple_state_machine.gemspec +14 -24
  33. data/spec/.DS_Store +0 -0
  34. data/spec/active_record_spec.rb +216 -179
  35. data/spec/{decorator_spec.rb → decorator/default_spec.rb} +32 -32
  36. data/spec/examples_spec.rb +17 -17
  37. data/spec/mountable_spec.rb +26 -14
  38. data/spec/simple_state_machine_spec.rb +128 -92
  39. data/spec/spec_helper.rb +18 -5
  40. data/spec/state_machine_definition_spec.rb +48 -34
  41. data/spec/state_machine_spec.rb +36 -2
  42. data/spec/tools/graphviz_spec.rb +30 -0
  43. data/spec/tools/inspector_spec.rb +70 -0
  44. metadata +54 -128
  45. data/autotest/discover.rb +0 -1
  46. data/lib/tasks/graphiz.rake +0 -13
  47. data/rails/graphiz.rake +0 -16
@@ -4,73 +4,11 @@ module SimpleStateMachine::ActiveRecord
4
4
  include SimpleStateMachine::Extendable
5
5
  include SimpleStateMachine::Inheritable
6
6
 
7
- class Decorator < SimpleStateMachine::Decorator
8
-
9
- # decorates subject with:
10
- # * {event_name}_and_save
11
- # * {event_name}_and_save!
12
- # * {event_name}!
13
- def decorate transition
14
- super transition
15
- event_name = transition.event_name.to_s
16
- event_name_and_save = "#{event_name}_and_save"
17
- unless @subject.method_defined?(event_name_and_save)
18
- @subject.send(:define_method, event_name_and_save) do |*args|
19
- old_state = self.send(self.class.state_machine_definition.state_method)
20
- send "with_managed_state_#{event_name}", *args
21
- if !self.errors.entries.empty?
22
- self.send("#{self.class.state_machine_definition.state_method}=", old_state)
23
- return false
24
- else
25
- if save
26
- return true
27
- else
28
- self.send("#{self.class.state_machine_definition.state_method}=", old_state)
29
- return false
30
- end
31
- end
32
- end
33
- end
34
- event_name_and_save_bang = "#{event_name_and_save}!"
35
- unless @subject.method_defined?(event_name_and_save_bang)
36
- @subject.send(:define_method, event_name_and_save_bang) do |*args|
37
- old_state = self.send(self.class.state_machine_definition.state_method)
38
- send "with_managed_state_#{event_name}", *args
39
- if !self.errors.entries.empty?
40
- self.send("#{self.class.state_machine_definition.state_method}=", old_state)
41
- raise ActiveRecord::RecordInvalid.new(self)
42
- end
43
- begin
44
- save!
45
- rescue ActiveRecord::RecordInvalid
46
- self.send("#{self.class.state_machine_definition.state_method}=", old_state)
47
- raise #re raise
48
- end
49
- end
50
- @subject.send :alias_method, "#{transition.event_name}!", event_name_and_save_bang
51
- end
52
- end
53
-
54
- protected
55
-
56
- def alias_event_methods event_name
57
- @subject.send(:define_method, "cannot_call_#{event_name}") do |*args|
58
- raise "You cannot call #{event_name}. Use #{event_name}! instead"
59
- end
60
- @subject.send :alias_method, "without_managed_state_#{event_name}", event_name
61
- @subject.send :alias_method, event_name, "cannot_call_#{event_name}"
62
- end
63
-
64
- def define_state_setter_method; end
65
-
66
- def define_state_getter_method; end
67
-
68
- end
69
-
70
7
  def state_machine_definition
71
8
  unless @state_machine_definition
72
9
  @state_machine_definition = SimpleStateMachine::StateMachineDefinition.new
73
- @state_machine_definition.lazy_decorator = lambda { Decorator.new(self) }
10
+ @state_machine_definition.decorator_class = SimpleStateMachine::Decorator::ActiveRecord
11
+ @state_machine_definition.subject = self
74
12
  end
75
13
  @state_machine_definition
76
14
  end
@@ -0,0 +1,68 @@
1
+ module SimpleStateMachine
2
+ module Decorator
3
+ class ActiveRecord < SimpleStateMachine::Decorator::Default
4
+
5
+ # decorates subject with:
6
+ # * {event_name}_and_save
7
+ # * {event_name}_and_save!
8
+ # * {event_name}!
9
+ # * {event_name}
10
+ def decorate transition
11
+ super transition
12
+ event_name = transition.event_name.to_s
13
+ decorate_save event_name
14
+ decorate_save! event_name
15
+ end
16
+
17
+ protected
18
+
19
+ def decorate_save event_name
20
+ event_name_and_save = "#{event_name}_and_save"
21
+ unless @subject.method_defined?(event_name_and_save)
22
+ @subject.send(:define_method, event_name_and_save) do |*args|
23
+ old_state = self.send(self.class.state_machine_definition.state_method)
24
+ send "#{event_name}_with_managed_state", *args
25
+ if self.errors.entries.empty? && save
26
+ true
27
+ else
28
+ self.send("#{self.class.state_machine_definition.state_method}=", old_state)
29
+ false
30
+ end
31
+ end
32
+ @subject.send :alias_method, "#{event_name}", event_name_and_save
33
+ end
34
+ end
35
+
36
+ def decorate_save! event_name
37
+ event_name_and_save_bang = "#{event_name}_and_save!"
38
+ unless @subject.method_defined?(event_name_and_save_bang)
39
+ @subject.send(:define_method, event_name_and_save_bang) do |*args|
40
+ old_state = self.send(self.class.state_machine_definition.state_method)
41
+ send "#{event_name}_with_managed_state", *args
42
+ if self.errors.entries.empty?
43
+ begin
44
+ save!
45
+ rescue ::ActiveRecord::RecordInvalid
46
+ self.send("#{self.class.state_machine_definition.state_method}=", old_state)
47
+ raise #re raise
48
+ end
49
+ else
50
+ self.send("#{self.class.state_machine_definition.state_method}=", old_state)
51
+ raise ::ActiveRecord::RecordInvalid.new(self)
52
+ end
53
+ end
54
+ @subject.send :alias_method, "#{event_name}!", event_name_and_save_bang
55
+ end
56
+ end
57
+
58
+ def alias_event_methods event_name
59
+ @subject.send :alias_method, "#{event_name}_without_managed_state", event_name
60
+ end
61
+
62
+ def define_state_setter_method; end
63
+
64
+ def define_state_getter_method; end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,91 @@
1
+ module SimpleStateMachine
2
+ module Decorator
3
+ ##
4
+ # Decorates @subject with methods to access the state machine
5
+ class Default
6
+
7
+ attr_writer :subject
8
+ def initialize(subject)
9
+ @subject = subject
10
+ end
11
+
12
+ def decorate transition
13
+ define_state_machine_method
14
+ define_state_getter_method
15
+ define_state_setter_method
16
+
17
+ define_state_helper_method(transition.from)
18
+ define_state_helper_method(transition.to)
19
+ define_event_method(transition.event_name)
20
+ decorate_event_method(transition.event_name)
21
+ end
22
+
23
+ private
24
+
25
+ def define_state_machine_method
26
+ unless any_method_defined?("state_machine")
27
+ @subject.send(:define_method, "state_machine") do
28
+ @state_machine ||= StateMachine.new(self)
29
+ end
30
+ end
31
+ end
32
+
33
+ def define_state_helper_method state
34
+ unless any_method_defined?("#{state.to_s}?")
35
+ @subject.send(:define_method, "#{state.to_s}?") do
36
+ self.send(self.class.state_machine_definition.state_method) == state.to_s
37
+ end
38
+ end
39
+ end
40
+
41
+ def define_event_method event_name
42
+ unless any_method_defined?("#{event_name}")
43
+ @subject.send(:define_method, "#{event_name}") {}
44
+ end
45
+ end
46
+
47
+ def decorate_event_method event_name
48
+ # TODO put in transaction for activeRecord?
49
+ unless @subject.method_defined?("#{event_name}_with_managed_state")
50
+ @subject.send(:define_method, "#{event_name}_with_managed_state") do |*args|
51
+ return state_machine.transition(event_name) do
52
+ send("#{event_name}_without_managed_state", *args)
53
+ end
54
+ end
55
+ alias_event_methods event_name
56
+ end
57
+ end
58
+
59
+ def define_state_setter_method
60
+ unless any_method_defined?("#{state_method}=")
61
+ @subject.send(:define_method, "#{state_method}=") do |new_state|
62
+ instance_variable_set(:"@#{self.class.state_machine_definition.state_method}", new_state)
63
+ end
64
+ end
65
+ end
66
+
67
+ def define_state_getter_method
68
+ unless any_method_defined?(state_method)
69
+ @subject.send(:attr_reader, state_method)
70
+ end
71
+ end
72
+
73
+ def any_method_defined?(method)
74
+ @subject.method_defined?(method) ||
75
+ @subject.protected_method_defined?(method) ||
76
+ @subject.private_method_defined?(method)
77
+ end
78
+
79
+ protected
80
+
81
+ def alias_event_methods event_name
82
+ @subject.send :alias_method, "#{event_name}_without_managed_state", event_name
83
+ @subject.send :alias_method, event_name, "#{event_name}_with_managed_state"
84
+ end
85
+
86
+ def state_method
87
+ @subject.state_machine_definition.state_method
88
+ end
89
+ end
90
+ end
91
+ end
@@ -4,7 +4,7 @@ require 'rails'
4
4
  module SimpleStateMachine
5
5
  class Railtie < Rails::Railtie
6
6
  rake_tasks do
7
- load "tasks/graphiz.rake"
7
+ load "tasks/graphviz.rake"
8
8
  end
9
9
  end
10
10
  end
@@ -1,17 +1,12 @@
1
1
  module SimpleStateMachine
2
2
 
3
- require 'cgi'
4
-
5
- class IllegalStateTransitionError < ::RuntimeError
6
- end
7
-
8
3
  ##
9
4
  # Allows class to mount a state_machine
10
5
  module Mountable
11
6
  def state_machine_definition
12
7
  unless @state_machine_definition
13
8
  @state_machine_definition = StateMachineDefinition.new
14
- @state_machine_definition.lazy_decorator = lambda { Decorator.new(self) }
9
+ @state_machine_definition.subject = self
15
10
  end
16
11
  @state_machine_definition
17
12
  end
@@ -22,22 +17,22 @@ module SimpleStateMachine
22
17
  state_machine_definition.decorator.decorate(transition)
23
18
  end
24
19
  end
20
+
21
+ def mount_state_machine mountable_class
22
+ self.state_machine_definition = mountable_class.new
23
+ self.state_machine_definition.subject = self
24
+ self.state_machine_definition.add_events
25
+ end
25
26
  end
26
27
  include Mountable
27
28
 
28
29
  ##
29
30
  # Adds state machine methods to the extended class
30
31
  module Extendable
31
-
32
32
  # mark the method as an event and specify how the state should transition
33
33
  def event event_name, state_transitions
34
- state_transitions.each do |froms, to|
35
- [froms].flatten.each do |from|
36
- state_machine_definition.add_transition(event_name, from, to)
37
- end
38
- end
34
+ state_machine_definition.define_event event_name, state_transitions
39
35
  end
40
-
41
36
  end
42
37
  include Extendable
43
38
 
@@ -54,242 +49,4 @@ module SimpleStateMachine
54
49
  end
55
50
  include Inheritable
56
51
 
57
- ##
58
- # Defines state machine transitions
59
- class StateMachineDefinition
60
-
61
- attr_writer :state_method, :decorator, :lazy_decorator
62
-
63
- def decorator
64
- @decorator ||= @lazy_decorator.call
65
- end
66
-
67
- def transitions
68
- @transitions ||= []
69
- end
70
-
71
- def add_transition event_name, from, to
72
- transition = Transition.new(event_name, from, to)
73
- transitions << transition
74
- decorator.decorate(transition)
75
- end
76
-
77
- def state_method
78
- @state_method ||= :state
79
- end
80
-
81
- # Human readable format: old_state.event! => new_state
82
- def to_s
83
- transitions.map(&:to_s).join("\n")
84
- end
85
-
86
- # Graphiz dot format for rendering as a directional graph
87
- def to_graphiz_dot
88
- transitions.map { |t| t.to_graphiz_dot }.join(";")
89
- end
90
-
91
- # Generates a url that renders states and events as a directional graph.
92
- # See http://code.google.com/apis/chart/docs/gallery/graphviz.html
93
- def google_chart_url
94
- "http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape to_graphiz_dot}}"
95
- end
96
- end
97
-
98
- ##
99
- # Defines the state machine used by the instance
100
- class StateMachine
101
-
102
- def initialize(subject)
103
- @subject = subject
104
- end
105
-
106
- # Returns the next state for the subject for event_name
107
- def next_state(event_name)
108
- transition = transitions.select{|t| t.is_transition_for?(event_name, @subject.send(state_method))}.first
109
- transition ? transition.to : nil
110
- end
111
-
112
- # Returns the error state for the subject for event_name and error
113
- def error_state(event_name, error)
114
- transition = transitions.select{|t| t.is_error_transition_for?(event_name, error) }.first
115
- transition ? transition.to : nil
116
- end
117
-
118
- # Transitions to the next state if next_state exists.
119
- # Calls illegal_event_callback event_name if no next_state is found
120
- def transition(event_name)
121
- if to = next_state(event_name)
122
- begin
123
- result = yield
124
- rescue => e
125
- if error_state = error_state(event_name, e)
126
- @subject.send("#{state_method}=", error_state)
127
- return result
128
- else
129
- raise
130
- end
131
- end
132
- # TODO refactor out to AR module
133
- if defined?(::ActiveRecord) && @subject.is_a?(::ActiveRecord::Base)
134
- if @subject.errors.entries.empty?
135
- @subject.send("#{state_method}=", to)
136
- return true
137
- else
138
- return false
139
- end
140
- else
141
- @subject.send("#{state_method}=", to)
142
- return result
143
- end
144
- else
145
- illegal_event_callback event_name
146
- end
147
- end
148
-
149
- private
150
-
151
- def state_machine_definition
152
- @subject.class.state_machine_definition
153
- end
154
-
155
- def transitions
156
- state_machine_definition.transitions
157
- end
158
-
159
- def state_method
160
- state_machine_definition.state_method
161
- end
162
-
163
- # override with your own implementation, like setting errors in your model
164
- def illegal_event_callback event_name
165
- raise IllegalStateTransitionError.new("You cannot '#{event_name}' when state is '#{@subject.send(state_method)}'")
166
- end
167
-
168
- end
169
-
170
- ##
171
- # Defines transitions for events
172
- class Transition
173
- attr_reader :event_name, :from, :to
174
- def initialize(event_name, from, to)
175
- @event_name = event_name.to_s
176
- @from = from.is_a?(Class) ? from : from.to_s
177
- @to = to.to_s
178
- end
179
-
180
- # returns true if it's a transition for event_name
181
- def is_transition_for?(event_name, subject_state)
182
- is_same_event?(event_name) && is_same_from?(subject_state)
183
- end
184
-
185
- # returns true if it's a error transition for event_name and error
186
- def is_error_transition_for?(event_name, error)
187
- is_same_event?(event_name) && error.class == from
188
- end
189
-
190
- def to_s
191
- "#{from}.#{event_name}! => #{to}"
192
- end
193
-
194
- def to_graphiz_dot
195
- %("#{from}"->"#{to}"[label=#{event_name}])
196
- end
197
-
198
-
199
- private
200
-
201
- def is_same_event?(event_name)
202
- self.event_name == event_name.to_s
203
- end
204
-
205
- def is_same_from?(subject_from)
206
- from.to_s == 'all' || subject_from.to_s == from.to_s
207
- end
208
- end
209
-
210
- ##
211
- # Decorates @subject with methods to access the state machine
212
- class Decorator
213
-
214
- attr_writer :subject
215
- def initialize(subject)
216
- @subject = subject
217
- define_state_machine_method
218
- define_state_getter_method
219
- define_state_setter_method
220
- end
221
-
222
- def decorate transition
223
- define_state_helper_method(transition.from)
224
- define_state_helper_method(transition.to)
225
- define_event_method(transition.event_name)
226
- decorate_event_method(transition.event_name)
227
- end
228
-
229
- private
230
-
231
- def define_state_machine_method
232
- @subject.send(:define_method, "state_machine") do
233
- @state_machine ||= StateMachine.new(self)
234
- end
235
- end
236
-
237
- def define_state_helper_method state
238
- unless any_method_defined?("#{state.to_s}?")
239
- @subject.send(:define_method, "#{state.to_s}?") do
240
- self.send(self.class.state_machine_definition.state_method) == state.to_s
241
- end
242
- end
243
- end
244
-
245
- def define_event_method event_name
246
- unless any_method_defined?("#{event_name}")
247
- @subject.send(:define_method, "#{event_name}") {}
248
- end
249
- end
250
-
251
- def decorate_event_method event_name
252
- # TODO put in transaction for activeRecord?
253
- unless @subject.method_defined?("with_managed_state_#{event_name}")
254
- @subject.send(:define_method, "with_managed_state_#{event_name}") do |*args|
255
- return state_machine.transition(event_name) do
256
- send("without_managed_state_#{event_name}", *args)
257
- end
258
- end
259
- alias_event_methods event_name
260
- end
261
- end
262
-
263
- def define_state_setter_method
264
- unless any_method_defined?("#{state_method}=")
265
- @subject.send(:define_method, "#{state_method}=") do |new_state|
266
- instance_variable_set(:"@#{self.class.state_machine_definition.state_method}", new_state)
267
- end
268
- end
269
- end
270
-
271
- def define_state_getter_method
272
- unless any_method_defined?(state_method)
273
- @subject.send(:attr_reader, state_method)
274
- end
275
- end
276
-
277
- def any_method_defined?(method)
278
- @subject.method_defined?(method) ||
279
- @subject.protected_method_defined?(method) ||
280
- @subject.private_method_defined?(method)
281
- end
282
-
283
- protected
284
-
285
- def alias_event_methods event_name
286
- @subject.send :alias_method, "without_managed_state_#{event_name}", event_name
287
- @subject.send :alias_method, event_name, "with_managed_state_#{event_name}"
288
- end
289
-
290
- def state_method
291
- @subject.state_machine_definition.state_method
292
- end
293
- end
294
-
295
52
  end
@@ -0,0 +1,88 @@
1
+ module SimpleStateMachine
2
+
3
+ class IllegalStateTransitionError < ::RuntimeError; end
4
+
5
+ ##
6
+ # Defines the state machine used by the instance
7
+ class StateMachine
8
+ attr_reader :raised_error
9
+
10
+ def initialize(subject)
11
+ @subject = subject
12
+ end
13
+
14
+ # Returns the next state for the subject for event_name
15
+ def next_state(event_name)
16
+ transition = transitions.select{|t| t.is_transition_for?(event_name, @subject.send(state_method))}.first
17
+ transition ? transition.to : nil
18
+ end
19
+
20
+ # Returns the error state for the subject for event_name and error
21
+ def error_state(event_name, error)
22
+ transition = transitions.select{|t| t.is_error_transition_for?(event_name, error) }.first
23
+ transition ? transition.to : nil
24
+ end
25
+
26
+ # Transitions to the next state if next_state exists.
27
+ # When an error occurs, it uses the error to determine next state.
28
+ # If no next state can be determined it transitions to the default error
29
+ # state if defined, otherwise the error is re-raised.
30
+ # Calls illegal_event_callback event_name if no next_state is found
31
+ def transition(event_name)
32
+ clear_raised_error
33
+ if to = next_state(event_name)
34
+ begin
35
+ result = yield
36
+ rescue => e
37
+ error_state = error_state(event_name, e) ||
38
+ state_machine_definition.default_error_state
39
+ if error_state
40
+ @raised_error = e
41
+ @subject.send("#{state_method}=", error_state)
42
+ return result
43
+ else
44
+ raise
45
+ end
46
+ end
47
+ # TODO refactor out to AR module
48
+ if defined?(::ActiveRecord) && @subject.is_a?(::ActiveRecord::Base)
49
+ if @subject.errors.entries.empty?
50
+ @subject.send("#{state_method}=", to)
51
+ return true
52
+ else
53
+ return false
54
+ end
55
+ else
56
+ @subject.send("#{state_method}=", to)
57
+ return result
58
+ end
59
+ else
60
+ illegal_event_callback event_name
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def clear_raised_error
67
+ @raised_error = nil
68
+ end
69
+
70
+ def state_machine_definition
71
+ @subject.class.state_machine_definition
72
+ end
73
+
74
+ def transitions
75
+ state_machine_definition.transitions
76
+ end
77
+
78
+ def state_method
79
+ state_machine_definition.state_method
80
+ end
81
+
82
+ # override with your own implementation, like setting errors in your model
83
+ def illegal_event_callback event_name
84
+ raise IllegalStateTransitionError.new("You cannot '#{event_name}' when state is '#{@subject.send(state_method)}'")
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,72 @@
1
+ module SimpleStateMachine
2
+ ##
3
+ # Defines state machine transitions
4
+ class StateMachineDefinition
5
+
6
+ attr_writer :default_error_state, :state_method, :subject, :decorator,
7
+ :decorator_class
8
+
9
+ def decorator
10
+ @decorator ||= decorator_class.new(@subject)
11
+ end
12
+
13
+ def decorator_class
14
+ @decorator_class ||= Decorator::Default
15
+ end
16
+
17
+ def default_error_state
18
+ @default_error_state && @default_error_state.to_s
19
+ end
20
+
21
+ def transitions
22
+ @transitions ||= []
23
+ end
24
+
25
+ def define_event event_name, state_transitions
26
+ state_transitions.each do |froms, to|
27
+ [froms].flatten.each do |from|
28
+ add_transition(event_name, from, to)
29
+ end
30
+ end
31
+ end
32
+
33
+ def add_transition event_name, from, to
34
+ transition = Transition.new(event_name, from, to)
35
+ transitions << transition
36
+ decorator.decorate(transition)
37
+ end
38
+
39
+ def state_method
40
+ @state_method ||= :state
41
+ end
42
+
43
+ # Human readable format: old_state.event! => new_state
44
+ def to_s
45
+ transitions.map(&:to_s).join("\n")
46
+ end
47
+
48
+ module Mountable
49
+ def event event_name, state_transitions
50
+ events << [event_name, state_transitions]
51
+ end
52
+
53
+ def events
54
+ @events ||= []
55
+ end
56
+
57
+ module InstanceMethods
58
+ def add_events
59
+ self.class.events.each do |event_name, state_transitions|
60
+ define_event event_name, state_transitions
61
+ end
62
+ end
63
+ end
64
+ end
65
+ extend Mountable
66
+ include Mountable::InstanceMethods
67
+
68
+ # TODO only include in rake task?
69
+ include ::SimpleStateMachine::Tools::Graphviz
70
+ include ::SimpleStateMachine::Tools::Inspector
71
+ end
72
+ end