pluginaweek-state_machine 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
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,694 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ begin
4
+ # Load library
5
+ require 'rubygems'
6
+
7
+ gem 'dm-core', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
8
+ require 'dm-core'
9
+
10
+ # Establish database connection
11
+ DataMapper.setup(:default, 'sqlite3::memory:')
12
+ DataObjects::Sqlite3.logger = DataObjects::Logger.new("#{File.dirname(__FILE__)}/../../data_mapper.log", :info)
13
+
14
+ module DataMapperTest
15
+ class BaseTestCase < Test::Unit::TestCase
16
+ def default_test
17
+ end
18
+
19
+ protected
20
+ # Creates a new DataMapper resource (and the associated table)
21
+ def new_resource(auto_migrate = true, &block)
22
+ resource = Class.new do
23
+ include DataMapper::Resource
24
+
25
+ storage_names[:default] = 'foo'
26
+ def self.name; 'DataMapperTest::Foo'; end
27
+
28
+ property :id, Integer, :serial => true
29
+ property :state, String
30
+
31
+ auto_migrate! if auto_migrate
32
+ end
33
+ resource.class_eval(&block) if block_given?
34
+ resource
35
+ end
36
+
37
+ # Creates a new DataMapper observer
38
+ def new_observer(resource, &block)
39
+ observer = Class.new do
40
+ include DataMapper::Observer
41
+ end
42
+ observer.observe(resource)
43
+ observer.class_eval(&block) if block_given?
44
+ observer
45
+ end
46
+ end
47
+
48
+ class IntegrationTest < BaseTestCase
49
+ def test_should_match_if_class_inherits_from_active_record
50
+ assert StateMachine::Integrations::DataMapper.matches?(new_resource)
51
+ end
52
+
53
+ def test_should_not_match_if_class_does_not_inherit_from_active_record
54
+ assert !StateMachine::Integrations::DataMapper.matches?(Class.new)
55
+ end
56
+ end
57
+
58
+ class MachineByDefaultTest < BaseTestCase
59
+ def setup
60
+ @resource = new_resource
61
+ @machine = StateMachine::Machine.new(@resource)
62
+ end
63
+
64
+ def test_should_use_save_as_action
65
+ assert_equal :save, @machine.action
66
+ end
67
+
68
+ def test_should_not_use_transactions
69
+ assert_equal false, @machine.use_transactions
70
+ end
71
+ end
72
+
73
+ class MachineTest < BaseTestCase
74
+ def setup
75
+ @resource = new_resource
76
+ @machine = StateMachine::Machine.new(@resource)
77
+ @machine.state :parked, :first_gear
78
+ @machine.state :idling, :value => lambda {'idling'}
79
+ end
80
+
81
+ def test_should_create_singular_with_scope
82
+ assert @resource.respond_to?(:with_state)
83
+ end
84
+
85
+ def test_should_only_include_records_with_state_in_singular_with_scope
86
+ parked = @resource.create :state => 'parked'
87
+ idling = @resource.create :state => 'idling'
88
+
89
+ assert_equal [parked], @resource.with_state(:parked)
90
+ end
91
+
92
+ def test_should_create_plural_with_scope
93
+ assert @resource.respond_to?(:with_states)
94
+ end
95
+
96
+ def test_should_only_include_records_with_states_in_plural_with_scope
97
+ parked = @resource.create :state => 'parked'
98
+ idling = @resource.create :state => 'idling'
99
+
100
+ assert_equal [parked, idling], @resource.with_states(:parked, :idling)
101
+ end
102
+
103
+ def test_should_create_singular_without_scope
104
+ assert @resource.respond_to?(:without_state)
105
+ end
106
+
107
+ def test_should_only_include_records_without_state_in_singular_without_scope
108
+ parked = @resource.create :state => 'parked'
109
+ idling = @resource.create :state => 'idling'
110
+
111
+ assert_equal [parked], @resource.without_state(:idling)
112
+ end
113
+
114
+ def test_should_create_plural_without_scope
115
+ assert @resource.respond_to?(:without_states)
116
+ end
117
+
118
+ def test_should_only_include_records_without_states_in_plural_without_scope
119
+ parked = @resource.create :state => 'parked'
120
+ idling = @resource.create :state => 'idling'
121
+ first_gear = @resource.create :state => 'first_gear'
122
+
123
+ assert_equal [parked, idling], @resource.without_states(:first_gear)
124
+ end
125
+
126
+ def test_should_allow_chaining_scopes
127
+ parked = @resource.create :state => 'parked'
128
+ idling = @resource.create :state => 'idling'
129
+
130
+ assert_equal [idling], @resource.without_state(:parked).with_state(:idling)
131
+ end
132
+
133
+ def test_should_not_rollback_transaction_if_false
134
+ @machine.within_transaction(@resource.new) do
135
+ @resource.create
136
+ false
137
+ end
138
+
139
+ assert_equal 1, @resource.all.size
140
+ end
141
+
142
+ def test_should_not_rollback_transaction_if_true
143
+ @machine.within_transaction(@resource.new) do
144
+ @resource.create
145
+ true
146
+ end
147
+
148
+ assert_equal 1, @resource.all.size
149
+ end
150
+
151
+ def test_should_not_override_the_column_reader
152
+ record = @resource.new
153
+ record.attribute_set(:state, 'parked')
154
+ assert_equal 'parked', record.state
155
+ end
156
+
157
+ def test_should_not_override_the_column_writer
158
+ record = @resource.new
159
+ record.state = 'parked'
160
+ assert_equal 'parked', record.attribute_get(:state)
161
+ end
162
+ end
163
+
164
+ class MachineUnmigratedTest < BaseTestCase
165
+ def setup
166
+ @resource = new_resource(false)
167
+ end
168
+
169
+ def test_should_allow_machine_creation
170
+ assert_nothing_raised { StateMachine::Machine.new(@resource) }
171
+ end
172
+ end
173
+
174
+ class MachineWithInitialStateTest < BaseTestCase
175
+ def setup
176
+ @resource = new_resource
177
+ @machine = StateMachine::Machine.new(@resource, :initial => 'parked')
178
+ @record = @resource.new
179
+ end
180
+
181
+ def test_should_set_initial_state_on_created_object
182
+ assert_equal 'parked', @record.state
183
+ end
184
+ end
185
+
186
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
187
+ def setup
188
+ @resource = new_resource do
189
+ def initialize
190
+ # Skip attribute initialization
191
+ end
192
+ end
193
+
194
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => 'parked')
195
+ @record = @resource.new
196
+ end
197
+
198
+ def test_should_define_a_new_property_for_the_attribute
199
+ assert_not_nil @resource.properties[:status]
200
+ assert @record.respond_to?(:status)
201
+ assert @record.respond_to?(:status=)
202
+ end
203
+ end
204
+
205
+ class MachineWithComplexPluralizationTest < BaseTestCase
206
+ def setup
207
+ @resource = new_resource
208
+ @machine = StateMachine::Machine.new(@resource, :status)
209
+ end
210
+
211
+ def test_should_create_singular_with_scope
212
+ assert @resource.respond_to?(:with_status)
213
+ end
214
+
215
+ def test_should_create_plural_with_scope
216
+ assert @resource.respond_to?(:with_statuses)
217
+ end
218
+ end
219
+
220
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
221
+ def setup
222
+ @resource = new_resource do
223
+ attr_accessor :status
224
+ end
225
+
226
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => 'parked')
227
+ @record = @resource.new
228
+ end
229
+
230
+ def test_should_set_initial_state_on_created_object
231
+ assert_equal 'parked', @record.status
232
+ end
233
+ end
234
+
235
+ class MachineWithCallbacksTest < BaseTestCase
236
+ def setup
237
+ @resource = new_resource
238
+ @machine = StateMachine::Machine.new(@resource)
239
+ @machine.state :parked, :idling
240
+ @machine.event :ignite
241
+ @record = @resource.new(:state => 'parked')
242
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
243
+ end
244
+
245
+ def test_should_run_before_callbacks
246
+ called = false
247
+ @machine.before_transition(lambda {called = true})
248
+
249
+ @transition.perform
250
+ assert called
251
+ end
252
+
253
+ def test_should_pass_transition_to_before_callbacks_with_one_argument
254
+ transition = nil
255
+ @machine.before_transition(lambda {|arg| transition = arg})
256
+
257
+ @transition.perform
258
+ assert_equal @transition, transition
259
+ end
260
+
261
+ def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
262
+ callback_args = nil
263
+ @machine.before_transition(lambda {|*args| callback_args = args})
264
+
265
+ @transition.perform
266
+ assert_equal [@transition], callback_args
267
+ end
268
+
269
+ def test_should_run_before_callbacks_within_the_context_of_the_record
270
+ context = nil
271
+ @machine.before_transition(lambda {context = self})
272
+
273
+ @transition.perform
274
+ assert_equal @record, context
275
+ end
276
+
277
+ def test_should_run_after_callbacks
278
+ called = false
279
+ @machine.after_transition(lambda {called = true})
280
+
281
+ @transition.perform
282
+ assert called
283
+ end
284
+
285
+ def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
286
+ callback_args = nil
287
+ @machine.after_transition(lambda {|*args| callback_args = args})
288
+
289
+ @transition.perform
290
+ assert_equal [@transition], callback_args
291
+ end
292
+
293
+ def test_should_run_after_callbacks_with_the_context_of_the_record
294
+ context = nil
295
+ @machine.after_transition(lambda {context = self})
296
+
297
+ @transition.perform
298
+ assert_equal @record, context
299
+ end
300
+
301
+ def test_should_allow_symbolic_callbacks
302
+ callback_args = nil
303
+
304
+ klass = class << @record; self; end
305
+ klass.send(:define_method, :after_ignite) do |*args|
306
+ callback_args = args
307
+ end
308
+
309
+ @machine.before_transition(:after_ignite)
310
+
311
+ @transition.perform
312
+ assert_equal [@transition], callback_args
313
+ end
314
+
315
+ def test_should_allow_string_callbacks
316
+ class << @record
317
+ attr_reader :callback_result
318
+ end
319
+
320
+ @machine.before_transition('@callback_result = [1, 2, 3]')
321
+ @transition.perform
322
+
323
+ assert_equal [1, 2, 3], @record.callback_result
324
+ end
325
+ end
326
+
327
+ begin
328
+ gem 'dm-observer', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
329
+ require 'dm-observer'
330
+
331
+ class MachineWithObserversTest < BaseTestCase
332
+ def setup
333
+ @resource = new_resource
334
+ @machine = StateMachine::Machine.new(@resource)
335
+ @machine.state :parked, :idling
336
+ @machine.event :ignite
337
+ @record = @resource.new(:state => 'parked')
338
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
339
+ end
340
+
341
+ def test_should_provide_matcher_helpers
342
+ matchers = []
343
+
344
+ new_observer(@resource) do
345
+ matchers = [all, any, same]
346
+ end
347
+
348
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
349
+ end
350
+
351
+ def test_should_call_before_transition_callback_if_requirements_match
352
+ called = false
353
+
354
+ observer = new_observer(@resource) do
355
+ before_transition :from => :parked do
356
+ called = true
357
+ end
358
+ end
359
+
360
+ @transition.perform
361
+ assert called
362
+ end
363
+
364
+ def test_should_not_call_before_transition_callback_if_requirements_do_not_match
365
+ called = false
366
+
367
+ observer = new_observer(@resource) do
368
+ before_transition :from => :idling do
369
+ called = true
370
+ end
371
+ end
372
+
373
+ @transition.perform
374
+ assert !called
375
+ end
376
+
377
+ def test_should_pass_transition_to_before_callbacks
378
+ callback_args = nil
379
+
380
+ observer = new_observer(@resource) do
381
+ before_transition do |*args|
382
+ callback_args = args
383
+ end
384
+ end
385
+
386
+ @transition.perform
387
+ assert_equal [@transition], callback_args
388
+ end
389
+
390
+ def test_should_call_after_transition_callback_if_requirements_match
391
+ called = false
392
+
393
+ observer = new_observer(@resource) do
394
+ after_transition :from => :parked do
395
+ called = true
396
+ end
397
+ end
398
+
399
+ @transition.perform
400
+ assert called
401
+ end
402
+
403
+ def test_should_not_call_after_transition_callback_if_requirements_do_not_match
404
+ called = false
405
+
406
+ observer = new_observer(@resource) do
407
+ after_transition :from => :idling do
408
+ called = true
409
+ end
410
+ end
411
+
412
+ @transition.perform
413
+ assert !called
414
+ end
415
+
416
+ def test_should_pass_transition_to_after_callbacks
417
+ callback_args = nil
418
+
419
+ observer = new_observer(@resource) do
420
+ after_transition do |*args|
421
+ callback_args = args
422
+ end
423
+ end
424
+
425
+ @transition.perform
426
+ assert_equal [@transition], callback_args
427
+ end
428
+
429
+ def test_should_raise_exception_if_targeting_invalid_machine
430
+ assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) do
431
+ new_observer(@resource) do
432
+ before_transition :invalid, :from => :parked do
433
+ end
434
+ end
435
+ end
436
+ end
437
+
438
+ def test_should_allow_targeting_specific_machine
439
+ @second_machine = StateMachine::Machine.new(@resource, :status)
440
+ @resource.auto_migrate!
441
+
442
+ called_state = false
443
+ called_status = false
444
+
445
+ observer = new_observer(@resource) do
446
+ before_transition :state, :from => :parked do
447
+ called_state = true
448
+ end
449
+
450
+ before_transition :status, :from => :parked do
451
+ called_status = true
452
+ end
453
+ end
454
+
455
+ @transition.perform
456
+
457
+ assert called_state
458
+ assert !called_status
459
+ end
460
+
461
+ def test_should_allow_targeting_multiple_specific_machines
462
+ @second_machine = StateMachine::Machine.new(@resource, :status)
463
+ @second_machine.state :parked, :idling
464
+ @second_machine.event :ignite
465
+ @resource.auto_migrate!
466
+
467
+ called_attribute = nil
468
+
469
+ attributes = []
470
+ observer = new_observer(@resource) do
471
+ before_transition :state, :status, :from => :parked do |transition|
472
+ called_attribute = transition.attribute
473
+ end
474
+ end
475
+
476
+ @transition.perform
477
+ assert_equal :state, called_attribute
478
+
479
+ StateMachine::Transition.new(@record, @second_machine, :ignite, :parked, :idling).perform
480
+ assert_equal :status, called_attribute
481
+ end
482
+ end
483
+
484
+ class MachineWithMixedCallbacksTest < BaseTestCase
485
+ def setup
486
+ @resource = new_resource
487
+ @machine = StateMachine::Machine.new(@resource)
488
+ @machine.state :parked, :idling
489
+ @machine.event :ignite
490
+ @record = @resource.new(:state => 'parked')
491
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
492
+
493
+ @notifications = notifications = []
494
+
495
+ # Create callbacks
496
+ @machine.before_transition(lambda {notifications << :callback_before_transition})
497
+ @machine.after_transition(lambda {notifications << :callback_after_transition})
498
+
499
+ observer = new_observer(@resource) do
500
+ before_transition do
501
+ notifications << :observer_before_transition
502
+ end
503
+
504
+ after_transition do
505
+ notifications << :observer_after_transition
506
+ end
507
+ end
508
+
509
+ @transition.perform
510
+ end
511
+
512
+ def test_should_invoke_callbacks_in_specific_order
513
+ expected = [
514
+ :callback_before_transition,
515
+ :observer_before_transition,
516
+ :callback_after_transition,
517
+ :observer_after_transition
518
+ ]
519
+
520
+ assert_equal expected, @notifications
521
+ end
522
+ end
523
+ rescue LoadError
524
+ $stderr.puts "Skipping DataMapper Observer tests. `gem install dm-observer#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}` and try again."
525
+ end
526
+
527
+ begin
528
+ gem 'dm-validations', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
529
+ require 'dm-validations'
530
+
531
+ class MachineWithValidationsTest < BaseTestCase
532
+ def setup
533
+ @resource = new_resource
534
+ @machine = StateMachine::Machine.new(@resource)
535
+ @machine.state :parked
536
+
537
+ @record = @resource.new
538
+ end
539
+
540
+ def test_should_invalidate_using_errors
541
+ @record.state = 'parked'
542
+
543
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
544
+ assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
545
+ end
546
+
547
+ def test_should_auto_prefix_custom_attributes_on_invalidation
548
+ @machine.invalidate(@record, :event, :invalid)
549
+
550
+ assert_equal ['is invalid'], @record.errors.on(:state_event)
551
+ end
552
+
553
+ def test_should_clear_errors_on_reset
554
+ @record.state = 'parked'
555
+ @record.errors.add(:state, 'is invalid')
556
+
557
+ @machine.reset(@record)
558
+ assert_nil @record.errors.on(:id)
559
+ end
560
+
561
+ def test_should_be_valid_if_state_is_known
562
+ @record.state = 'parked'
563
+
564
+ assert @record.valid?
565
+ end
566
+
567
+ def test_should_not_be_valid_if_state_is_unknown
568
+ @record.state = 'invalid'
569
+
570
+ assert !@record.valid?
571
+ assert_equal ['is invalid'], @record.errors.on(:state)
572
+ end
573
+ end
574
+
575
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
576
+ def setup
577
+ @resource = new_resource do
578
+ attr_accessor :seatbelt
579
+ end
580
+
581
+ @machine = StateMachine::Machine.new(@resource)
582
+ @machine.state :first_gear, :second_gear do
583
+ validates_present :seatbelt
584
+ end
585
+ @machine.other_states :parked
586
+ end
587
+
588
+ def test_should_be_valid_if_validation_fails_outside_state_scope
589
+ record = @resource.new(:state => 'parked', :seatbelt => nil)
590
+ assert record.valid?
591
+ end
592
+
593
+ def test_should_be_invalid_if_validation_fails_within_state_scope
594
+ record = @resource.new(:state => 'first_gear', :seatbelt => nil)
595
+ assert !record.valid?
596
+ end
597
+
598
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
599
+ record = @resource.new(:state => 'second_gear', :seatbelt => true)
600
+ assert record.valid?
601
+ end
602
+ end
603
+
604
+ class MachineWithEventAttributesOnValidationTest < BaseTestCase
605
+ def setup
606
+ @resource = new_resource
607
+ @machine = StateMachine::Machine.new(@resource)
608
+ @machine.event :ignite do
609
+ transition :parked => :idling
610
+ end
611
+
612
+ @record = @resource.new
613
+ @record.state = 'parked'
614
+ @record.state_event = 'ignite'
615
+ end
616
+
617
+ def test_should_fail_if_event_is_invalid
618
+ @record.state_event = 'invalid'
619
+ assert !@record.valid?
620
+ assert_equal ['is invalid'], @record.errors.full_messages
621
+ end
622
+
623
+ def test_should_fail_if_event_has_no_transition
624
+ @record.state = 'idling'
625
+ assert !@record.valid?
626
+ assert_equal ['cannot transition when idling'], @record.errors.full_messages
627
+ end
628
+
629
+ def test_should_be_successful_if_event_has_transition
630
+ assert @record.valid?
631
+ end
632
+
633
+ def test_should_run_before_callbacks
634
+ ran_callback = false
635
+ @machine.before_transition { ran_callback = true }
636
+
637
+ @record.valid?
638
+ assert ran_callback
639
+ end
640
+
641
+ def test_should_persist_new_state
642
+ @record.valid?
643
+ assert_equal 'idling', @record.state
644
+ end
645
+
646
+ def test_should_not_run_after_callbacks
647
+ ran_callback = false
648
+ @machine.after_transition { ran_callback = true }
649
+
650
+ @record.valid?
651
+ assert !ran_callback
652
+ end
653
+ end
654
+
655
+ class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
656
+ def setup
657
+ @superclass = new_resource do
658
+ def persist
659
+ save
660
+ end
661
+ end
662
+ @resource = Class.new(@superclass)
663
+ @machine = StateMachine::Machine.new(@resource, :action => :persist)
664
+ @machine.event :ignite do
665
+ transition :parked => :idling
666
+ end
667
+
668
+ @record = @resource.new
669
+ @record.state = 'parked'
670
+ @record.state_event = 'ignite'
671
+ end
672
+
673
+ def test_should_not_transition_on_valid?
674
+ @record.valid?
675
+ assert_equal 'parked', @record.state
676
+ end
677
+
678
+ def test_should_not_transition_on_save
679
+ @record.save
680
+ assert_equal 'parked', @record.state
681
+ end
682
+
683
+ def test_should_transition_on_custom_action
684
+ @record.persist
685
+ assert_equal 'idling', @record.state
686
+ end
687
+ end
688
+ rescue LoadError
689
+ $stderr.puts "Skipping DataMapper Validation tests. `gem install dm-validations#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}` and try again."
690
+ end
691
+ end
692
+ rescue LoadError
693
+ $stderr.puts "Skipping DataMapper tests. `gem install dm-core#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}`, `gem install cucumber rspec hoe launchy do_sqlite3` and try again."
694
+ end