verborghs-state_machine 0.9.4

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 (89) hide show
  1. data/CHANGELOG.rdoc +360 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +635 -0
  4. data/Rakefile +77 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine/assertions.rb +36 -0
  28. data/lib/state_machine/callback.rb +241 -0
  29. data/lib/state_machine/condition_proxy.rb +106 -0
  30. data/lib/state_machine/eval_helpers.rb +83 -0
  31. data/lib/state_machine/event.rb +267 -0
  32. data/lib/state_machine/event_collection.rb +122 -0
  33. data/lib/state_machine/extensions.rb +149 -0
  34. data/lib/state_machine/guard.rb +230 -0
  35. data/lib/state_machine/initializers/merb.rb +1 -0
  36. data/lib/state_machine/initializers/rails.rb +5 -0
  37. data/lib/state_machine/initializers.rb +4 -0
  38. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  40. data/lib/state_machine/integrations/active_model.rb +445 -0
  41. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  42. data/lib/state_machine/integrations/active_record.rb +522 -0
  43. data/lib/state_machine/integrations/data_mapper/observer.rb +175 -0
  44. data/lib/state_machine/integrations/data_mapper.rb +379 -0
  45. data/lib/state_machine/integrations/mongo_mapper.rb +309 -0
  46. data/lib/state_machine/integrations/sequel.rb +356 -0
  47. data/lib/state_machine/integrations.rb +83 -0
  48. data/lib/state_machine/machine.rb +1645 -0
  49. data/lib/state_machine/machine_collection.rb +64 -0
  50. data/lib/state_machine/matcher.rb +123 -0
  51. data/lib/state_machine/matcher_helpers.rb +54 -0
  52. data/lib/state_machine/node_collection.rb +152 -0
  53. data/lib/state_machine/state.rb +260 -0
  54. data/lib/state_machine/state_collection.rb +112 -0
  55. data/lib/state_machine/transition.rb +399 -0
  56. data/lib/state_machine/transition_collection.rb +244 -0
  57. data/lib/state_machine.rb +421 -0
  58. data/lib/tasks/state_machine.rake +1 -0
  59. data/lib/tasks/state_machine.rb +27 -0
  60. data/test/files/en.yml +9 -0
  61. data/test/files/switch.rb +11 -0
  62. data/test/functional/state_machine_test.rb +980 -0
  63. data/test/test_helper.rb +4 -0
  64. data/test/unit/assertions_test.rb +40 -0
  65. data/test/unit/callback_test.rb +728 -0
  66. data/test/unit/condition_proxy_test.rb +328 -0
  67. data/test/unit/eval_helpers_test.rb +222 -0
  68. data/test/unit/event_collection_test.rb +324 -0
  69. data/test/unit/event_test.rb +795 -0
  70. data/test/unit/guard_test.rb +909 -0
  71. data/test/unit/integrations/active_model_test.rb +956 -0
  72. data/test/unit/integrations/active_record_test.rb +1918 -0
  73. data/test/unit/integrations/data_mapper_test.rb +1814 -0
  74. data/test/unit/integrations/mongo_mapper_test.rb +1382 -0
  75. data/test/unit/integrations/sequel_test.rb +1492 -0
  76. data/test/unit/integrations_test.rb +50 -0
  77. data/test/unit/invalid_event_test.rb +7 -0
  78. data/test/unit/invalid_transition_test.rb +7 -0
  79. data/test/unit/machine_collection_test.rb +565 -0
  80. data/test/unit/machine_test.rb +2349 -0
  81. data/test/unit/matcher_helpers_test.rb +37 -0
  82. data/test/unit/matcher_test.rb +155 -0
  83. data/test/unit/node_collection_test.rb +207 -0
  84. data/test/unit/state_collection_test.rb +280 -0
  85. data/test/unit/state_machine_test.rb +31 -0
  86. data/test/unit/state_test.rb +848 -0
  87. data/test/unit/transition_collection_test.rb +2098 -0
  88. data/test/unit/transition_test.rb +1384 -0
  89. metadata +176 -0
