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,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