pluginaweek-state_machine 0.7.6

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 (78) hide show
  1. data/CHANGELOG.rdoc +273 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +466 -0
  4. data/Rakefile +98 -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.rb +429 -0
  28. data/lib/state_machine/assertions.rb +36 -0
  29. data/lib/state_machine/callback.rb +189 -0
  30. data/lib/state_machine/condition_proxy.rb +94 -0
  31. data/lib/state_machine/eval_helpers.rb +67 -0
  32. data/lib/state_machine/event.rb +251 -0
  33. data/lib/state_machine/event_collection.rb +113 -0
  34. data/lib/state_machine/extensions.rb +158 -0
  35. data/lib/state_machine/guard.rb +219 -0
  36. data/lib/state_machine/integrations.rb +68 -0
  37. data/lib/state_machine/integrations/active_record.rb +444 -0
  38. data/lib/state_machine/integrations/active_record/locale.rb +10 -0
  39. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  40. data/lib/state_machine/integrations/data_mapper.rb +325 -0
  41. data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
  42. data/lib/state_machine/integrations/sequel.rb +292 -0
  43. data/lib/state_machine/machine.rb +1431 -0
  44. data/lib/state_machine/machine_collection.rb +146 -0
  45. data/lib/state_machine/matcher.rb +123 -0
  46. data/lib/state_machine/matcher_helpers.rb +54 -0
  47. data/lib/state_machine/node_collection.rb +152 -0
  48. data/lib/state_machine/state.rb +249 -0
  49. data/lib/state_machine/state_collection.rb +112 -0
  50. data/lib/state_machine/transition.rb +367 -0
  51. data/tasks/state_machine.rake +1 -0
  52. data/tasks/state_machine.rb +30 -0
  53. data/test/classes/switch.rb +11 -0
  54. data/test/functional/state_machine_test.rb +941 -0
  55. data/test/test_helper.rb +4 -0
  56. data/test/unit/assertions_test.rb +40 -0
  57. data/test/unit/callback_test.rb +455 -0
  58. data/test/unit/condition_proxy_test.rb +328 -0
  59. data/test/unit/eval_helpers_test.rb +129 -0
  60. data/test/unit/event_collection_test.rb +293 -0
  61. data/test/unit/event_test.rb +605 -0
  62. data/test/unit/guard_test.rb +862 -0
  63. data/test/unit/integrations/active_record_test.rb +1001 -0
  64. data/test/unit/integrations/data_mapper_test.rb +694 -0
  65. data/test/unit/integrations/sequel_test.rb +486 -0
  66. data/test/unit/integrations_test.rb +42 -0
  67. data/test/unit/invalid_event_test.rb +7 -0
  68. data/test/unit/invalid_transition_test.rb +7 -0
  69. data/test/unit/machine_collection_test.rb +710 -0
  70. data/test/unit/machine_test.rb +1910 -0
  71. data/test/unit/matcher_helpers_test.rb +37 -0
  72. data/test/unit/matcher_test.rb +155 -0
  73. data/test/unit/node_collection_test.rb +207 -0
  74. data/test/unit/state_collection_test.rb +280 -0
  75. data/test/unit/state_machine_test.rb +31 -0
  76. data/test/unit/state_test.rb +795 -0
  77. data/test/unit/transition_test.rb +1113 -0
  78. metadata +161 -0