@@ -0,0 +1,324 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EventCollectionByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machine = StateMachine::Machine.new(Class.new)
6
+ @events = StateMachine::EventCollection.new(@machine)
7
+ end
8
+
9
+ def test_should_not_have_any_nodes
10
+ assert_equal 0, @events.length
11
+ end
12
+
13
+ def test_should_have_a_machine
14
+ assert_equal @machine, @events.machine
15
+ end
16
+
17
+ def test_should_not_have_any_valid_events_for_an_object
18
+ assert @events.valid_for(@object).empty?
19
+ end
20
+
21
+ def test_should_not_have_any_transitions_for_an_object
22
+ assert @events.transitions_for(@object).empty?
23
+ end
24
+ end
25
+
26
+ class EventCollectionTest < Test::Unit::TestCase
27
+ def setup
28
+ machine = StateMachine::Machine.new(Class.new, :namespace => 'alarm')
29
+ @events = StateMachine::EventCollection.new(machine)
30
+
31
+ @events << @open = StateMachine::Event.new(machine, :enable)
32
+ end
33
+
34
+ def test_should_index_by_name
35
+ assert_equal @open, @events[:enable, :name]
36
+ end
37
+
38
+ def test_should_index_by_name_by_default
39
+ assert_equal @open, @events[:enable]
40
+ end
41
+
42
+ def test_should_index_by_qualified_name
43
+ assert_equal @open, @events[:enable_alarm, :qualified_name]
44
+ end
45
+ end
46
+
47
+ class EventCollectionWithEventsWithTransitionsTest < Test::Unit::TestCase
48
+ def setup
49
+ @klass = Class.new
50
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
51
+ @events = StateMachine::EventCollection.new(@machine)
52
+
53
+ @machine.state :idling, :stalled
54
+ @machine.event :ignite
55
+
56
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
57
+ @ignite.transition :parked => :idling
58
+ @ignite.transition :stalled => :idling
59
+ end
60
+
61
+ def test_should_only_include_valid_events_for_an_object
62
+ object = @klass.new
63
+ object.state = 'parked'
64
+ assert_equal [@ignite], @events.valid_for(object)
65
+
66
+ object.state = 'stalled'
67
+ assert_equal [@ignite], @events.valid_for(object)
68
+
69
+ object.state = 'idling'
70
+ assert_equal [], @events.valid_for(object)
71
+ end
72
+
73
+ def test_should_only_include_valid_transitions_for_an_object
74
+ object = @klass.new
75
+ object.state = 'parked'
76
+ assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
77
+
78
+ object.state = 'stalled'
79
+ assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'stalled', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
80
+
81
+ object.state = 'idling'
82
+ assert_equal [], @events.transitions_for(object)
83
+ end
84
+
85
+ def test_should_filter_valid_transitions_for_an_object_if_requirements_specified
86
+ object = @klass.new
87
+ assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'stalled', :to => 'idling'}], @events.transitions_for(object, :from => :stalled).map {|transition| transition.attributes}
88
+ assert_equal [], @events.transitions_for(object, :from => :idling).map {|transition| transition.attributes}
89
+ end
90
+ end
91
+
92
+ class EventCollectionWithMultipleEventsTest < Test::Unit::TestCase
93
+ def setup
94
+ @klass = Class.new
95
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
96
+ @events = StateMachine::EventCollection.new(@machine)
97
+
98
+ @machine.state :first_gear
99
+ @machine.event :park, :shift_down
100
+
101
+ @events << @park = StateMachine::Event.new(@machine, :park)
102
+ @park.transition :first_gear => :parked
103
+
104
+ @events << @shift_down = StateMachine::Event.new(@machine, :shift_down)
105
+ @shift_down.transition :first_gear => :parked
106
+ end
107
+
108
+ def test_should_only_include_all_valid_events_for_an_object
109
+ object = @klass.new
110
+ object.state = 'first_gear'
111
+ assert_equal [@park, @shift_down], @events.valid_for(object)
112
+ end
113
+ end
114
+
115
+ class EventCollectionWithoutMachineActionTest < Test::Unit::TestCase
116
+ def setup
117
+ @klass = Class.new
118
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
119
+ @events = StateMachine::EventCollection.new(@machine)
120
+
121
+ @machine.event :ignite
122
+ @events << StateMachine::Event.new(@machine, :ignite)
123
+
124
+ @object = @klass.new
125
+ end
126
+
127
+ def test_should_not_have_an_attribute_transition
128
+ assert_nil @events.attribute_transition_for(@object)
129
+ end
130
+ end
131
+
132
+ class EventCollectionAttributeWithMachineActionTest < Test::Unit::TestCase
133
+ def setup
134
+ @klass = Class.new do
135
+ def save
136
+ end
137
+ end
138
+
139
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
140
+ @events = StateMachine::EventCollection.new(@machine)
141
+
142
+ @machine.event :ignite
143
+ @machine.state :parked, :idling
144
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
145
+
146
+ @object = @klass.new
147
+ end
148
+
149
+ def test_should_not_have_transition_if_nil
150
+ @object.state_event = nil
151
+ assert_nil @events.attribute_transition_for(@object)
152
+ end
153
+
154
+ def test_should_not_have_transition_if_empty
155
+ @object.state_event = ''
156
+ assert_nil @events.attribute_transition_for(@object)
157
+ end
158
+
159
+ def test_should_have_invalid_transition_if_invalid_event_specified
160
+ @object.state_event = 'invalid'
161
+ assert_equal false, @events.attribute_transition_for(@object)
162
+ end
163
+
164
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
165
+ @object.state_event = 'ignite'
166
+ assert_equal false, @events.attribute_transition_for(@object)
167
+ end
168
+
169
+ def test_should_have_valid_transition_if_event_can_be_fired
170
+ @ignite.transition :parked => :idling
171
+ @object.state_event = 'ignite'
172
+
173
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
174
+ end
175
+
176
+ def test_should_have_valid_transition_if_already_defined_in_transition_cache
177
+ @ignite.transition :parked => :idling
178
+ @object.state_event = nil
179
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
180
+
181
+ assert_equal transition, @events.attribute_transition_for(@object)
182
+ end
183
+
184
+ def test_should_use_transition_cache_if_both_event_and_transition_are_present
185
+ @ignite.transition :parked => :idling
186
+ @object.state_event = 'ignite'
187
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
188
+
189
+ assert_equal transition, @events.attribute_transition_for(@object)
190
+ end
191
+ end
192
+
193
+ class EventCollectionAttributeWithNamespacedMachineTest < Test::Unit::TestCase
194
+ def setup
195
+ @klass = Class.new do
196
+ def save
197
+ end
198
+ end
199
+
200
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
201
+ @events = StateMachine::EventCollection.new(@machine)
202
+
203
+ @machine.event :disable
204
+ @machine.state :active, :off
205
+ @events << @disable = StateMachine::Event.new(@machine, :disable)
206
+
207
+ @object = @klass.new
208
+ end
209
+
210
+ def test_should_not_have_transition_if_nil
211
+ @object.state_event = nil
212
+ assert_nil @events.attribute_transition_for(@object)
213
+ end
214
+
215
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
216
+ @object.state_event = 'disable'
217
+ assert_equal false, @events.attribute_transition_for(@object)
218
+ end
219
+
220
+ def test_should_have_valid_transition_if_event_can_be_fired
221
+ @disable.transition :active => :off
222
+ @object.state_event = 'disable'
223
+
224
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
225
+ end
226
+ end
227
+
228
+ class EventCollectionWithValidationsTest < Test::Unit::TestCase
229
+ def setup
230
+ StateMachine::Integrations.const_set('Custom', Module.new do
231
+ def invalidate(object, attribute, message, values = [])
232
+ (object.errors ||= []) << generate_message(message, values)
233
+ end
234
+
235
+ def reset(object)
236
+ object.errors = []
237
+ end
238
+ end)
239
+
240
+ @klass = Class.new do
241
+ attr_accessor :errors
242
+
243
+ def initialize
244
+ @errors = []
245
+ super
246
+ end
247
+ end
248
+
249
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
250
+ @events = StateMachine::EventCollection.new(@machine)
251
+
252
+ @machine.event :ignite
253
+ @parked, @idling = @machine.state :parked, :idling
254
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
255
+
256
+ @object = @klass.new
257
+ end
258
+
259
+ def test_should_invalidate_if_invalid_event_specified
260
+ @object.state_event = 'invalid'
261
+ @events.attribute_transition_for(@object, true)
262
+
263
+ assert_equal ['is invalid'], @object.errors
264
+ end
265
+
266
+ def test_should_invalidate_if_event_cannot_be_fired
267
+ @object.state = 'idling'
268
+ @object.state_event = 'ignite'
269
+ @events.attribute_transition_for(@object, true)
270
+
271
+ assert_equal ['cannot transition when idling'], @object.errors
272
+ end
273
+
274
+ def test_should_invalidate_with_human_name_if_invalid_event_specified
275
+ @idling.human_name = 'waiting'
276
+ @object.state = 'idling'
277
+ @object.state_event = 'ignite'
278
+ @events.attribute_transition_for(@object, true)
279
+
280
+ assert_equal ['cannot transition when waiting'], @object.errors
281
+ end
282
+
283
+ def test_should_not_invalidate_event_can_be_fired
284
+ @ignite.transition :parked => :idling
285
+ @object.state_event = 'ignite'
286
+ @events.attribute_transition_for(@object, true)
287
+
288
+ assert_equal [], @object.errors
289
+ end
290
+
291
+ def teardown
292
+ StateMachine::Integrations.send(:remove_const, 'Custom')
293
+ end
294
+ end
295
+
296
+ class EventCollectionWithCustomMachineAttributeTest < Test::Unit::TestCase
297
+ def setup
298
+ @klass = Class.new do
299
+ def save
300
+ end
301
+ end
302
+
303
+ @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :parked, :action => :save)
304
+ @events = StateMachine::EventCollection.new(@machine)
305
+
306
+ @machine.event :ignite
307
+ @machine.state :parked, :idling
308
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
309
+
310
+ @object = @klass.new
311
+ end
312
+
313
+ def test_should_not_have_transition_if_nil
314
+ @object.state_event = nil
315
+ assert_nil @events.attribute_transition_for(@object)
316
+ end
317
+
318
+ def test_should_have_valid_transition_if_event_can_be_fired
319
+ @ignite.transition :parked => :idling
320
+ @object.state_event = 'ignite'
321
+
322
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
323
+ end
324
+ end