enum_state_machine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. metadata +83 -130
  6. data/.rvmrc +0 -1
  7. data/enum_state_machine.gemspec +0 -25
  8. data/lib/enum_state_machine.rb +0 -9
  9. data/lib/enum_state_machine/assertions.rb +0 -36
  10. data/lib/enum_state_machine/branch.rb +0 -225
  11. data/lib/enum_state_machine/callback.rb +0 -232
  12. data/lib/enum_state_machine/core.rb +0 -12
  13. data/lib/enum_state_machine/core_ext.rb +0 -2
  14. data/lib/enum_state_machine/core_ext/class/state_machine.rb +0 -5
  15. data/lib/enum_state_machine/error.rb +0 -13
  16. data/lib/enum_state_machine/eval_helpers.rb +0 -87
  17. data/lib/enum_state_machine/event.rb +0 -257
  18. data/lib/enum_state_machine/event_collection.rb +0 -141
  19. data/lib/enum_state_machine/extensions.rb +0 -149
  20. data/lib/enum_state_machine/graph.rb +0 -92
  21. data/lib/enum_state_machine/helper_module.rb +0 -17
  22. data/lib/enum_state_machine/initializers.rb +0 -4
  23. data/lib/enum_state_machine/initializers/rails.rb +0 -22
  24. data/lib/enum_state_machine/integrations.rb +0 -97
  25. data/lib/enum_state_machine/integrations/active_model.rb +0 -585
  26. data/lib/enum_state_machine/integrations/active_model/locale.rb +0 -11
  27. data/lib/enum_state_machine/integrations/active_model/observer.rb +0 -33
  28. data/lib/enum_state_machine/integrations/active_model/observer_update.rb +0 -42
  29. data/lib/enum_state_machine/integrations/active_model/versions.rb +0 -31
  30. data/lib/enum_state_machine/integrations/active_record.rb +0 -548
  31. data/lib/enum_state_machine/integrations/active_record/locale.rb +0 -20
  32. data/lib/enum_state_machine/integrations/active_record/versions.rb +0 -123
  33. data/lib/enum_state_machine/integrations/base.rb +0 -100
  34. data/lib/enum_state_machine/machine.rb +0 -2292
  35. data/lib/enum_state_machine/machine_collection.rb +0 -86
  36. data/lib/enum_state_machine/macro_methods.rb +0 -518
  37. data/lib/enum_state_machine/matcher.rb +0 -123
  38. data/lib/enum_state_machine/matcher_helpers.rb +0 -54
  39. data/lib/enum_state_machine/node_collection.rb +0 -222
  40. data/lib/enum_state_machine/path.rb +0 -120
  41. data/lib/enum_state_machine/path_collection.rb +0 -90
  42. data/lib/enum_state_machine/state.rb +0 -297
  43. data/lib/enum_state_machine/state_collection.rb +0 -112
  44. data/lib/enum_state_machine/state_context.rb +0 -138
  45. data/lib/enum_state_machine/state_enum.rb +0 -23
  46. data/lib/enum_state_machine/transition.rb +0 -470
  47. data/lib/enum_state_machine/transition_collection.rb +0 -245
  48. data/lib/enum_state_machine/version.rb +0 -3
  49. data/lib/enum_state_machine/yard.rb +0 -8
  50. data/lib/enum_state_machine/yard/handlers.rb +0 -12
  51. data/lib/enum_state_machine/yard/handlers/base.rb +0 -32
  52. data/lib/enum_state_machine/yard/handlers/event.rb +0 -25
  53. data/lib/enum_state_machine/yard/handlers/machine.rb +0 -344
  54. data/lib/enum_state_machine/yard/handlers/state.rb +0 -25
  55. data/lib/enum_state_machine/yard/handlers/transition.rb +0 -47
  56. data/lib/enum_state_machine/yard/templates.rb +0 -3
  57. data/lib/enum_state_machine/yard/templates/default/class/html/setup.rb +0 -30
  58. data/lib/enum_state_machine/yard/templates/default/class/html/state_machines.erb +0 -12
  59. data/lib/tasks/enum_state_machine.rake +0 -1
  60. data/lib/tasks/enum_state_machine.rb +0 -24
  61. data/lib/yard-enum_state_machine.rb +0 -2
  62. data/test/functional/state_machine_test.rb +0 -1066
  63. data/test/unit/integrations/active_model_test.rb +0 -1245
  64. data/test/unit/integrations/active_record_test.rb +0 -2551
  65. data/test/unit/integrations/base_test.rb +0 -104
  66. data/test/unit/integrations_test.rb +0 -71
  67. data/test/unit/invalid_event_test.rb +0 -20
  68. data/test/unit/invalid_parallel_transition_test.rb +0 -18
  69. data/test/unit/invalid_transition_test.rb +0 -115
  70. data/test/unit/machine_collection_test.rb +0 -603
  71. data/test/unit/machine_test.rb +0 -3395
  72. data/test/unit/state_machine_test.rb +0 -31