@@ -0,0 +1,4 @@
1
+ require 'test/unit'
2
+
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ require File.dirname(__FILE__) + '/../init'
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class AssertionsTest < Test::Unit::TestCase
4
+ include StateMachine::Assertions
5
+
6
+ def default_test
7
+ end
8
+ end
9
+
10
+ class AssertValidKeysTest < AssertionsTest
11
+ def test_should_not_raise_exception_if_key_is_valid
12
+ assert_nothing_raised { assert_valid_keys({:name => 'foo', :value => 'bar'}, :name, :value, :force) }
13
+ end
14
+
15
+ def test_should_raise_exception_if_key_is_invalid
16
+ exception = assert_raise(ArgumentError) { assert_valid_keys({:name => 'foo', :value => 'bar', :invalid => true}, :name, :value, :force) }
17
+ assert_equal 'Invalid key(s): invalid', exception.message
18
+ end
19
+ end
20
+
21
+ class AssertExclusiveKeysTest < AssertionsTest
22
+ def test_should_not_raise_exception_if_no_keys_found
23
+ assert_nothing_raised { assert_exclusive_keys({:on => :park}, :only, :except) }
24
+ end
25
+
26
+ def test_should_not_raise_exception_if_one_key_found
27
+ assert_nothing_raised { assert_exclusive_keys({:only => :parked}, :only, :except) }
28
+ assert_nothing_raised { assert_exclusive_keys({:except => :parked}, :only, :except) }
29
+ end
30
+
31
+ def test_should_raise_exception_if_two_keys_found
32
+ exception = assert_raise(ArgumentError) { assert_exclusive_keys({:only => :parked, :except => :parked}, :only, :except) }
33
+ assert_equal 'Conflicting keys: only, except', exception.message
34
+ end
35
+
36
+ def test_should_raise_exception_if_multiple_keys_found
37
+ exception = assert_raise(ArgumentError) { assert_exclusive_keys({:only => :parked, :except => :parked, :on => :park}, :only, :except, :with) }
38
+ assert_equal 'Conflicting keys: only, except', exception.message
39
+ end
40
+ end
@@ -0,0 +1,455 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class CallbackTest < Test::Unit::TestCase
4
+ def test_should_raise_exception_if_no_methods_specified
5
+ exception = assert_raise(ArgumentError) { StateMachine::Callback.new }
6
+ assert_equal 'Method(s) for callback must be specified', exception.message
7
+ end
8
+
9
+ def test_should_not_raise_exception_if_method_specified_in_do_option
10
+ assert_nothing_raised { StateMachine::Callback.new(:do => :run) }
11
+ end
12
+
13
+ def test_should_not_raise_exception_if_method_specified_as_argument
14
+ assert_nothing_raised { StateMachine::Callback.new(:run) }
15
+ end
16
+
17
+ def test_should_not_raise_exception_if_method_specified_as_block
18
+ assert_nothing_raised { StateMachine::Callback.new(:run) {} }
19
+ end
20
+
21
+ def test_should_not_raise_exception_if_implicit_option_specified
22
+ assert_nothing_raised { StateMachine::Callback.new(:do => :run, :invalid => :valid) }
23
+ end
24
+
25
+ def test_should_not_bind_to_objects
26
+ assert !StateMachine::Callback.bind_to_object
27
+ end
28
+
29
+ def test_should_not_have_a_terminator
30
+ assert_nil StateMachine::Callback.terminator
31
+ end
32
+ end
33
+
34
+ class CallbackByDefaultTest < Test::Unit::TestCase
35
+ def setup
36
+ @callback = StateMachine::Callback.new(:do => lambda {})
37
+ end
38
+
39
+ def test_should_not_have_a_terminator
40
+ assert_nil @callback.terminator
41
+ end
42
+
43
+ def test_should_have_a_guard_with_all_matcher_requirements
44
+ assert_equal StateMachine::AllMatcher.instance, @callback.guard.event_requirement
45
+ assert_equal StateMachine::AllMatcher.instance, @callback.guard.state_requirements.first[:from]
46
+ assert_equal StateMachine::AllMatcher.instance, @callback.guard.state_requirements.first[:to]
47
+ end
48
+
49
+ def test_should_not_have_any_known_states
50
+ assert_equal [], @callback.known_states
51
+ end
52
+ end
53
+
54
+ class CallbackWithMethodArgumentTest < Test::Unit::TestCase
55
+ def setup
56
+ @callback = StateMachine::Callback.new(lambda {|*args| @args = args})
57
+
58
+ @object = Object.new
59
+ @result = @callback.call(@object)
60
+ end
61
+
62
+ def test_should_be_successful
63
+ assert @result
64
+ end
65
+
66
+ def test_should_call_with_empty_context
67
+ assert_equal [@object], @args
68
+ end
69
+ end
70
+
71
+ class CallbackWithMultipleMethodArgumentsTest < Test::Unit::TestCase
72
+ def setup
73
+ @callback = StateMachine::Callback.new(:run_1, :run_2)
74
+
75
+ class << @object = Object.new
76
+ attr_accessor :callbacks
77
+
78
+ def run_1
79
+ (@callbacks ||= []) << :run_1
80
+ end
81
+
82
+ def run_2
83
+ (@callbacks ||= []) << :run_2
84
+ end
85
+ end
86
+
87
+ @result = @callback.call(@object)
88
+ end
89
+
90
+ def test_should_be_successful
91
+ assert @result
92
+ end
93
+
94
+ def test_should_call_each_callback_in_order
95
+ assert_equal [:run_1, :run_2], @object.callbacks
96
+ end
97
+ end
98
+
99
+ class CallbackWithDoMethodTest < Test::Unit::TestCase
100
+ def setup
101
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| @args = args})
102
+
103
+ @object = Object.new
104
+ @result = @callback.call(@object)
105
+ end
106
+
107
+ def test_should_be_successful
108
+ assert @result
109
+ end
110
+
111
+ def test_should_call_with_empty_context
112
+ assert_equal [@object], @args
113
+ end
114
+ end
115
+
116
+ class CallbackWithMultipleDoMethodsTest < Test::Unit::TestCase
117
+ def setup
118
+ @callback = StateMachine::Callback.new(:do => [:run_1, :run_2])
119
+
120
+ class << @object = Object.new
121
+ attr_accessor :callbacks
122
+
123
+ def run_1
124
+ (@callbacks ||= []) << :run_1
125
+ end
126
+
127
+ def run_2
128
+ (@callbacks ||= []) << :run_2
129
+ end
130
+ end
131
+
132
+ @result = @callback.call(@object)
133
+ end
134
+
135
+ def test_should_be_successful
136
+ assert @result
137
+ end
138
+
139
+ def test_should_call_each_callback_in_order
140
+ assert_equal [:run_1, :run_2], @object.callbacks
141
+ end
142
+ end
143
+
144
+ class CallbackWithBlockTest < Test::Unit::TestCase
145
+ def setup
146
+ @callback = StateMachine::Callback.new do |*args|
147
+ @args = args
148
+ end
149
+
150
+ @object = Object.new
151
+ @result = @callback.call(@object)
152
+ end
153
+
154
+ def test_should_be_successful
155
+ assert @result
156
+ end
157
+
158
+ def test_should_call_with_empty_context
159
+ assert_equal [@object], @args
160
+ end
161
+ end
162
+
163
+ class CallbackWithMixedMethodsTest < Test::Unit::TestCase
164
+ def setup
165
+ @callback = StateMachine::Callback.new(:run_argument, :do => :run_do) do |object|
166
+ object.callbacks << :block
167
+ end
168
+
169
+ class << @object = Object.new
170
+ attr_accessor :callbacks
171
+
172
+ def run_argument
173
+ (@callbacks ||= []) << :argument
174
+ end
175
+
176
+ def run_do
177
+ (@callbacks ||= []) << :do
178
+ end
179
+ end
180
+
181
+ @result = @callback.call(@object)
182
+ end
183
+
184
+ def test_should_be_successful
185
+ assert @result
186
+ end
187
+
188
+ def test_should_call_each_callback_in_order
189
+ assert_equal [:argument, :do, :block], @object.callbacks
190
+ end
191
+ end
192
+
193
+ class CallbackWithExplicitRequirementsTest < Test::Unit::TestCase
194
+ def setup
195
+ @object = Object.new
196
+ @callback = StateMachine::Callback.new(:from => :parked, :to => :idling, :on => :ignite, :do => lambda {})
197
+ end
198
+
199
+ def test_should_call_with_empty_context
200
+ assert @callback.call(@object, {})
201
+ end
202
+
203
+ def test_should_not_call_if_from_not_included
204
+ assert !@callback.call(@object, :from => :idling)
205
+ end
206
+
207
+ def test_should_not_call_if_to_not_included
208
+ assert !@callback.call(@object, :to => :parked)
209
+ end
210
+
211
+ def test_should_not_call_if_on_not_included
212
+ assert !@callback.call(@object, :on => :park)
213
+ end
214
+
215
+ def test_should_call_if_all_requirements_met
216
+ assert @callback.call(@object, :from => :parked, :to => :idling, :on => :ignite)
217
+ end
218
+
219
+ def test_should_include_in_known_states
220
+ assert_equal [:parked, :idling], @callback.known_states
221
+ end
222
+ end
223
+
224
+ class CallbackWithImplicitRequirementsTest < Test::Unit::TestCase
225
+ def setup
226
+ @object = Object.new
227
+ @callback = StateMachine::Callback.new(:parked => :idling, :on => :ignite, :do => lambda {})
228
+ end
229
+
230
+ def test_should_call_with_empty_context
231
+ assert @callback.call(@object, {})
232
+ end
233
+
234
+ def test_should_not_call_if_from_not_included
235
+ assert !@callback.call(@object, :from => :idling)
236
+ end
237
+
238
+ def test_should_not_call_if_to_not_included
239
+ assert !@callback.call(@object, :to => :parked)
240
+ end
241
+
242
+ def test_should_not_call_if_on_not_included
243
+ assert !@callback.call(@object, :on => :park)
244
+ end
245
+
246
+ def test_should_call_if_all_requirements_met
247
+ assert @callback.call(@object, :from => :parked, :to => :idling, :on => :ignite)
248
+ end
249
+
250
+ def test_should_include_in_known_states
251
+ assert_equal [:parked, :idling], @callback.known_states
252
+ end
253
+ end
254
+
255
+ class CallbackWithIfConditionTest < Test::Unit::TestCase
256
+ def setup
257
+ @object = Object.new
258
+ end
259
+
260
+ def test_should_call_if_true
261
+ callback = StateMachine::Callback.new(:if => lambda {true}, :do => lambda {})
262
+ assert callback.call(@object)
263
+ end
264
+
265
+ def test_should_not_call_if_false
266
+ callback = StateMachine::Callback.new(:if => lambda {false}, :do => lambda {})
267
+ assert !callback.call(@object)
268
+ end
269
+ end
270
+
271
+ class CallbackWithUnlessConditionTest < Test::Unit::TestCase
272
+ def setup
273
+ @object = Object.new
274
+ end
275
+
276
+ def test_should_call_if_false
277
+ callback = StateMachine::Callback.new(:unless => lambda {false}, :do => lambda {})
278
+ assert callback.call(@object)
279
+ end
280
+
281
+ def test_should_not_call_if_true
282
+ callback = StateMachine::Callback.new(:unless => lambda {true}, :do => lambda {})
283
+ assert !callback.call(@object)
284
+ end
285
+ end
286
+
287
+ class CallbackWithoutTerminatorTest < Test::Unit::TestCase
288
+ def setup
289
+ @object = Object.new
290
+ end
291
+
292
+ def test_should_not_halt_if_result_is_false
293
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => nil)
294
+ assert_nothing_thrown { callback.call(@object) }
295
+ end
296
+ end
297
+
298
+ class CallbackWithTerminatorTest < Test::Unit::TestCase
299
+ def setup
300
+ @object = Object.new
301
+ end
302
+
303
+ def test_should_not_halt_if_terminator_does_not_match
304
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => lambda {|result| result == true})
305
+ assert_nothing_thrown { callback.call(@object) }
306
+ end
307
+
308
+ def test_should_halt_if_terminator_matches
309
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => lambda {|result| result == false})
310
+ assert_throws(:halt) { callback.call(@object) }
311
+ end
312
+
313
+ def test_should_halt_if_terminator_matches_any_method
314
+ callback = StateMachine::Callback.new(:do => [lambda {true}, lambda {false}], :terminator => lambda {|result| result == false})
315
+ assert_throws(:halt) { callback.call(@object) }
316
+ end
317
+ end
318
+
319
+ class CallbackWithoutArgumentsTest < Test::Unit::TestCase
320
+ def setup
321
+ @callback = StateMachine::Callback.new(:do => lambda {|object| @arg = object})
322
+
323
+ @object = Object.new
324
+ @callback.call(@object, {}, 1, 2, 3)
325
+ end
326
+
327
+ def test_should_call_method_with_object_as_argument
328
+ assert_equal @object, @arg
329
+ end
330
+ end
331
+
332
+ class CallbackWithArgumentsTest < Test::Unit::TestCase
333
+ def setup
334
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| @args = args})
335
+
336
+ @object = Object.new
337
+ @callback.call(@object, {}, 1, 2, 3)
338
+ end
339
+
340
+ def test_should_call_method_with_all_arguments
341
+ assert_equal [@object, 1, 2, 3], @args
342
+ end
343
+ end
344
+
345
+ class CallbackWithUnboundMethodTest < Test::Unit::TestCase
346
+ def setup
347
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| @context = args.unshift(self)})
348
+
349
+ @object = Object.new
350
+ @callback.call(@object, {}, 1, 2, 3)
351
+ end
352
+
353
+ def test_should_call_method_outside_the_context_of_the_object
354
+ assert_equal [self, @object, 1, 2, 3], @context
355
+ end
356
+ end
357
+
358
+ class CallbackWithBoundMethodTest < Test::Unit::TestCase
359
+ def setup
360
+ @object = Object.new
361
+ end
362
+
363
+ def test_should_call_method_within_the_context_of_the_object_for_block_methods
364
+ context = nil
365
+ callback = StateMachine::Callback.new(:do => lambda {|*args| context = [self] + args}, :bind_to_object => true)
366
+ callback.call(@object, {}, 1, 2, 3)
367
+
368
+ assert_equal [@object, 1, 2, 3], context
369
+ end
370
+
371
+ def test_should_ignore_option_for_symbolic_methods
372
+ class << @object
373
+ attr_reader :context
374
+
375
+ def after_ignite(*args)
376
+ @context = args
377
+ end
378
+ end
379
+
380
+ callback = StateMachine::Callback.new(:do => :after_ignite, :bind_to_object => true)
381
+ callback.call(@object)
382
+
383
+ assert_equal [], @object.context
384
+ end
385
+
386
+ def test_should_ignore_option_for_string_methods
387
+ callback = StateMachine::Callback.new(:do => '[1, 2, 3]', :bind_to_object => true)
388
+ assert callback.call(@object)
389
+ end
390
+ end
391
+
392
+ class CallbackWithMultipleBoundMethodsTest < Test::Unit::TestCase
393
+ def setup
394
+ @object = Object.new
395
+
396
+ first_context = nil
397
+ second_context = nil
398
+
399
+ @callback = StateMachine::Callback.new(:do => [lambda {first_context = self}, lambda {second_context = self}], :bind_to_object => true)
400
+ @callback.call(@object)
401
+
402
+ @first_context = first_context
403
+ @second_context = second_context
404
+ end
405
+
406
+ def test_should_call_each_method_within_the_context_of_the_object
407
+ assert_equal @object, @first_context
408
+ assert_equal @object, @second_context
409
+ end
410
+ end
411
+
412
+ class CallbackWithApplicationBoundObjectTest < Test::Unit::TestCase
413
+ def setup
414
+ @original_bind_to_object = StateMachine::Callback.bind_to_object
415
+ StateMachine::Callback.bind_to_object = true
416
+
417
+ context = nil
418
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| context = self})
419
+
420
+ @object = Object.new
421
+ @callback.call(@object)
422
+ @context = context
423
+ end
424
+
425
+ def test_should_call_method_within_the_context_of_the_object
426
+ assert_equal @object, @context
427
+ end
428
+
429
+ def teardown
430
+ StateMachine::Callback.bind_to_object = @original_bind_to_object
431
+ end
432
+ end
433
+
434
+ class CallbackWithApplicationTerminatorTest < Test::Unit::TestCase
435
+ def setup
436
+ @original_terminator = StateMachine::Callback.bind_to_object
437
+ StateMachine::Callback.terminator = lambda {|result| result == false}
438
+
439
+ @object = Object.new
440
+ end
441
+
442
+ def test_should_not_halt_if_terminator_does_not_match
443
+ callback = StateMachine::Callback.new(:do => lambda {true})
444
+ assert_nothing_thrown { callback.call(@object) }
445
+ end
446
+
447
+ def test_should_halt_if_terminator_matches
448
+ callback = StateMachine::Callback.new(:do => lambda {false})
449
+ assert_throws(:halt) { callback.call(@object) }
450
+ end
451
+
452
+ def teardown
453
+ StateMachine::Callback.bind_to_object = @original_bind_to_object
454
+ end
455
+ end