@@ -1,2551 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
-
3
- require 'active_record'
4
-
5
- FIXTURES_ROOT = File.dirname(__FILE__) + '/../../fixtures/'
6
-
7
- # Load TestCase helpers
8
- require 'active_support/test_case'
9
- require 'active_record/fixtures'
10
-
11
- begin
12
- require 'active_record/test_case'
13
- rescue LoadError
14
- class ActiveRecord::TestCase < ActiveSupport::TestCase
15
- self.fixture_path = FIXTURES_ROOT
16
- self.use_instantiated_fixtures = false
17
- self.use_transactional_fixtures = true
18
- end
19
- end
20
-
21
- require 'active_record/version'
22
- if ActiveRecord::VERSION::MAJOR >= 4
23
- require 'rails/observers/activerecord/active_record'
24
- require 'active_record/mass_assignment_security'
25
- end
26
-
27
- # Establish database connection
28
- ActiveRecord::Base.establish_connection('adapter' => RUBY_PLATFORM == 'java' ? 'jdbcsqlite3' : 'sqlite3', 'database' => ':memory:')
29
- ActiveRecord::Base.logger = Logger.new("#{File.dirname(__FILE__)}/../../active_record.log")
30
-
31
- module ActiveRecordTest
32
- class BaseTestCase < ActiveRecord::TestCase
33
- def default_test
34
- end
35
-
36
- protected
37
- # Creates a new ActiveRecord model (and the associated table)
38
- def new_model(create_table = :foo, &block)
39
- name = create_table || :foo
40
- table_name = "#{name}_#{rand(1000000)}"
41
-
42
- model = Class.new(ActiveRecord::Base) do
43
- self.table_name = table_name.to_s
44
- connection.create_table(table_name, :force => true) {|t| t.string(:state)} if create_table
45
-
46
- (class << self; self; end).class_eval do
47
- define_method(:name) { "ActiveRecordTest::#{name.to_s.capitalize}" }
48
- end
49
- end
50
- model.class_eval(&block) if block_given?
51
- model.reset_column_information if create_table
52
- model
53
- end
54
-
55
- # Creates a new ActiveRecord observer
56
- def new_observer(model, &block)
57
- observer = Class.new(ActiveRecord::Observer) do
58
- attr_accessor :notifications
59
-
60
- def initialize
61
- super
62
- @notifications = []
63
- end
64
- end
65
-
66
- (class << observer; self; end).class_eval do
67
- define_method(:name) do
68
- "#{model.name}Observer"
69
- end
70
- end
71
-
72
- observer.observe(model)
73
- observer.class_eval(&block) if block_given?
74
- observer
75
- end
76
- end
77
-
78
- class IntegrationTest < BaseTestCase
79
- def test_should_have_an_integration_name
80
- assert_equal :active_record, EnumStateMachine::Integrations::ActiveRecord.integration_name
81
- end
82
-
83
- def test_should_be_available
84
- assert EnumStateMachine::Integrations::ActiveRecord.available?
85
- end
86
-
87
- def test_should_match_if_class_inherits_from_active_record
88
- assert EnumStateMachine::Integrations::ActiveRecord.matches?(new_model)
89
- end
90
-
91
- def test_should_not_match_if_class_does_not_inherit_from_active_record
92
- assert !EnumStateMachine::Integrations::ActiveRecord.matches?(Class.new)
93
- end
94
-
95
- def test_should_have_defaults
96
- assert_equal({:action => :save}, EnumStateMachine::Integrations::ActiveRecord.defaults)
97
- end
98
-
99
- def test_should_have_a_locale_path
100
- assert_not_nil EnumStateMachine::Integrations::ActiveRecord.locale_path
101
- end
102
- end
103
-
104
- class MachineWithoutDatabaseTest < BaseTestCase
105
- def setup
106
- @model = new_model(false) do
107
- # Simulate the database not being available entirely
108
- def self.connection
109
- raise ActiveRecord::ConnectionNotEstablished
110
- end
111
-
112
- def self.connected?
113
- false
114
- end
115
- end
116
- end
117
-
118
- def test_should_allow_machine_creation
119
- assert_nothing_raised { EnumStateMachine::Machine.new(@model) }
120
- end
121
- end
122
-
123
- class MachineUnmigratedTest < BaseTestCase
124
- def setup
125
- @model = new_model(false)
126
-
127
- # Drop the table so that it definitely doesn't exist
128
- @model.connection.drop_table(@model.table_name) if @model.table_exists?
129
- end
130
-
131
- def test_should_allow_machine_creation
132
- assert_nothing_raised { EnumStateMachine::Machine.new(@model) }
133
- end
134
- end
135
-
136
- class MachineByDefaultTest < BaseTestCase
137
- def setup
138
- @model = new_model
139
- @machine = EnumStateMachine::Machine.new(@model)
140
- end
141
-
142
- def test_should_use_save_as_action
143
- assert_equal :save, @machine.action
144
- end
145
-
146
- def test_should_use_transactions
147
- assert_equal true, @machine.use_transactions
148
- end
149
-
150
- def test_should_create_notifier_before_callback
151
- assert_equal 1, @machine.callbacks[:before].size
152
- end
153
-
154
- def test_should_create_notifier_after_callback
155
- assert_equal 1, @machine.callbacks[:after].size
156
- end
157
- end
158
-
159
- class MachineWithStatesTest < BaseTestCase
160
- def setup
161
- @model = new_model
162
- @machine = EnumStateMachine::Machine.new(@model)
163
- @machine.state :first_gear
164
- end
165
-
166
- def test_should_humanize_name
167
- assert_equal 'first gear', @machine.state(:first_gear).human_name
168
- end
169
- end
170
-
171
- class MachineWithStaticInitialStateTest < BaseTestCase
172
- def setup
173
- @model = new_model(:vehicle) do
174
- attr_accessor :value
175
- end
176
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
177
- end
178
-
179
- def test_should_set_initial_state_on_created_object
180
- record = @model.new
181
- assert_equal 'parked', record.state
182
- end
183
-
184
- def test_should_set_initial_state_with_nil_attributes
185
- record = @model.new(nil)
186
- assert_equal 'parked', record.state
187
- end
188
-
189
- def test_should_still_set_attributes
190
- record = @model.new(:value => 1)
191
- assert_equal 1, record.value
192
- end
193
-
194
- def test_should_still_allow_initialize_blocks
195
- block_args = nil
196
- record = @model.new do |*args|
197
- block_args = args
198
- end
199
-
200
- assert_equal [record], block_args
201
- end
202
-
203
- def test_should_set_attributes_prior_to_initialize_block
204
- state = nil
205
- @model.new do |record|
206
- state = record.state
207
- end
208
-
209
- assert_equal 'parked', state
210
- end
211
-
212
- def test_should_set_attributes_prior_to_after_initialize_hook
213
- state = nil
214
- @model.class_eval {define_method(:after_initialize) {}} if ActiveRecord::VERSION::MAJOR <= 2
215
- @model.after_initialize do |record|
216
- state = record.state
217
- end
218
- @model.new
219
- assert_equal 'parked', state
220
- end
221
-
222
- def test_should_set_initial_state_before_setting_attributes
223
- @model.class_eval do
224
- attr_accessor :state_during_setter
225
-
226
- remove_method :value=
227
- define_method(:value=) do |value|
228
- self.state_during_setter = state
229
- end
230
- end
231
-
232
- record = @model.new(:value => 1)
233
- assert_equal 'parked', record.state_during_setter
234
- end
235
-
236
- def test_should_not_set_initial_state_after_already_initialized
237
- record = @model.new(:value => 1)
238
- assert_equal 'parked', record.state
239
-
240
- record.state = 'idling'
241
- record.attributes = {}
242
- assert_equal 'idling', record.state
243
- end
244
-
245
- def test_should_persist_initial_state
246
- record = @model.new
247
- record.save
248
- record.reload
249
- assert_equal 'parked', record.state
250
- end
251
-
252
- unless ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
253
- def test_should_persist_initial_state_on_dup
254
- record = @model.create.dup
255
- record.save
256
- record.reload
257
- assert_equal 'parked', record.state
258
- end
259
- end
260
-
261
- def test_should_use_stored_values_when_loading_from_database
262
- @machine.state :idling
263
-
264
- record = @model.find(@model.create(:state => 'idling').id)
265
- assert_equal 'idling', record.state
266
- end
267
-
268
- def test_should_use_stored_values_when_loading_from_database_with_nil_state
269
- @machine.state nil
270
-
271
- record = @model.find(@model.create(:state => nil).id)
272
- assert_nil record.state
273
- end
274
-
275
- def test_should_use_stored_values_when_loading_for_many_association
276
- @machine.state :idling
277
-
278
- @model.connection.add_column @model.table_name, :owner_id, :integer
279
- @model.reset_column_information
280
- ActiveRecordTest.const_set('Vehicle', @model)
281
-
282
- owner_model = new_model(:owner) do
283
- has_many :vehicles, :class_name => 'ActiveRecordTest::Vehicle'
284
- end
285
- ActiveRecordTest.const_set('Owner', owner_model)
286
-
287
- owner = owner_model.create
288
- record = @model.create(:state => 'idling', :owner_id => owner.id)
289
- assert_equal 'idling', owner.vehicles[0].state
290
- end
291
-
292
- def test_should_use_stored_values_when_loading_for_one_association
293
- @machine.state :idling
294
-
295
- @model.connection.add_column @model.table_name, :owner_id, :integer
296
- @model.reset_column_information
297
- ActiveRecordTest.const_set('Vehicle', @model)
298
-
299
- owner_model = new_model(:owner) do
300
- has_one :vehicle, :class_name => 'ActiveRecordTest::Vehicle'
301
- end
302
- ActiveRecordTest.const_set('Owner', owner_model)
303
-
304
- owner = owner_model.create
305
- record = @model.create(:state => 'idling', :owner_id => owner.id)
306
- assert_equal 'idling', owner.vehicle.state
307
- end
308
-
309
- def test_should_use_stored_values_when_loading_for_belongs_to_association
310
- @machine.state :idling
311
-
312
- ActiveRecordTest.const_set('Vehicle', @model)
313
-
314
- driver_model = new_model(:driver) do
315
- connection.add_column table_name, :vehicle_id, :integer
316
-
317
- belongs_to :vehicle, :class_name => 'ActiveRecordTest::Vehicle'
318
- end
319
-
320
- ActiveRecordTest.const_set('Driver', driver_model)
321
-
322
- record = @model.create(:state => 'idling')
323
- driver = driver_model.create(:vehicle_id => record.id)
324
- assert_equal 'idling', driver.vehicle.state
325
- end
326
-
327
- def teardown
328
- ActiveRecordTest.class_eval do
329
- remove_const('Vehicle') if defined?(ActiveRecordTest::Vehicle)
330
- remove_const('Owner') if defined?(ActiveRecordTest::Owner)
331
- remove_const('Driver') if defined?(ActiveRecordTest::Driver)
332
- end
333
- ActiveSupport::Dependencies.clear if defined?(ActiveSupport::Dependencies)
334
- super
335
- end
336
- end
337
-
338
- class MachineWithDynamicInitialStateTest < BaseTestCase
339
- def setup
340
- @model = new_model do
341
- attr_accessor :value
342
- end
343
- @machine = EnumStateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
344
- @machine.state :parked
345
- end
346
-
347
- def test_should_set_initial_state_on_created_object
348
- record = @model.new
349
- assert_equal 'parked', record.state
350
- end
351
-
352
- def test_should_still_set_attributes
353
- record = @model.new(:value => 1)
354
- assert_equal 1, record.value
355
- end
356
-
357
- def test_should_still_allow_initialize_blocks
358
- block_args = nil
359
- record = @model.new do |*args|
360
- block_args = args
361
- end
362
-
363
- assert_equal [record], block_args
364
- end
365
-
366
- def test_should_set_attributes_prior_to_initialize_block
367
- state = nil
368
- @model.new do |record|
369
- state = record.state
370
- end
371
-
372
- assert_equal 'parked', state
373
- end
374
-
375
- def test_should_set_attributes_prior_to_after_initialize_hook
376
- state = nil
377
- @model.class_eval {define_method(:after_initialize) {}} if ActiveRecord::VERSION::MAJOR <= 2
378
- @model.after_initialize do |record|
379
- state = record.state
380
- end
381
- @model.new
382
- assert_equal 'parked', state
383
- end
384
-
385
- def test_should_set_initial_state_after_setting_attributes
386
- @model.class_eval do
387
- attr_accessor :state_during_setter
388
-
389
- remove_method :value=
390
- define_method(:value=) do |value|
391
- self.state_during_setter = state || 'nil'
392
- end
393
- end
394
-
395
- record = @model.new(:value => 1)
396
- assert_equal 'nil', record.state_during_setter
397
- end
398
-
399
- def test_should_not_set_initial_state_after_already_initialized
400
- record = @model.new(:value => 1)
401
- assert_equal 'parked', record.state
402
-
403
- record.state = 'idling'
404
- record.attributes = {}
405
- assert_equal 'idling', record.state
406
- end
407
-
408
- def test_should_persist_initial_state
409
- record = @model.new
410
- record.save
411
- record.reload
412
- assert_equal 'parked', record.state
413
- end
414
-
415
- unless ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
416
- def test_should_persist_initial_state_on_dup
417
- record = @model.create.dup
418
- record.save
419
- record.reload
420
- assert_equal 'parked', record.state
421
- end
422
- end
423
-
424
- def test_should_use_stored_values_when_loading_from_database
425
- @machine.state :idling
426
-
427
- record = @model.find(@model.create(:state => 'idling').id)
428
- assert_equal 'idling', record.state
429
- end
430
-
431
- def test_should_use_stored_values_when_loading_from_database_with_nil_state
432
- @machine.state nil
433
-
434
- record = @model.find(@model.create(:state => nil).id)
435
- assert_nil record.state
436
- end
437
- end
438
-
439
- class MachineWithEventsTest < BaseTestCase
440
- def setup
441
- @model = new_model
442
- @machine = EnumStateMachine::Machine.new(@model)
443
- @machine.event :shift_up
444
- end
445
-
446
- def test_should_humanize_name
447
- assert_equal 'shift up', @machine.event(:shift_up).human_name
448
- end
449
- end
450
-
451
- class MachineWithSameColumnDefaultTest < BaseTestCase
452
- def setup
453
- @original_stderr, $stderr = $stderr, StringIO.new
454
-
455
- @model = new_model do
456
- connection.add_column table_name, :status, :string, :default => 'parked'
457
- end
458
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
459
- @record = @model.new
460
- end
461
-
462
- def test_should_use_machine_default
463
- assert_equal 'parked', @record.status
464
- end
465
-
466
- def test_should_not_generate_a_warning
467
- assert_no_match(/have defined a different default/, $stderr.string)
468
- end
469
-
470
- def teardown
471
- $stderr = @original_stderr
472
- super
473
- end
474
- end
475
-
476
- class MachineWithDifferentColumnDefaultTest < BaseTestCase
477
- def setup
478
- @original_stderr, $stderr = $stderr, StringIO.new
479
-
480
- @model = new_model do
481
- connection.add_column table_name, :status, :string, :default => 'idling'
482
- end
483
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
484
- @record = @model.new
485
- end
486
-
487
- def test_should_use_machine_default
488
- assert_equal 'parked', @record.status
489
- end
490
-
491
- def test_should_generate_a_warning
492
- assert_match(/Both ActiveRecordTest::Foo and its :status machine have defined a different default for "status". Use only one or the other for defining defaults to avoid unexpected behaviors\./, $stderr.string)
493
- end
494
-
495
- def teardown
496
- $stderr = @original_stderr
497
- super
498
- end
499
- end
500
-
501
- class MachineWithDifferentIntegerColumnDefaultTest < BaseTestCase
502
- def setup
503
- @original_stderr, $stderr = $stderr, StringIO.new
504
-
505
- @model = new_model do
506
- connection.add_column table_name, :status, :integer, :default => 0
507
- end
508
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
509
- @machine.state :parked, :value => 1
510
- @record = @model.new
511
- end
512
-
513
- def test_should_use_machine_default
514
- assert_equal 1, @record.status
515
- end
516
-
517
- def test_should_generate_a_warning
518
- assert_match(/Both ActiveRecordTest::Foo and its :status machine have defined a different default for "status". Use only one or the other for defining defaults to avoid unexpected behaviors\./, $stderr.string)
519
- end
520
-
521
- def teardown
522
- $stderr = @original_stderr
523
- super
524
- end
525
- end
526
-
527
- class MachineWithConflictingPredicateTest < BaseTestCase
528
- def setup
529
- @model = new_model do
530
- def state?(*args)
531
- true
532
- end
533
- end
534
-
535
- @machine = EnumStateMachine::Machine.new(@model)
536
- @record = @model.new
537
- end
538
-
539
- def test_should_not_define_attribute_predicate
540
- assert @record.state?
541
- end
542
- end
543
-
544
- class MachineWithConflictingStateNameTest < BaseTestCase
545
- def setup
546
- require 'stringio'
547
- @original_stderr, $stderr = $stderr, StringIO.new
548
-
549
- @model = new_model
550
- end
551
-
552
- def test_should_output_warning_with_same_machine_name
553
- @machine = EnumStateMachine::Machine.new(@model)
554
- @machine.state :state
555
-
556
- assert_match(/^Instance method "state\?" is already defined in ActiveRecordTest::Foo, use generic helper instead.*\n$/, $stderr.string)
557
- end
558
-
559
- def test_should_output_warning_with_same_machine_attribute
560
- @machine = EnumStateMachine::Machine.new(@model, :public_state, :attribute => :state)
561
- @machine.state :state
562
-
563
- assert_match(/^Instance method "state\?" is already defined in ActiveRecordTest::Foo, use generic helper instead.*\n$/, $stderr.string)
564
- end
565
-
566
- def teardown
567
- $stderr = @original_stderr
568
- super
569
- end
570
- end
571
-
572
- class MachineWithColumnStateAttributeTest < BaseTestCase
573
- def setup
574
- @model = new_model
575
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
576
- @machine.other_states(:idling)
577
-
578
- @record = @model.new
579
- end
580
-
581
- def test_should_not_override_the_column_reader
582
- @record[:state] = 'parked'
583
- assert_equal 'parked', @record.state
584
- end
585
-
586
- def test_should_not_override_the_column_writer
587
- @record.state = 'parked'
588
- assert_equal 'parked', @record[:state]
589
- end
590
-
591
- def test_should_have_an_attribute_predicate
592
- assert @record.respond_to?(:state?)
593
- end
594
-
595
- def test_should_test_for_existence_on_predicate_without_parameters
596
- assert @record.state?
597
-
598
- @record.state = nil
599
- assert !@record.state?
600
- end
601
-
602
- def test_should_return_false_for_predicate_if_does_not_match_current_value
603
- assert !@record.state?(:idling)
604
- end
605
-
606
- def test_should_return_true_for_predicate_if_matches_current_value
607
- assert @record.state?(:parked)
608
- end
609
-
610
- def test_should_raise_exception_for_predicate_if_invalid_state_specified
611
- assert_raise(IndexError) { @record.state?(:invalid) }
612
- end
613
- end
614
-
615
- class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
616
- def setup
617
- @model = new_model do
618
- def initialize
619
- # Skip attribute initialization
620
- @initialized_state_machines = true
621
- super
622
- end
623
- end
624
-
625
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
626
- @machine.other_states(:idling)
627
- @record = @model.new
628
- end
629
-
630
- def test_should_not_define_a_column_for_the_attribute
631
- assert_nil @model.columns_hash['status']
632
- end
633
-
634
- def test_should_define_a_reader_attribute_for_the_attribute
635
- assert @record.respond_to?(:status)
636
- end
637
-
638
- def test_should_define_a_writer_attribute_for_the_attribute
639
- assert @record.respond_to?(:status=)
640
- end
641
-
642
- def test_should_define_an_attribute_predicate
643
- assert @record.respond_to?(:status?)
644
- end
645
- end
646
-
647
- class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
648
- def setup
649
- @model = new_model do
650
- def status=(value)
651
- self['status'] = value
652
- end
653
-
654
- def status
655
- self['status']
656
- end
657
- end
658
-
659
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
660
- @machine.other_states(:idling)
661
- @record = @model.new
662
- end
663
-
664
- def test_should_return_false_for_predicate_if_does_not_match_current_value
665
- assert !@record.status?(:idling)
666
- end
667
-
668
- def test_should_return_true_for_predicate_if_matches_current_value
669
- assert @record.status?(:parked)
670
- end
671
-
672
- def test_should_raise_exception_for_predicate_if_invalid_state_specified
673
- assert_raise(IndexError) { @record.status?(:invalid) }
674
- end
675
-
676
- def test_should_set_initial_state_on_created_object
677
- assert_equal 'parked', @record.status
678
- end
679
- end
680
-
681
- class MachineWithAliasedAttributeTest < BaseTestCase
682
- def setup
683
- @model = new_model do
684
- alias_attribute :vehicle_status, :state
685
- end
686
-
687
- @machine = EnumStateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
688
- @machine.state :parked
689
-
690
- @record = @model.new
691
- end
692
-
693
- def test_should_check_custom_attribute_for_predicate
694
- @record.vehicle_status = nil
695
- assert !@record.status?(:parked)
696
-
697
- @record.vehicle_status = 'parked'
698
- assert @record.status?(:parked)
699
- end
700
- end
701
-
702
- class MachineWithCustomAttributeTest < BaseTestCase
703
- def setup
704
- require 'stringio'
705
- @original_stderr, $stderr = $stderr, StringIO.new
706
-
707
- @model = new_model
708
- @machine = EnumStateMachine::Machine.new(@model, :public_state, :attribute => :state)
709
- @record = @model.new
710
- end
711
-
712
- def test_should_not_delegate_attribute_predicate_with_different_attribute
713
- assert_raise(ArgumentError) { @record.public_state? }
714
- end
715
-
716
- def teardown
717
- $stderr = @original_stderr
718
- super
719
- end
720
- end
721
-
722
- class MachineWithInitializedStateTest < BaseTestCase
723
- def setup
724
- @model = new_model
725
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
726
- @machine.state :idling
727
- end
728
-
729
- def test_should_allow_nil_initial_state_when_static
730
- @machine.state nil
731
-
732
- record = @model.new(:state => nil)
733
- assert_nil record.state
734
- end
735
-
736
- def test_should_allow_nil_initial_state_when_dynamic
737
- @machine.state nil
738
-
739
- @machine.initial_state = lambda {:parked}
740
- record = @model.new(:state => nil)
741
- assert_nil record.state
742
- end
743
-
744
- def test_should_allow_different_initial_state_when_static
745
- record = @model.new(:state => 'idling')
746
- assert_equal 'idling', record.state
747
- end
748
-
749
- def test_should_allow_different_initial_state_when_dynamic
750
- @machine.initial_state = lambda {:parked}
751
- record = @model.new(:state => 'idling')
752
- assert_equal 'idling', record.state
753
- end
754
-
755
- def test_should_use_default_state_if_protected
756
- @model.class_eval do
757
- attr_protected :state
758
- end
759
-
760
- record = @model.new(:state => 'idling')
761
- assert_equal 'parked', record.state
762
- end
763
- end
764
-
765
- class MachineMultipleTest < BaseTestCase
766
- def setup
767
- @model = new_model do
768
- connection.add_column table_name, :status, :string
769
- end
770
- @state_machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
771
- @status_machine = EnumStateMachine::Machine.new(@model, :status, :initial => :idling)
772
- end
773
-
774
- def test_should_should_initialize_each_state
775
- record = @model.new
776
- assert_equal 'parked', record.state
777
- assert_equal 'idling', record.status
778
- end
779
- end
780
-
781
- class MachineWithLoopbackTest < BaseTestCase
782
- def setup
783
- @model = new_model do
784
- connection.add_column table_name, :updated_at, :datetime
785
- end
786
-
787
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
788
- @machine.event :park
789
-
790
- @record = @model.create(:updated_at => Time.now - 1)
791
- @transition = EnumStateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
792
-
793
- @timestamp = @record.updated_at
794
- @transition.perform
795
- end
796
-
797
- if ActiveRecord.const_defined?(:Dirty) || ActiveRecord::AttributeMethods.const_defined?(:Dirty)
798
- def test_should_not_update_record
799
- assert_equal @timestamp, @record.updated_at
800
- end
801
- else
802
- def test_should_update_record
803
- assert_not_equal @timestamp, @record.updated_at
804
- end
805
- end
806
- end
807
-
808
- if ActiveRecord.const_defined?(:Dirty) || ActiveRecord::AttributeMethods.const_defined?(:Dirty)
809
- class MachineWithDirtyAttributesTest < BaseTestCase
810
- def setup
811
- @model = new_model
812
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
813
- @machine.event :ignite
814
- @machine.state :idling
815
-
816
- @record = @model.create
817
-
818
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
819
- @transition.perform(false)
820
- end
821
-
822
- def test_should_include_state_in_changed_attributes
823
- assert_equal %w(state), @record.changed
824
- end
825
-
826
- def test_should_track_attribute_change
827
- assert_equal %w(parked idling), @record.changes['state']
828
- end
829
-
830
- def test_should_not_reset_changes_on_multiple_transitions
831
- transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
832
- transition.perform(false)
833
-
834
- assert_equal %w(parked idling), @record.changes['state']
835
- end
836
-
837
- def test_should_not_have_changes_when_loaded_from_database
838
- record = @model.find(@record.id)
839
- assert !record.changed?
840
- end
841
- end
842
-
843
- class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
844
- def setup
845
- @model = new_model
846
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
847
- @machine.event :park
848
-
849
- @record = @model.create
850
-
851
- @transition = EnumStateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
852
- @transition.perform(false)
853
- end
854
-
855
- def test_should_not_include_state_in_changed_attributes
856
- assert_equal [], @record.changed
857
- end
858
-
859
- def test_should_not_track_attribute_changes
860
- assert_equal nil, @record.changes['state']
861
- end
862
- end
863
-
864
- class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
865
- def setup
866
- @model = new_model do
867
- connection.add_column table_name, :status, :string
868
- end
869
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
870
- @machine.event :ignite
871
- @machine.state :idling
872
-
873
- @record = @model.create
874
-
875
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
876
- @transition.perform(false)
877
- end
878
-
879
- def test_should_include_state_in_changed_attributes
880
- assert_equal %w(status), @record.changed
881
- end
882
-
883
- def test_should_track_attribute_change
884
- assert_equal %w(parked idling), @record.changes['status']
885
- end
886
-
887
- def test_should_not_reset_changes_on_multiple_transitions
888
- transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
889
- transition.perform(false)
890
-
891
- assert_equal %w(parked idling), @record.changes['status']
892
- end
893
- end
894
-
895
- class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
896
- def setup
897
- @model = new_model do
898
- connection.add_column table_name, :status, :string
899
- end
900
- @machine = EnumStateMachine::Machine.new(@model, :status, :initial => :parked)
901
- @machine.event :park
902
-
903
- @record = @model.create
904
-
905
- @transition = EnumStateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
906
- @transition.perform(false)
907
- end
908
-
909
- def test_should_not_include_state_in_changed_attributes
910
- assert_equal [], @record.changed
911
- end
912
-
913
- def test_should_not_track_attribute_changes
914
- assert_equal nil, @record.changes['status']
915
- end
916
- end
917
-
918
- class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
919
- def setup
920
- @model = new_model
921
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
922
- @machine.event :ignite
923
-
924
- @record = @model.create
925
- @record.state_event = 'ignite'
926
- end
927
-
928
- def test_should_not_include_state_in_changed_attributes
929
- assert_equal [], @record.changed
930
- end
931
-
932
- def test_should_not_track_attribute_change
933
- assert_equal nil, @record.changes['state']
934
- end
935
- end
936
- else
937
- $stderr.puts 'Skipping ActiveRecord Dirty tests.'
938
- end
939
-
940
- class MachineWithoutTransactionsTest < BaseTestCase
941
- def setup
942
- @model = new_model
943
- @machine = EnumStateMachine::Machine.new(@model, :use_transactions => false)
944
- end
945
-
946
- def test_should_not_rollback_transaction_if_false
947
- @machine.within_transaction(@model.new) do
948
- @model.create
949
- false
950
- end
951
-
952
- assert_equal 1, @model.count
953
- end
954
-
955
- def test_should_not_rollback_transaction_if_true
956
- @machine.within_transaction(@model.new) do
957
- @model.create
958
- true
959
- end
960
-
961
- assert_equal 1, @model.count
962
- end
963
- end
964
-
965
- class MachineWithTransactionsTest < BaseTestCase
966
- def setup
967
- @model = new_model
968
- @machine = EnumStateMachine::Machine.new(@model, :use_transactions => true)
969
- end
970
-
971
- def test_should_rollback_transaction_if_false
972
- @machine.within_transaction(@model.new) do
973
- @model.create
974
- false
975
- end
976
-
977
- assert_equal 0, @model.count
978
- end
979
-
980
- def test_should_not_rollback_transaction_if_true
981
- @machine.within_transaction(@model.new) do
982
- @model.create
983
- true
984
- end
985
-
986
- assert_equal 1, @model.count
987
- end
988
- end
989
-
990
- class MachineWithCallbacksTest < BaseTestCase
991
- def setup
992
- @model = new_model
993
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
994
- @machine.other_states :idling
995
- @machine.event :ignite
996
-
997
- @record = @model.new(:state => 'parked')
998
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
999
- end
1000
-
1001
- def test_should_run_before_callbacks
1002
- called = false
1003
- @machine.before_transition {called = true}
1004
-
1005
- @transition.perform
1006
- assert called
1007
- end
1008
-
1009
- def test_should_pass_record_to_before_callbacks_with_one_argument
1010
- record = nil
1011
- @machine.before_transition {|arg| record = arg}
1012
-
1013
- @transition.perform
1014
- assert_equal @record, record
1015
- end
1016
-
1017
- def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
1018
- callback_args = nil
1019
- @machine.before_transition {|*args| callback_args = args}
1020
-
1021
- @transition.perform
1022
- assert_equal [@record, @transition], callback_args
1023
- end
1024
-
1025
- def test_should_run_before_callbacks_outside_the_context_of_the_record
1026
- context = nil
1027
- @machine.before_transition {context = self}
1028
-
1029
- @transition.perform
1030
- assert_equal self, context
1031
- end
1032
-
1033
- def test_should_run_after_callbacks
1034
- called = false
1035
- @machine.after_transition {called = true}
1036
-
1037
- @transition.perform
1038
- assert called
1039
- end
1040
-
1041
- def test_should_pass_record_to_after_callbacks_with_one_argument
1042
- record = nil
1043
- @machine.after_transition {|arg| record = arg}
1044
-
1045
- @transition.perform
1046
- assert_equal @record, record
1047
- end
1048
-
1049
- def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
1050
- callback_args = nil
1051
- @machine.after_transition {|*args| callback_args = args}
1052
-
1053
- @transition.perform
1054
- assert_equal [@record, @transition], callback_args
1055
- end
1056
-
1057
- def test_should_run_after_callbacks_outside_the_context_of_the_record
1058
- context = nil
1059
- @machine.after_transition {context = self}
1060
-
1061
- @transition.perform
1062
- assert_equal self, context
1063
- end
1064
-
1065
- def test_should_run_after_callbacks_if_model_callback_added_prior_to_state_machine_definition
1066
- model = new_model do
1067
- after_save { nil }
1068
- end
1069
- machine = EnumStateMachine::Machine.new(model, :initial => :parked)
1070
- machine.other_states :idling
1071
- machine.event :ignite
1072
- after_called = false
1073
- machine.after_transition {after_called = true}
1074
-
1075
- record = model.new(:state => 'parked')
1076
- transition = EnumStateMachine::Transition.new(record, machine, :ignite, :parked, :idling)
1077
- transition.perform
1078
- assert_equal true, after_called
1079
- end
1080
-
1081
- def test_should_run_around_callbacks
1082
- before_called = false
1083
- after_called = false
1084
- ensure_called = 0
1085
- @machine.around_transition do |block|
1086
- before_called = true
1087
- begin
1088
- block.call
1089
- ensure
1090
- ensure_called += 1
1091
- end
1092
- after_called = true
1093
- end
1094
-
1095
- @transition.perform
1096
- assert before_called
1097
- assert after_called
1098
- assert_equal ensure_called, 1
1099
- end
1100
-
1101
- def test_should_include_transition_states_in_known_states
1102
- @machine.before_transition :to => :first_gear, :do => lambda {}
1103
-
1104
- assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
1105
- end
1106
-
1107
- def test_should_allow_symbolic_callbacks
1108
- callback_args = nil
1109
-
1110
- klass = class << @record; self; end
1111
- klass.send(:define_method, :after_ignite) do |*args|
1112
- callback_args = args
1113
- end
1114
-
1115
- @machine.before_transition(:after_ignite)
1116
-
1117
- @transition.perform
1118
- assert_equal [@transition], callback_args
1119
- end
1120
-
1121
- def test_should_allow_string_callbacks
1122
- class << @record
1123
- attr_reader :callback_result
1124
- end
1125
-
1126
- @machine.before_transition('@callback_result = [1, 2, 3]')
1127
- @transition.perform
1128
-
1129
- assert_equal [1, 2, 3], @record.callback_result
1130
- end
1131
-
1132
- def test_should_run_in_expected_order
1133
- expected = [
1134
- :before_transition, :before_validation, :after_validation,
1135
- :before_save, :before_create, :after_create, :after_save,
1136
- :after_transition
1137
- ]
1138
-
1139
- callbacks = []
1140
- @model.before_validation { callbacks << :before_validation }
1141
- @model.after_validation { callbacks << :after_validation }
1142
- @model.before_save { callbacks << :before_save }
1143
- @model.before_create { callbacks << :before_create }
1144
- @model.after_create { callbacks << :after_create }
1145
- @model.after_save { callbacks << :after_save }
1146
- if @model.respond_to?(:after_commit)
1147
- @model.after_commit { callbacks << :after_commit }
1148
- expected << :after_commit
1149
- end
1150
-
1151
- @machine.before_transition { callbacks << :before_transition }
1152
- @machine.after_transition { callbacks << :after_transition }
1153
-
1154
- @transition.perform
1155
-
1156
- assert_equal expected, callbacks
1157
- end
1158
- end
1159
-
1160
- class MachineWithFailedBeforeCallbacksTest < BaseTestCase
1161
- def setup
1162
- @callbacks = []
1163
-
1164
- @model = new_model
1165
- @machine = EnumStateMachine::Machine.new(@model)
1166
- @machine.state :parked, :idling
1167
- @machine.event :ignite
1168
- @machine.before_transition {@callbacks << :before_1; false}
1169
- @machine.before_transition {@callbacks << :before_2}
1170
- @machine.after_transition {@callbacks << :after}
1171
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
1172
-
1173
- @record = @model.new(:state => 'parked')
1174
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1175
- @result = @transition.perform
1176
- end
1177
-
1178
- def test_should_not_be_successful
1179
- assert !@result
1180
- end
1181
-
1182
- def test_should_not_change_current_state
1183
- assert_equal 'parked', @record.state
1184
- end
1185
-
1186
- def test_should_not_run_action
1187
- assert @record.new_record?
1188
- end
1189
-
1190
- def test_should_not_run_further_callbacks
1191
- assert_equal [:before_1], @callbacks
1192
- end
1193
- end
1194
-
1195
- class MachineNestedActionTest < BaseTestCase
1196
- def setup
1197
- @callbacks = []
1198
-
1199
- @model = new_model
1200
- @machine = EnumStateMachine::Machine.new(@model)
1201
- @machine.event :ignite do
1202
- transition :parked => :idling
1203
- end
1204
-
1205
- @record = @model.new(:state => 'parked')
1206
- end
1207
-
1208
- def test_should_allow_transition_prior_to_creation_if_skipping_action
1209
- record = @record
1210
- @model.before_create { record.ignite(false) }
1211
- result = @record.save
1212
-
1213
- assert_equal true, result
1214
- assert_equal "idling", @record.state
1215
- @record.reload
1216
- assert_equal "idling", @record.state
1217
- end
1218
-
1219
- def test_should_allow_transition_after_creation
1220
- record = @record
1221
- @model.after_create { record.ignite }
1222
- result = @record.save
1223
-
1224
- assert_equal true, result
1225
- assert_equal "idling", @record.state
1226
- @record.reload
1227
- assert_equal "idling", @record.state
1228
- end
1229
- end
1230
-
1231
- class MachineWithFailedActionTest < BaseTestCase
1232
- def setup
1233
- @model = new_model do
1234
- validates_inclusion_of :state, :in => %w(first_gear)
1235
- end
1236
-
1237
- @machine = EnumStateMachine::Machine.new(@model)
1238
- @machine.state :parked, :idling
1239
- @machine.event :ignite
1240
-
1241
- @callbacks = []
1242
- @machine.before_transition {@callbacks << :before}
1243
- @machine.after_transition {@callbacks << :after}
1244
- @machine.after_failure {@callbacks << :after_failure}
1245
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
1246
-
1247
- @record = @model.new(:state => 'parked')
1248
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1249
- @result = @transition.perform
1250
- end
1251
-
1252
- def test_should_not_be_successful
1253
- assert !@result
1254
- end
1255
-
1256
- def test_should_not_change_current_state
1257
- assert_equal 'parked', @record.state
1258
- end
1259
-
1260
- def test_should_not_save_record
1261
- assert @record.new_record?
1262
- end
1263
-
1264
- def test_should_run_before_callbacks_and_after_callbacks_with_failures
1265
- assert_equal [:before, :around_before, :after_failure], @callbacks
1266
- end
1267
- end
1268
-
1269
- class MachineWithFailedAfterCallbacksTest < BaseTestCase
1270
- def setup
1271
- @callbacks = []
1272
-
1273
- @model = new_model
1274
- @machine = EnumStateMachine::Machine.new(@model)
1275
- @machine.state :parked, :idling
1276
- @machine.event :ignite
1277
- @machine.after_transition {@callbacks << :after_1; false}
1278
- @machine.after_transition {@callbacks << :after_2}
1279
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
1280
-
1281
- @record = @model.new(:state => 'parked')
1282
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1283
- @result = @transition.perform
1284
- end
1285
-
1286
- def test_should_be_successful
1287
- assert @result
1288
- end
1289
-
1290
- def test_should_change_current_state
1291
- assert_equal 'idling', @record.state
1292
- end
1293
-
1294
- def test_should_save_record
1295
- assert !@record.new_record?
1296
- end
1297
-
1298
- def test_should_not_run_further_after_callbacks
1299
- assert_equal [:around_before, :around_after, :after_1], @callbacks
1300
- end
1301
- end
1302
-
1303
- class MachineWithValidationsTest < BaseTestCase
1304
- def setup
1305
- @model = new_model
1306
- @machine = EnumStateMachine::Machine.new(@model)
1307
- @machine.state :parked
1308
-
1309
- @record = @model.new
1310
- end
1311
-
1312
- def test_should_invalidate_using_errors
1313
- I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
1314
- @record.state = 'parked'
1315
-
1316
- @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
1317
- assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
1318
- end
1319
-
1320
- def test_should_auto_prefix_custom_attributes_on_invalidation
1321
- @machine.invalidate(@record, :event, :invalid)
1322
-
1323
- assert_equal ['State event is invalid'], @record.errors.full_messages
1324
- end
1325
-
1326
- def test_should_clear_errors_on_reset
1327
- @record.state = 'parked'
1328
- @record.errors.add(:state, 'is invalid')
1329
-
1330
- @machine.reset(@record)
1331
- assert_equal [], @record.errors.full_messages
1332
- end
1333
-
1334
- def test_should_be_valid_if_state_is_known
1335
- @record.state = 'parked'
1336
-
1337
- assert @record.valid?
1338
- end
1339
-
1340
- def test_should_not_be_valid_if_state_is_unknown
1341
- @record.state = 'invalid'
1342
-
1343
- assert !@record.valid?
1344
- assert_equal ['State is invalid'], @record.errors.full_messages
1345
- end
1346
- end
1347
-
1348
- class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
1349
- def setup
1350
- @model = new_model
1351
- @machine = EnumStateMachine::Machine.new(@model, :status, :attribute => :state)
1352
- @machine.state :parked
1353
-
1354
- @record = @model.new
1355
- end
1356
-
1357
- def test_should_add_validation_errors_to_custom_attribute
1358
- @record.state = 'invalid'
1359
-
1360
- assert !@record.valid?
1361
- assert_equal ['State is invalid'], @record.errors.full_messages
1362
-
1363
- @record.state = 'parked'
1364
- assert @record.valid?
1365
- end
1366
- end
1367
-
1368
- class MachineErrorsTest < BaseTestCase
1369
- def setup
1370
- @model = new_model
1371
- @machine = EnumStateMachine::Machine.new(@model)
1372
- @record = @model.new
1373
- end
1374
-
1375
- def test_should_be_able_to_describe_current_errors
1376
- @record.errors.add(:id, 'cannot be blank')
1377
- @record.errors.add(:state, 'is invalid')
1378
- assert_equal ['Id cannot be blank', 'State is invalid'], @machine.errors_for(@record).split(', ').sort
1379
- end
1380
-
1381
- def test_should_describe_as_halted_with_no_errors
1382
- assert_equal 'Transition halted', @machine.errors_for(@record)
1383
- end
1384
- end
1385
-
1386
- class MachineWithStateDrivenValidationsTest < BaseTestCase
1387
- def setup
1388
- @model = new_model do
1389
- attr_accessor :seatbelt
1390
- end
1391
-
1392
- @machine = EnumStateMachine::Machine.new(@model)
1393
- @machine.state :first_gear, :second_gear do
1394
- validates_presence_of :seatbelt
1395
- end
1396
- @machine.other_states :parked
1397
- end
1398
-
1399
- def test_should_be_valid_if_validation_fails_outside_state_scope
1400
- record = @model.new(:state => 'parked', :seatbelt => nil)
1401
- assert record.valid?
1402
- end
1403
-
1404
- def test_should_be_invalid_if_validation_fails_within_state_scope
1405
- record = @model.new(:state => 'first_gear', :seatbelt => nil)
1406
- assert !record.valid?
1407
- end
1408
-
1409
- def test_should_be_valid_if_validation_succeeds_within_state_scope
1410
- record = @model.new(:state => 'second_gear', :seatbelt => true)
1411
- assert record.valid?
1412
- end
1413
- end
1414
-
1415
- class MachineWithEventAttributesOnValidationTest < BaseTestCase
1416
- def setup
1417
- @model = new_model
1418
- @machine = EnumStateMachine::Machine.new(@model)
1419
- @machine.event :ignite do
1420
- transition :parked => :idling
1421
- end
1422
-
1423
- @record = @model.new
1424
- @record.state = 'parked'
1425
- @record.state_event = 'ignite'
1426
- end
1427
-
1428
- def test_should_fail_if_event_is_invalid
1429
- @record.state_event = 'invalid'
1430
- assert !@record.valid?
1431
- assert_equal ['State event is invalid'], @record.errors.full_messages
1432
- end
1433
-
1434
- def test_should_fail_if_event_has_no_transition
1435
- @record.state = 'idling'
1436
- assert !@record.valid?
1437
- assert_equal ['State event cannot transition when idling'], @record.errors.full_messages
1438
- end
1439
-
1440
- def test_should_be_successful_if_event_has_transition
1441
- assert @record.valid?
1442
- end
1443
-
1444
- def test_should_run_before_callbacks
1445
- ran_callback = false
1446
- @machine.before_transition { ran_callback = true }
1447
-
1448
- @record.valid?
1449
- assert ran_callback
1450
- end
1451
-
1452
- def test_should_run_around_callbacks_before_yield
1453
- ran_callback = false
1454
- @machine.around_transition {|block| ran_callback = true; block.call }
1455
-
1456
- begin
1457
- @record.valid?
1458
- rescue ArgumentError
1459
- raise if EnumStateMachine::Transition.pause_supported?
1460
- end
1461
- assert ran_callback
1462
- end
1463
-
1464
- def test_should_persist_new_state
1465
- @record.valid?
1466
- assert_equal 'idling', @record.state
1467
- end
1468
-
1469
- def test_should_not_run_after_callbacks
1470
- ran_callback = false
1471
- @machine.after_transition { ran_callback = true }
1472
-
1473
- @record.valid?
1474
- assert !ran_callback
1475
- end
1476
-
1477
- def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
1478
- @model.class_eval do
1479
- attr_accessor :seatbelt
1480
- validates_presence_of :seatbelt
1481
- end
1482
-
1483
- ran_callback = false
1484
- @machine.after_transition { ran_callback = true }
1485
-
1486
- @record.valid?
1487
- assert !ran_callback
1488
- end
1489
-
1490
- def test_should_run_after_callbacks_if_validation_fails
1491
- @model.class_eval do
1492
- attr_accessor :seatbelt
1493
- validates_presence_of :seatbelt
1494
- end
1495
-
1496
- ran_callback = false
1497
- @machine.after_failure { ran_callback = true }
1498
-
1499
- @record.valid?
1500
- assert ran_callback
1501
- end
1502
-
1503
- def test_should_not_run_around_callbacks_after_yield
1504
- ran_callback = false
1505
- @machine.around_transition {|block| block.call; ran_callback = true }
1506
-
1507
- begin
1508
- @record.valid?
1509
- rescue ArgumentError
1510
- raise if EnumStateMachine::Transition.pause_supported?
1511
- end
1512
- assert !ran_callback
1513
- end
1514
-
1515
- def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
1516
- @model.class_eval do
1517
- attr_accessor :seatbelt
1518
- validates_presence_of :seatbelt
1519
- end
1520
-
1521
- ran_callback = false
1522
- @machine.around_transition {|block| block.call; ran_callback = true }
1523
-
1524
- @record.valid?
1525
- assert !ran_callback
1526
- end
1527
-
1528
- def test_should_not_run_before_transitions_within_transaction
1529
- @machine.before_transition { @model.create; raise ActiveRecord::Rollback }
1530
-
1531
- begin
1532
- @record.valid?
1533
- rescue Exception
1534
- end
1535
-
1536
- assert_equal 1, @model.count
1537
- end
1538
- end
1539
-
1540
- class MachineWithEventAttributesOnSaveTest < BaseTestCase
1541
- def setup
1542
- @model = new_model
1543
- @machine = EnumStateMachine::Machine.new(@model)
1544
- @machine.event :ignite do
1545
- transition :parked => :idling
1546
- end
1547
-
1548
- @record = @model.new
1549
- @record.state = 'parked'
1550
- @record.state_event = 'ignite'
1551
- end
1552
-
1553
- def test_should_fail_if_event_is_invalid
1554
- @record.state_event = 'invalid'
1555
- assert_equal false, @record.save
1556
- end
1557
-
1558
- def test_should_fail_if_event_has_no_transition
1559
- @record.state = 'idling'
1560
- assert_equal false, @record.save
1561
- end
1562
-
1563
- def test_should_run_before_callbacks
1564
- ran_callback = false
1565
- @machine.before_transition { ran_callback = true }
1566
-
1567
- @record.save
1568
- assert ran_callback
1569
- end
1570
-
1571
- def test_should_run_before_callbacks_once
1572
- before_count = 0
1573
- @machine.before_transition { before_count += 1 }
1574
-
1575
- @record.save
1576
- assert_equal 1, before_count
1577
- end
1578
-
1579
- def test_should_run_around_callbacks_before_yield
1580
- ran_callback = false
1581
- @machine.around_transition {|block| ran_callback = true; block.call }
1582
-
1583
- @record.save
1584
- assert ran_callback
1585
- end
1586
-
1587
- def test_should_run_around_callbacks_before_yield_once
1588
- around_before_count = 0
1589
- @machine.around_transition {|block| around_before_count += 1; block.call }
1590
-
1591
- @record.save
1592
- assert_equal 1, around_before_count
1593
- end
1594
-
1595
- def test_should_persist_new_state
1596
- @record.save
1597
- assert_equal 'idling', @record.state
1598
- end
1599
-
1600
- def test_should_run_after_callbacks
1601
- ran_callback = false
1602
- @machine.after_transition { ran_callback = true }
1603
-
1604
- @record.save
1605
- assert ran_callback
1606
- end
1607
-
1608
- def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1609
- @model.before_create {|record| false}
1610
-
1611
- ran_callback = false
1612
- @machine.after_transition { ran_callback = true }
1613
-
1614
- begin; @record.save; rescue; end
1615
- assert !ran_callback
1616
- end
1617
-
1618
- def test_should_run_failure_callbacks__if_fails
1619
- @model.before_create {|record| false}
1620
-
1621
- ran_callback = false
1622
- @machine.after_failure { ran_callback = true }
1623
-
1624
- begin; @record.save; rescue; end
1625
- assert ran_callback
1626
- end
1627
-
1628
- def test_should_not_run_around_callbacks_if_fails
1629
- @model.before_create {|record| false}
1630
-
1631
- ran_callback = false
1632
- @machine.around_transition {|block| block.call; ran_callback = true }
1633
-
1634
- begin; @record.save; rescue; end
1635
- assert !ran_callback
1636
- end
1637
-
1638
- def test_should_run_around_callbacks_after_yield
1639
- ran_callback = false
1640
- @machine.around_transition {|block| block.call; ran_callback = true }
1641
-
1642
- @record.save
1643
- assert ran_callback
1644
- end
1645
-
1646
- def test_should_run_before_transitions_within_transaction
1647
- @machine.before_transition { @model.create; raise ActiveRecord::Rollback }
1648
-
1649
- begin
1650
- @record.save
1651
- rescue Exception
1652
- end
1653
-
1654
- assert_equal 0, @model.count
1655
- end
1656
-
1657
- def test_should_run_after_transitions_within_transaction
1658
- @machine.after_transition { @model.create; raise ActiveRecord::Rollback }
1659
-
1660
- begin
1661
- @record.save
1662
- rescue Exception
1663
- end
1664
-
1665
- assert_equal 0, @model.count
1666
- end
1667
-
1668
- def test_should_run_around_transition_within_transaction
1669
- @machine.around_transition { @model.create; raise ActiveRecord::Rollback }
1670
-
1671
- begin
1672
- @record.save
1673
- rescue Exception
1674
- end
1675
-
1676
- assert_equal 0, @model.count
1677
- end
1678
-
1679
- def test_should_allow_additional_transitions_to_new_state_in_after_transitions
1680
- @machine.event :park do
1681
- transition :idling => :parked
1682
- end
1683
-
1684
- @machine.after_transition(:on => :ignite) { @record.park }
1685
-
1686
- @record.save
1687
- assert_equal 'parked', @record.state
1688
-
1689
- @record.reload
1690
- assert_equal 'parked', @record.state
1691
- end
1692
-
1693
- def test_should_allow_additional_transitions_to_previous_state_in_after_transitions
1694
- @machine.event :shift_up do
1695
- transition :idling => :first_gear
1696
- end
1697
-
1698
- @machine.after_transition(:on => :ignite) { @record.shift_up }
1699
-
1700
- @record.save
1701
- assert_equal 'first_gear', @record.state
1702
-
1703
- @record.reload
1704
- assert_equal 'first_gear', @record.state
1705
- end
1706
-
1707
- def test_should_return_nil_on_manual_rollback
1708
- @machine.before_transition { raise ActiveRecord::Rollback }
1709
-
1710
- assert_equal nil, @record.save
1711
- end
1712
- end
1713
-
1714
- if ActiveRecord::VERSION::MAJOR >= 3 || ActiveRecord::VERSION::MINOR >= 3
1715
- class MachineWithEventAttributesOnAutosaveTest < BaseTestCase
1716
- def setup
1717
- @vehicle_model = new_model(:vehicle) do
1718
- connection.add_column table_name, :owner_id, :integer
1719
- end
1720
- ActiveRecordTest.const_set('Vehicle', @vehicle_model)
1721
-
1722
- @owner_model = new_model(:owner)
1723
- ActiveRecordTest.const_set('Owner', @owner_model)
1724
-
1725
- machine = EnumStateMachine::Machine.new(@vehicle_model)
1726
- machine.event :ignite do
1727
- transition :parked => :idling
1728
- end
1729
-
1730
- @owner = @owner_model.create
1731
- @vehicle = @vehicle_model.create(:state => 'parked', :owner_id => @owner.id)
1732
- end
1733
-
1734
- def test_should_persist_has_one_autosave
1735
- @owner_model.has_one :vehicle, :class_name => 'ActiveRecordTest::Vehicle', :autosave => true
1736
- @owner.vehicle.state_event = 'ignite'
1737
- @owner.save
1738
-
1739
- @vehicle.reload
1740
- assert_equal 'idling', @vehicle.state
1741
- end
1742
-
1743
- def test_should_persist_has_many_autosave
1744
- @owner_model.has_many :vehicles, :class_name => 'ActiveRecordTest::Vehicle', :autosave => true
1745
- @owner.vehicles[0].state_event = 'ignite'
1746
- @owner.save
1747
-
1748
- @vehicle.reload
1749
- assert_equal 'idling', @vehicle.state
1750
- end
1751
-
1752
- def teardown
1753
- ActiveRecordTest.class_eval do
1754
- remove_const('Vehicle')
1755
- remove_const('Owner')
1756
- end
1757
- ActiveSupport::Dependencies.clear if defined?(ActiveSupport::Dependencies)
1758
- super
1759
- end
1760
- end
1761
- end
1762
-
1763
- class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
1764
- def setup
1765
- @model = new_model
1766
- @machine = EnumStateMachine::Machine.new(@model)
1767
- @machine.event :ignite do
1768
- transition :parked => :idling
1769
- end
1770
-
1771
- @record = @model.new
1772
- @record.state = 'parked'
1773
- @record.state_event = 'ignite'
1774
- end
1775
-
1776
- def test_should_fail_if_event_is_invalid
1777
- @record.state_event = 'invalid'
1778
- assert_raise(ActiveRecord::RecordInvalid) { @record.save! }
1779
- end
1780
-
1781
- def test_should_fail_if_event_has_no_transition
1782
- @record.state = 'idling'
1783
- assert_raise(ActiveRecord::RecordInvalid) { @record.save! }
1784
- end
1785
-
1786
- def test_should_be_successful_if_event_has_transition
1787
- assert_equal true, @record.save!
1788
- end
1789
-
1790
- def test_should_run_before_callbacks
1791
- ran_callback = false
1792
- @machine.before_transition { ran_callback = true }
1793
-
1794
- @record.save!
1795
- assert ran_callback
1796
- end
1797
-
1798
- def test_should_run_before_callbacks_once
1799
- before_count = 0
1800
- @machine.before_transition { before_count += 1 }
1801
-
1802
- @record.save!
1803
- assert_equal 1, before_count
1804
- end
1805
-
1806
- def test_should_run_around_callbacks_before_yield
1807
- ran_callback = false
1808
- @machine.around_transition {|block| ran_callback = true; block.call }
1809
-
1810
- @record.save!
1811
- assert ran_callback
1812
- end
1813
-
1814
- def test_should_run_around_callbacks_before_yield_once
1815
- around_before_count = 0
1816
- @machine.around_transition {|block| around_before_count += 1; block.call }
1817
-
1818
- @record.save!
1819
- assert_equal 1, around_before_count
1820
- end
1821
-
1822
- def test_should_persist_new_state
1823
- @record.save!
1824
- assert_equal 'idling', @record.state
1825
- end
1826
-
1827
- def test_should_run_after_callbacks
1828
- ran_callback = false
1829
- @machine.after_transition { ran_callback = true }
1830
-
1831
- @record.save!
1832
- assert ran_callback
1833
- end
1834
-
1835
- def test_should_run_around_callbacks_after_yield
1836
- ran_callback = false
1837
- @machine.around_transition {|block| block.call; ran_callback = true }
1838
-
1839
- @record.save!
1840
- assert ran_callback
1841
- end
1842
- end
1843
-
1844
- class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1845
- def setup
1846
- @superclass = new_model do
1847
- def persist
1848
- create_or_update
1849
- end
1850
- end
1851
- @model = Class.new(@superclass)
1852
- @machine = EnumStateMachine::Machine.new(@model, :action => :persist)
1853
- @machine.event :ignite do
1854
- transition :parked => :idling
1855
- end
1856
-
1857
- @record = @model.new
1858
- @record.state = 'parked'
1859
- @record.state_event = 'ignite'
1860
- end
1861
-
1862
- def test_should_not_transition_on_valid?
1863
- @record.valid?
1864
- assert_equal 'parked', @record.state
1865
- end
1866
-
1867
- def test_should_not_transition_on_save
1868
- @record.save
1869
- assert_equal 'parked', @record.state
1870
- end
1871
-
1872
- def test_should_not_transition_on_save!
1873
- @record.save!
1874
- assert_equal 'parked', @record.state
1875
- end
1876
-
1877
- def test_should_transition_on_custom_action
1878
- @record.persist
1879
- assert_equal 'idling', @record.state
1880
- end
1881
- end
1882
-
1883
- class MachineWithObserversTest < BaseTestCase
1884
- def setup
1885
- @model = new_model
1886
- @machine = EnumStateMachine::Machine.new(@model)
1887
- @machine.state :parked, :idling
1888
- @machine.event :ignite
1889
- @record = @model.new(:state => 'parked')
1890
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1891
- end
1892
-
1893
- def test_should_call_all_transition_callback_permutations
1894
- callbacks = [
1895
- :before_ignite_from_parked_to_idling,
1896
- :before_ignite_from_parked,
1897
- :before_ignite_to_idling,
1898
- :before_ignite,
1899
- :before_transition_state_from_parked_to_idling,
1900
- :before_transition_state_from_parked,
1901
- :before_transition_state_to_idling,
1902
- :before_transition_state,
1903
- :before_transition
1904
- ]
1905
-
1906
- observer = new_observer(@model) do
1907
- callbacks.each do |callback|
1908
- define_method(callback) do |*args|
1909
- notifications << callback
1910
- end
1911
- end
1912
- end
1913
-
1914
- instance = observer.instance
1915
-
1916
- @transition.perform
1917
- assert_equal callbacks, instance.notifications
1918
- end
1919
-
1920
- def test_should_call_no_transition_callbacks_when_observers_disabled
1921
- return unless ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR >= 1
1922
-
1923
- callbacks = [
1924
- :before_ignite,
1925
- :before_transition
1926
- ]
1927
-
1928
- observer = new_observer(@model) do
1929
- callbacks.each do |callback|
1930
- define_method(callback) do |*args|
1931
- notifications << callback
1932
- end
1933
- end
1934
- end
1935
-
1936
- instance = observer.instance
1937
-
1938
- @model.observers.disable(observer) do
1939
- @transition.perform
1940
- end
1941
-
1942
- assert_equal [], instance.notifications
1943
- end
1944
-
1945
- def test_should_pass_record_and_transition_to_before_callbacks
1946
- observer = new_observer(@model) do
1947
- def before_transition(*args)
1948
- notifications << args
1949
- end
1950
- end
1951
- instance = observer.instance
1952
-
1953
- @transition.perform
1954
- assert_equal [[@record, @transition]], instance.notifications
1955
- end
1956
-
1957
- def test_should_pass_record_and_transition_to_after_callbacks
1958
- observer = new_observer(@model) do
1959
- def after_transition(*args)
1960
- notifications << args
1961
- end
1962
- end
1963
- instance = observer.instance
1964
-
1965
- @transition.perform
1966
- assert_equal [[@record, @transition]], instance.notifications
1967
- end
1968
-
1969
- def test_should_call_methods_outside_the_context_of_the_record
1970
- observer = new_observer(@model) do
1971
- def before_ignite(*args)
1972
- notifications << self
1973
- end
1974
- end
1975
- instance = observer.instance
1976
-
1977
- @transition.perform
1978
- assert_equal [instance], instance.notifications
1979
- end
1980
-
1981
- def test_should_continue_to_handle_non_state_machine_callbacks
1982
- observer = new_observer(@model) do
1983
- def before_save(object)
1984
- notifications << [:before_save, object]
1985
- end
1986
-
1987
- def before_ignite(*args)
1988
- notifications << :before_ignite
1989
- end
1990
- end
1991
-
1992
- instance = observer.instance
1993
-
1994
- @transition.perform
1995
- assert_equal [:before_ignite, [:before_save, @record]], instance.notifications
1996
- end
1997
-
1998
- def test_should_support_nil_from_states
1999
- callbacks = [
2000
- :before_ignite_from_nil_to_idling,
2001
- :before_ignite_from_nil,
2002
- :before_transition_state_from_nil_to_idling,
2003
- :before_transition_state_from_nil
2004
- ]
2005
-
2006
- observer = new_observer(@model) do
2007
- callbacks.each do |callback|
2008
- define_method(callback) do |*args|
2009
- notifications << callback
2010
- end
2011
- end
2012
- end
2013
-
2014
- instance = observer.instance
2015
-
2016
- transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, nil, :idling)
2017
- transition.perform
2018
- assert_equal callbacks, instance.notifications
2019
- end
2020
-
2021
- def test_should_support_nil_to_states
2022
- callbacks = [
2023
- :before_ignite_from_parked_to_nil,
2024
- :before_ignite_to_nil,
2025
- :before_transition_state_from_parked_to_nil,
2026
- :before_transition_state_to_nil
2027
- ]
2028
-
2029
- observer = new_observer(@model) do
2030
- callbacks.each do |callback|
2031
- define_method(callback) do |*args|
2032
- notifications << callback
2033
- end
2034
- end
2035
- end
2036
-
2037
- instance = observer.instance
2038
-
2039
- transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, nil)
2040
- transition.perform
2041
- assert_equal callbacks, instance.notifications
2042
- end
2043
- end
2044
-
2045
- class MachineWithNamespacedObserversTest < BaseTestCase
2046
- def setup
2047
- @model = new_model
2048
- @machine = EnumStateMachine::Machine.new(@model, :state, :namespace => 'alarm')
2049
- @machine.state :active, :off
2050
- @machine.event :enable
2051
- @record = @model.new(:state => 'off')
2052
- @transition = EnumStateMachine::Transition.new(@record, @machine, :enable, :off, :active)
2053
- end
2054
-
2055
- def test_should_call_namespaced_before_event_method
2056
- observer = new_observer(@model) do
2057
- def before_enable_alarm(*args)
2058
- notifications << args
2059
- end
2060
- end
2061
- instance = observer.instance
2062
-
2063
- @transition.perform
2064
- assert_equal [[@record, @transition]], instance.notifications
2065
- end
2066
-
2067
- def test_should_call_namespaced_after_event_method
2068
- observer = new_observer(@model) do
2069
- def after_enable_alarm(*args)
2070
- notifications << args
2071
- end
2072
- end
2073
- instance = observer.instance
2074
-
2075
- @transition.perform
2076
- assert_equal [[@record, @transition]], instance.notifications
2077
- end
2078
- end
2079
-
2080
- class MachineWithFailureCallbacksTest < BaseTestCase
2081
- def setup
2082
- @model = new_model
2083
- @machine = EnumStateMachine::Machine.new(@model)
2084
- @machine.state :parked, :idling
2085
- @machine.event :ignite
2086
- @record = @model.new(:state => 'parked')
2087
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
2088
-
2089
- @notifications = []
2090
-
2091
- # Create callbacks
2092
- @machine.before_transition {false}
2093
- @machine.after_failure {@notifications << :callback_after_failure}
2094
-
2095
- # Create observer callbacks
2096
- observer = new_observer(@model) do
2097
- def after_failure_to_ignite(*args)
2098
- notifications << :observer_after_failure_ignite
2099
- end
2100
-
2101
- def after_failure_to_transition(*args)
2102
- notifications << :observer_after_failure_transition
2103
- end
2104
- end
2105
- instance = observer.instance
2106
- instance.notifications = @notifications
2107
-
2108
- @transition.perform
2109
- end
2110
-
2111
- def test_should_invoke_callbacks_in_specific_order
2112
- expected = [
2113
- :callback_after_failure,
2114
- :observer_after_failure_ignite,
2115
- :observer_after_failure_transition
2116
- ]
2117
-
2118
- assert_equal expected, @notifications
2119
- end
2120
- end
2121
-
2122
- class MachineWithMixedCallbacksTest < BaseTestCase
2123
- def setup
2124
- @model = new_model
2125
- @machine = EnumStateMachine::Machine.new(@model)
2126
- @machine.state :parked, :idling
2127
- @machine.event :ignite
2128
- @record = @model.new(:state => 'parked')
2129
- @transition = EnumStateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
2130
-
2131
- @notifications = []
2132
-
2133
- # Create callbacks
2134
- @machine.before_transition {@notifications << :callback_before_transition}
2135
- @machine.after_transition {@notifications << :callback_after_transition}
2136
- @machine.around_transition do |block|
2137
- @notifications << :callback_around_before_transition
2138
- block.call
2139
- @notifications << :callback_arond_after_transition
2140
- end
2141
-
2142
- # Create observer callbacks
2143
- observer = new_observer(@model) do
2144
- def before_ignite(*args)
2145
- notifications << :observer_before_ignite
2146
- end
2147
-
2148
- def before_transition(*args)
2149
- notifications << :observer_before_transition
2150
- end
2151
-
2152
- def after_ignite(*args)
2153
- notifications << :observer_after_ignite
2154
- end
2155
-
2156
- def after_transition(*args)
2157
- notifications << :observer_after_transition
2158
- end
2159
- end
2160
- instance = observer.instance
2161
- instance.notifications = @notifications
2162
-
2163
- @transition.perform
2164
- end
2165
-
2166
- def test_should_invoke_callbacks_in_specific_order
2167
- expected = [
2168
- :callback_before_transition,
2169
- :callback_around_before_transition,
2170
- :observer_before_ignite,
2171
- :observer_before_transition,
2172
- :callback_arond_after_transition,
2173
- :callback_after_transition,
2174
- :observer_after_ignite,
2175
- :observer_after_transition
2176
- ]
2177
-
2178
- assert_equal expected, @notifications
2179
- end
2180
- end
2181
-
2182
- if ActiveRecord.const_defined?(:NamedScope)
2183
- class MachineWithScopesTest < BaseTestCase
2184
- def setup
2185
- @model = new_model
2186
- @machine = EnumStateMachine::Machine.new(@model)
2187
- @machine.state :parked, :first_gear
2188
- @machine.state :idling, :value => lambda {'idling'}
2189
- end
2190
-
2191
- def test_should_create_singular_with_scope
2192
- assert @model.respond_to?(:with_state)
2193
- end
2194
-
2195
- def test_should_only_include_records_with_state_in_singular_with_scope
2196
- parked = @model.create :state => 'parked'
2197
- @model.create :state => 'idling'
2198
-
2199
- assert_equal [parked], @model.with_state(:parked).find(:all)
2200
- end
2201
-
2202
- def test_should_create_plural_with_scope
2203
- assert @model.respond_to?(:with_states)
2204
- end
2205
-
2206
- def test_should_only_include_records_with_states_in_plural_with_scope
2207
- parked = @model.create :state => 'parked'
2208
- idling = @model.create :state => 'idling'
2209
-
2210
- assert_equal [parked, idling], @model.with_states(:parked, :idling).find(:all)
2211
- end
2212
-
2213
- def test_should_allow_lookup_by_string_name
2214
- parked = @model.create :state => 'parked'
2215
- idling = @model.create :state => 'idling'
2216
-
2217
- assert_equal [parked, idling], @model.with_states('parked', 'idling').find(:all)
2218
- end
2219
-
2220
- def test_should_create_singular_without_scope
2221
- assert @model.respond_to?(:without_state)
2222
- end
2223
-
2224
- def test_should_only_include_records_without_state_in_singular_without_scope
2225
- parked = @model.create :state => 'parked'
2226
- idling = @model.create :state => 'idling'
2227
-
2228
- assert_equal [parked], @model.without_state(:idling).find(:all)
2229
- end
2230
-
2231
- def test_should_create_plural_without_scope
2232
- assert @model.respond_to?(:without_states)
2233
- end
2234
-
2235
- def test_should_only_include_records_without_states_in_plural_without_scope
2236
- parked = @model.create :state => 'parked'
2237
- idling = @model.create :state => 'idling'
2238
- first_gear = @model.create :state => 'first_gear'
2239
-
2240
- assert_equal [parked, idling], @model.without_states(:first_gear).find(:all)
2241
- end
2242
-
2243
- def test_should_allow_chaining_scopes
2244
- parked = @model.create :state => 'parked'
2245
- idling = @model.create :state => 'idling'
2246
-
2247
- assert_equal [idling], @model.without_state(:parked).with_state(:idling).find(:all)
2248
- end
2249
- end
2250
-
2251
- class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
2252
- def setup
2253
- @model = new_model
2254
- @machine = EnumStateMachine::Machine.new(@model, :state)
2255
-
2256
- @subclass = Class.new(@model)
2257
- @subclass_machine = @subclass.state_machine(:state) {}
2258
- @subclass_machine.state :parked, :idling, :first_gear
2259
- end
2260
-
2261
- def test_should_only_include_records_with_subclass_states_in_with_scope
2262
- parked = @subclass.create :state => 'parked'
2263
- idling = @subclass.create :state => 'idling'
2264
-
2265
- assert_equal [parked, idling], @subclass.with_states(:parked, :idling).find(:all)
2266
- end
2267
-
2268
- def test_should_only_include_records_without_subclass_states_in_without_scope
2269
- parked = @subclass.create :state => 'parked'
2270
- idling = @subclass.create :state => 'idling'
2271
- first_gear = @subclass.create :state => 'first_gear'
2272
-
2273
- assert_equal [parked, idling], @subclass.without_states(:first_gear).find(:all)
2274
- end
2275
- end
2276
-
2277
- class MachineWithComplexPluralizationScopesTest < BaseTestCase
2278
- def setup
2279
- @model = new_model
2280
- @machine = EnumStateMachine::Machine.new(@model, :status)
2281
- end
2282
-
2283
- def test_should_create_singular_with_scope
2284
- assert @model.respond_to?(:with_status)
2285
- end
2286
-
2287
- def test_should_create_plural_with_scope
2288
- assert @model.respond_to?(:with_statuses)
2289
- end
2290
- end
2291
-
2292
- class MachineWithScopesAndJoinsTest < BaseTestCase
2293
- def setup
2294
- @company = new_model(:company)
2295
- ActiveRecordTest.const_set('Company', @company)
2296
-
2297
- @vehicle = new_model(:vehicle) do
2298
- connection.add_column table_name, :company_id, :integer
2299
- belongs_to :company, :class_name => 'ActiveRecordTest::Company'
2300
- end
2301
- ActiveRecordTest.const_set('Vehicle', @vehicle)
2302
-
2303
- @company_machine = EnumStateMachine::Machine.new(@company, :initial => :active)
2304
- @vehicle_machine = EnumStateMachine::Machine.new(@vehicle, :initial => :parked)
2305
- @vehicle_machine.state :idling
2306
-
2307
- @ford = @company.create
2308
- @mustang = @vehicle.create(:company => @ford)
2309
- end
2310
-
2311
- def test_should_find_records_in_with_scope
2312
- assert_equal [@mustang], @vehicle.with_states(:parked).find(:all, :joins => :company, :conditions => "#{@company.table_name}.state = \"active\"")
2313
- end
2314
-
2315
- def test_should_find_records_in_without_scope
2316
- assert_equal [@mustang], @vehicle.without_states(:idling).find(:all, :joins => :company, :conditions => "#{@company.table_name}.state = \"active\"")
2317
- end
2318
-
2319
- def teardown
2320
- ActiveRecordTest.class_eval do
2321
- remove_const('Vehicle')
2322
- remove_const('Company')
2323
- end
2324
- ActiveSupport::Dependencies.clear if defined?(ActiveSupport::Dependencies)
2325
- super
2326
- end
2327
- end
2328
- else
2329
- $stderr.puts 'Skipping ActiveRecord Scope tests.'
2330
- end
2331
-
2332
- if ActiveRecord.const_defined?(:Relation)
2333
- class MachineWithDefaultScope < BaseTestCase
2334
- def setup
2335
- @model = new_model
2336
- @machine = EnumStateMachine::Machine.new(@model, :initial => :parked)
2337
- @machine.state :idling
2338
-
2339
- @model.class_eval do
2340
- default_scope { with_state(:parked, :idling) }
2341
- end
2342
- end
2343
-
2344
- def test_should_set_initial_state_on_created_object
2345
- object = @model.new
2346
- assert_equal 'parked', object.state
2347
- end
2348
- end
2349
- else
2350
- $stderr.puts 'Skipping ActiveRecord Default Scope tests.'
2351
- end
2352
-
2353
- if Object.const_defined?(:I18n)
2354
- class MachineWithInternationalizationTest < BaseTestCase
2355
- def setup
2356
- I18n.backend = I18n::Backend::Simple.new
2357
-
2358
- # Initialize the backend
2359
- EnumStateMachine::Machine.new(new_model)
2360
- I18n.backend.translate(:en, 'activerecord.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
2361
-
2362
- @model = new_model
2363
- end
2364
-
2365
- def test_should_use_defaults
2366
- I18n.backend.store_translations(:en, {
2367
- :activerecord => {:errors => {:messages => {:invalid_transition => "cannot #{interpolation_key('event')}"}}}
2368
- })
2369
-
2370
- machine = EnumStateMachine::Machine.new(@model)
2371
- machine.state :parked, :idling
2372
- machine.event :ignite
2373
-
2374
- record = @model.new(:state => 'idling')
2375
-
2376
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
2377
- assert_equal ['State cannot ignite'], record.errors.full_messages
2378
- end
2379
-
2380
- def test_should_allow_customized_error_key
2381
- I18n.backend.store_translations(:en, {
2382
- :activerecord => {:errors => {:messages => {:bad_transition => "cannot #{interpolation_key('event')}"}}}
2383
- })
2384
-
2385
- machine = EnumStateMachine::Machine.new(@model, :messages => {:invalid_transition => :bad_transition})
2386
- machine.state :parked, :idling
2387
-
2388
- record = @model.new(:state => 'idling')
2389
-
2390
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
2391
- assert_equal ['State cannot ignite'], record.errors.full_messages
2392
- end
2393
-
2394
- def test_should_allow_customized_error_string
2395
- machine = EnumStateMachine::Machine.new(@model, :messages => {:invalid_transition => "cannot #{interpolation_key('event')}"})
2396
- machine.state :parked, :idling
2397
-
2398
- record = @model.new(:state => 'idling')
2399
-
2400
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
2401
- assert_equal ['State cannot ignite'], record.errors.full_messages
2402
- end
2403
-
2404
- def test_should_allow_customized_state_key_scoped_to_class_and_machine
2405
- I18n.backend.store_translations(:en, {
2406
- :activerecord => {:state_machines => {:'active_record_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
2407
- })
2408
-
2409
- machine = EnumStateMachine::Machine.new(@model)
2410
- machine.state :parked
2411
-
2412
- assert_equal 'shutdown', machine.state(:parked).human_name
2413
- end
2414
-
2415
- def test_should_allow_customized_state_key_scoped_to_class
2416
- I18n.backend.store_translations(:en, {
2417
- :activerecord => {:state_machines => {:'active_record_test/foo' => {:states => {:parked => 'shutdown'}}}}
2418
- })
2419
-
2420
- machine = EnumStateMachine::Machine.new(@model)
2421
- machine.state :parked
2422
-
2423
- assert_equal 'shutdown', machine.state(:parked).human_name
2424
- end
2425
-
2426
- def test_should_allow_customized_state_key_scoped_to_machine
2427
- I18n.backend.store_translations(:en, {
2428
- :activerecord => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
2429
- })
2430
-
2431
- machine = EnumStateMachine::Machine.new(@model)
2432
- machine.state :parked
2433
-
2434
- assert_equal 'shutdown', machine.state(:parked).human_name
2435
- end
2436
-
2437
- def test_should_allow_customized_state_key_unscoped
2438
- I18n.backend.store_translations(:en, {
2439
- :activerecord => {:state_machines => {:states => {:parked => 'shutdown'}}}
2440
- })
2441
-
2442
- machine = EnumStateMachine::Machine.new(@model)
2443
- machine.state :parked
2444
-
2445
- assert_equal 'shutdown', machine.state(:parked).human_name
2446
- end
2447
-
2448
- def test_should_support_nil_state_key
2449
- I18n.backend.store_translations(:en, {
2450
- :activerecord => {:state_machines => {:states => {:nil => 'empty'}}}
2451
- })
2452
-
2453
- machine = EnumStateMachine::Machine.new(@model)
2454
-
2455
- assert_equal 'empty', machine.state(nil).human_name
2456
- end
2457
-
2458
- def test_should_allow_customized_event_key_scoped_to_class_and_machine
2459
- I18n.backend.store_translations(:en, {
2460
- :activerecord => {:state_machines => {:'active_record_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
2461
- })
2462
-
2463
- machine = EnumStateMachine::Machine.new(@model)
2464
- machine.event :park
2465
-
2466
- assert_equal 'stop', machine.event(:park).human_name
2467
- end
2468
-
2469
- def test_should_allow_customized_event_key_scoped_to_class
2470
- I18n.backend.store_translations(:en, {
2471
- :activerecord => {:state_machines => {:'active_record_test/foo' => {:events => {:park => 'stop'}}}}
2472
- })
2473
-
2474
- machine = EnumStateMachine::Machine.new(@model)
2475
- machine.event :park
2476
-
2477
- assert_equal 'stop', machine.event(:park).human_name
2478
- end
2479
-
2480
- def test_should_allow_customized_event_key_scoped_to_machine
2481
- I18n.backend.store_translations(:en, {
2482
- :activerecord => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
2483
- })
2484
-
2485
- machine = EnumStateMachine::Machine.new(@model)
2486
- machine.event :park
2487
-
2488
- assert_equal 'stop', machine.event(:park).human_name
2489
- end
2490
-
2491
- def test_should_allow_customized_event_key_unscoped
2492
- I18n.backend.store_translations(:en, {
2493
- :activerecord => {:state_machines => {:events => {:park => 'stop'}}}
2494
- })
2495
-
2496
- machine = EnumStateMachine::Machine.new(@model)
2497
- machine.event :park
2498
-
2499
- assert_equal 'stop', machine.event(:park).human_name
2500
- end
2501
-
2502
- def test_should_only_add_locale_once_in_load_path
2503
- assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_record/locale\.rb$}}.length
2504
-
2505
- # Create another ActiveRecord model that will triger the i18n feature
2506
- new_model
2507
-
2508
- assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_record/locale\.rb$}}.length
2509
- end
2510
-
2511
- def test_should_add_locale_to_beginning_of_load_path
2512
- @original_load_path = I18n.load_path
2513
- I18n.backend = I18n::Backend::Simple.new
2514
-
2515
- app_locale = File.dirname(__FILE__) + '/../../files/en.yml'
2516
- default_locale = File.dirname(__FILE__) + '/../../../lib/state_machine/integrations/active_record/locale.rb'
2517
- I18n.load_path = [app_locale]
2518
-
2519
- EnumStateMachine::Machine.new(@model)
2520
-
2521
- assert_equal [default_locale, app_locale].map {|path| File.expand_path(path)}, I18n.load_path.map {|path| File.expand_path(path)}
2522
- ensure
2523
- I18n.load_path = @original_load_path
2524
- end
2525
-
2526
- def test_should_prefer_other_locales_first
2527
- @original_load_path = I18n.load_path
2528
- I18n.backend = I18n::Backend::Simple.new
2529
- I18n.load_path = [File.dirname(__FILE__) + '/../../files/en.yml']
2530
-
2531
- machine = EnumStateMachine::Machine.new(@model)
2532
- machine.state :parked, :idling
2533
- machine.event :ignite
2534
-
2535
- record = @model.new(:state => 'idling')
2536
-
2537
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
2538
- assert_equal ['State cannot transition'], record.errors.full_messages
2539
- ensure
2540
- I18n.load_path = @original_load_path
2541
- end
2542
-
2543
- private
2544
- def interpolation_key(key)
2545
- !defined?(I18n::VERSION) || I18n::VERSION < '0.4.0' ? "{{#{key}}}" : "%{#{key}}"
2546
- end
2547
- end
2548
- else
2549
- $stderr.puts 'Skipping ActiveRecord I18n tests.'
2550
- end
2551
- end