hsume2-state_machine 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/CHANGELOG.rdoc +413 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +717 -0
  4. data/Rakefile +77 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine.rb +448 -0
  28. data/lib/state_machine/alternate_machine.rb +79 -0
  29. data/lib/state_machine/assertions.rb +36 -0
  30. data/lib/state_machine/branch.rb +224 -0
  31. data/lib/state_machine/callback.rb +236 -0
  32. data/lib/state_machine/condition_proxy.rb +94 -0
  33. data/lib/state_machine/error.rb +13 -0
  34. data/lib/state_machine/eval_helpers.rb +86 -0
  35. data/lib/state_machine/event.rb +304 -0
  36. data/lib/state_machine/event_collection.rb +139 -0
  37. data/lib/state_machine/extensions.rb +149 -0
  38. data/lib/state_machine/initializers.rb +4 -0
  39. data/lib/state_machine/initializers/merb.rb +1 -0
  40. data/lib/state_machine/initializers/rails.rb +25 -0
  41. data/lib/state_machine/integrations.rb +110 -0
  42. data/lib/state_machine/integrations/active_model.rb +502 -0
  43. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  44. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  45. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  46. data/lib/state_machine/integrations/active_record.rb +424 -0
  47. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  48. data/lib/state_machine/integrations/active_record/versions.rb +143 -0
  49. data/lib/state_machine/integrations/base.rb +91 -0
  50. data/lib/state_machine/integrations/data_mapper.rb +392 -0
  51. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  52. data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
  53. data/lib/state_machine/integrations/mongo_mapper.rb +272 -0
  54. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  55. data/lib/state_machine/integrations/mongo_mapper/versions.rb +110 -0
  56. data/lib/state_machine/integrations/mongoid.rb +357 -0
  57. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  58. data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
  59. data/lib/state_machine/integrations/sequel.rb +428 -0
  60. data/lib/state_machine/integrations/sequel/versions.rb +36 -0
  61. data/lib/state_machine/machine.rb +1873 -0
  62. data/lib/state_machine/machine_collection.rb +87 -0
  63. data/lib/state_machine/matcher.rb +123 -0
  64. data/lib/state_machine/matcher_helpers.rb +54 -0
  65. data/lib/state_machine/node_collection.rb +157 -0
  66. data/lib/state_machine/path.rb +120 -0
  67. data/lib/state_machine/path_collection.rb +90 -0
  68. data/lib/state_machine/state.rb +271 -0
  69. data/lib/state_machine/state_collection.rb +112 -0
  70. data/lib/state_machine/transition.rb +458 -0
  71. data/lib/state_machine/transition_collection.rb +244 -0
  72. data/lib/tasks/state_machine.rake +1 -0
  73. data/lib/tasks/state_machine.rb +27 -0
  74. data/test/files/en.yml +17 -0
  75. data/test/files/switch.rb +11 -0
  76. data/test/functional/alternate_state_machine_test.rb +122 -0
  77. data/test/functional/state_machine_test.rb +993 -0
  78. data/test/test_helper.rb +4 -0
  79. data/test/unit/assertions_test.rb +40 -0
  80. data/test/unit/branch_test.rb +890 -0
  81. data/test/unit/callback_test.rb +701 -0
  82. data/test/unit/condition_proxy_test.rb +328 -0
  83. data/test/unit/error_test.rb +43 -0
  84. data/test/unit/eval_helpers_test.rb +222 -0
  85. data/test/unit/event_collection_test.rb +358 -0
  86. data/test/unit/event_test.rb +985 -0
  87. data/test/unit/integrations/active_model_test.rb +1097 -0
  88. data/test/unit/integrations/active_record_test.rb +2021 -0
  89. data/test/unit/integrations/base_test.rb +99 -0
  90. data/test/unit/integrations/data_mapper_test.rb +1909 -0
  91. data/test/unit/integrations/mongo_mapper_test.rb +1611 -0
  92. data/test/unit/integrations/mongoid_test.rb +1591 -0
  93. data/test/unit/integrations/sequel_test.rb +1523 -0
  94. data/test/unit/integrations_test.rb +61 -0
  95. data/test/unit/invalid_event_test.rb +20 -0
  96. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  97. data/test/unit/invalid_transition_test.rb +77 -0
  98. data/test/unit/machine_collection_test.rb +599 -0
  99. data/test/unit/machine_test.rb +3043 -0
  100. data/test/unit/matcher_helpers_test.rb +37 -0
  101. data/test/unit/matcher_test.rb +155 -0
  102. data/test/unit/node_collection_test.rb +217 -0
  103. data/test/unit/path_collection_test.rb +266 -0
  104. data/test/unit/path_test.rb +485 -0
  105. data/test/unit/state_collection_test.rb +310 -0
  106. data/test/unit/state_machine_test.rb +31 -0
  107. data/test/unit/state_test.rb +924 -0
  108. data/test/unit/transition_collection_test.rb +2102 -0
  109. data/test/unit/transition_test.rb +1541 -0
  110. metadata +207 -0
@@ -0,0 +1,2021 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ # Load library
4
+ require 'rubygems'
5
+
6
+ gem 'i18n', '<0.5' if ENV['VERSION'] && ENV['VERSION'] >= '2.3.5' && ENV['VERSION'] < '3.0.0'
7
+ gem 'activerecord', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=2.0.0'
8
+ require 'active_record'
9
+
10
+ FIXTURES_ROOT = File.dirname(__FILE__) + '/../../fixtures/'
11
+
12
+ # Load TestCase helpers
13
+ require 'active_support/test_case'
14
+ require 'active_record/fixtures'
15
+
16
+ begin
17
+ require 'active_record/test_case'
18
+ rescue LoadError
19
+ class ActiveRecord::TestCase < ActiveSupport::TestCase
20
+ self.fixture_path = FIXTURES_ROOT
21
+ self.use_instantiated_fixtures = false
22
+ self.use_transactional_fixtures = true
23
+ end
24
+ end
25
+
26
+ # Establish database connection
27
+ ActiveRecord::Base.establish_connection({'adapter' => 'sqlite3', 'database' => ':memory:'})
28
+ ActiveRecord::Base.logger = Logger.new("#{File.dirname(__FILE__)}/../../active_record.log")
29
+
30
+ module ActiveRecordTest
31
+ class BaseTestCase < ActiveRecord::TestCase
32
+ def default_test
33
+ end
34
+
35
+ protected
36
+ # Creates a new ActiveRecord model (and the associated table)
37
+ def new_model(create_table = :foo, &block)
38
+ table_name = create_table || :foo
39
+
40
+ model = Class.new(ActiveRecord::Base) do
41
+ connection.create_table(table_name, :force => true) {|t| t.string(:state)} if create_table
42
+ set_table_name(table_name.to_s)
43
+
44
+ (class << self; self; end).class_eval do
45
+ define_method(:name) { "ActiveRecordTest::#{table_name.to_s.capitalize}" }
46
+ end
47
+ end
48
+ model.class_eval(&block) if block_given?
49
+ model.reset_column_information if create_table
50
+ model
51
+ end
52
+
53
+ # Creates a new ActiveRecord observer
54
+ def new_observer(model, &block)
55
+ observer = Class.new(ActiveRecord::Observer) do
56
+ attr_accessor :notifications
57
+
58
+ def initialize
59
+ super
60
+ @notifications = []
61
+ end
62
+ end
63
+
64
+ (class << observer; self; end).class_eval do
65
+ define_method(:name) do
66
+ "#{model.name}Observer"
67
+ end
68
+ end
69
+
70
+ observer.observe(model)
71
+ observer.class_eval(&block) if block_given?
72
+ observer
73
+ end
74
+ end
75
+
76
+ class IntegrationTest < BaseTestCase
77
+ def test_should_have_an_integration_name
78
+ assert_equal :active_record, StateMachine::Integrations::ActiveRecord.integration_name
79
+ end
80
+
81
+ def test_should_be_available
82
+ assert StateMachine::Integrations::ActiveRecord.available?
83
+ end
84
+
85
+ def test_should_match_if_class_inherits_from_active_record
86
+ assert StateMachine::Integrations::ActiveRecord.matches?(new_model)
87
+ end
88
+
89
+ def test_should_not_match_if_class_does_not_inherit_from_active_record
90
+ assert !StateMachine::Integrations::ActiveRecord.matches?(Class.new)
91
+ end
92
+
93
+ def test_should_have_defaults
94
+ assert_equal e = {:action => :save}, StateMachine::Integrations::ActiveRecord.defaults
95
+ end
96
+
97
+ def test_should_have_a_locale_path
98
+ assert_not_nil StateMachine::Integrations::ActiveRecord.locale_path
99
+ end
100
+ end
101
+
102
+ class MachineWithoutDatabaseTest < BaseTestCase
103
+ def setup
104
+ @model = new_model(false) do
105
+ # Simulate the database not being available entirely
106
+ def self.connection
107
+ raise ActiveRecord::ConnectionNotEstablished
108
+ end
109
+ end
110
+ end
111
+
112
+ def test_should_allow_machine_creation
113
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
114
+ end
115
+ end
116
+
117
+ class MachineUnmigratedTest < BaseTestCase
118
+ def setup
119
+ @model = new_model(false)
120
+
121
+ # Drop the table so that it definitely doesn't exist
122
+ @model.connection.drop_table(:foo) if @model.table_exists?
123
+ end
124
+
125
+ def test_should_allow_machine_creation
126
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
127
+ end
128
+ end
129
+
130
+ class MachineByDefaultTest < BaseTestCase
131
+ def setup
132
+ @model = new_model
133
+ @machine = StateMachine::Machine.new(@model)
134
+ end
135
+
136
+ def test_should_use_save_as_action
137
+ assert_equal :save, @machine.action
138
+ end
139
+
140
+ def test_should_use_transactions
141
+ assert_equal true, @machine.use_transactions
142
+ end
143
+
144
+ def test_should_create_notifier_before_callback
145
+ assert_equal 1, @machine.callbacks[:before].size
146
+ end
147
+
148
+ def test_should_create_notifier_after_callback
149
+ assert_equal 1, @machine.callbacks[:after].size
150
+ end
151
+ end
152
+
153
+ class MachineWithStatesTest < BaseTestCase
154
+ def setup
155
+ @model = new_model
156
+ @machine = StateMachine::Machine.new(@model)
157
+ @machine.state :first_gear
158
+ end
159
+
160
+ def test_should_humanize_name
161
+ assert_equal 'first gear', @machine.state(:first_gear).human_name
162
+ end
163
+ end
164
+
165
+ class MachineWithStaticInitialStateTest < BaseTestCase
166
+ def setup
167
+ @model = new_model do
168
+ attr_accessor :value
169
+ end
170
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
171
+ end
172
+
173
+ def test_should_set_initial_state_on_created_object
174
+ record = @model.new
175
+ assert_equal 'parked', record.state
176
+ end
177
+
178
+ def test_should_set_initial_state_with_nil_attributes
179
+ record = @model.new(nil)
180
+ assert_equal 'parked', record.state
181
+ end
182
+
183
+ def test_should_still_set_attributes
184
+ record = @model.new(:value => 1)
185
+ assert_equal 1, record.value
186
+ end
187
+
188
+ def test_should_still_allow_initialize_blocks
189
+ block_args = nil
190
+ record = @model.new do |*args|
191
+ block_args = args
192
+ end
193
+
194
+ assert_equal [record], block_args
195
+ end
196
+
197
+ def test_should_set_attributes_prior_to_after_initialize_hook
198
+ state = nil
199
+ @model.class_eval {define_method(:after_initialize) {}} if ::ActiveRecord::VERSION::MAJOR <= 2
200
+ @model.after_initialize do |record|
201
+ state = record.state
202
+ end
203
+ @model.new
204
+ assert_equal 'parked', state
205
+ end
206
+
207
+ def test_should_set_initial_state_before_setting_attributes
208
+ @model.class_eval do
209
+ attr_accessor :state_during_setter
210
+
211
+ define_method(:value=) do |value|
212
+ self.state_during_setter = state
213
+ end
214
+ end
215
+
216
+ record = @model.new(:value => 1)
217
+ assert_equal 'parked', record.state_during_setter
218
+ end
219
+
220
+ def test_should_not_set_initial_state_after_already_initialized
221
+ record = @model.new(:value => 1)
222
+ assert_equal 'parked', record.state
223
+
224
+ record.state = 'idling'
225
+ record.attributes = {}
226
+ assert_equal 'idling', record.state
227
+ end
228
+
229
+ def test_should_use_stored_values_when_loading_from_database
230
+ @machine.state :idling
231
+
232
+ record = @model.find(@model.create(:state => 'idling').id)
233
+ assert_equal 'idling', record.state
234
+ end
235
+
236
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
237
+ @machine.state nil
238
+
239
+ record = @model.find(@model.create(:state => nil).id)
240
+ assert_nil record.state
241
+ end
242
+ end
243
+
244
+ class MachineWithDynamicInitialStateTest < BaseTestCase
245
+ def setup
246
+ @model = new_model do
247
+ attr_accessor :value
248
+ end
249
+ @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
250
+ @machine.state :parked
251
+ end
252
+
253
+ def test_should_set_initial_state_on_created_object
254
+ record = @model.new
255
+ assert_equal 'parked', record.state
256
+ end
257
+
258
+ def test_should_still_set_attributes
259
+ record = @model.new(:value => 1)
260
+ assert_equal 1, record.value
261
+ end
262
+
263
+ def test_should_still_allow_initialize_blocks
264
+ block_args = nil
265
+ record = @model.new do |*args|
266
+ block_args = args
267
+ end
268
+
269
+ assert_equal [record], block_args
270
+ end
271
+
272
+ def test_should_set_attributes_prior_to_after_initialize_hook
273
+ state = nil
274
+ @model.class_eval {define_method(:after_initialize) {}} if ::ActiveRecord::VERSION::MAJOR <= 2
275
+ @model.after_initialize do |record|
276
+ state = record.state
277
+ end
278
+ @model.new
279
+ assert_equal 'parked', state
280
+ end
281
+
282
+ def test_should_set_initial_state_after_setting_attributes
283
+ @model.class_eval do
284
+ attr_accessor :state_during_setter
285
+
286
+ define_method(:value=) do |value|
287
+ self.state_during_setter = state || 'nil'
288
+ end
289
+ end
290
+
291
+ record = @model.new(:value => 1)
292
+ assert_equal 'nil', record.state_during_setter
293
+ end
294
+
295
+ def test_should_not_set_initial_state_after_already_initialized
296
+ record = @model.new(:value => 1)
297
+ assert_equal 'parked', record.state
298
+
299
+ record.state = 'idling'
300
+ record.attributes = {}
301
+ assert_equal 'idling', record.state
302
+ end
303
+
304
+ def test_should_use_stored_values_when_loading_from_database
305
+ @machine.state :idling
306
+
307
+ record = @model.find(@model.create(:state => 'idling').id)
308
+ assert_equal 'idling', record.state
309
+ end
310
+
311
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
312
+ @machine.state nil
313
+
314
+ record = @model.find(@model.create(:state => nil).id)
315
+ assert_nil record.state
316
+ end
317
+ end
318
+
319
+ class MachineWithEventsTest < BaseTestCase
320
+ def setup
321
+ @model = new_model
322
+ @machine = StateMachine::Machine.new(@model)
323
+ @machine.event :shift_up
324
+ end
325
+
326
+ def test_should_humanize_name
327
+ assert_equal 'shift up', @machine.event(:shift_up).human_name
328
+ end
329
+ end
330
+
331
+ class MachineWithColumnDefaultTest < BaseTestCase
332
+ def setup
333
+ @model = new_model do
334
+ connection.add_column :foo, :status, :string, :default => 'idling'
335
+ end
336
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
337
+ @record = @model.new
338
+ end
339
+
340
+ def test_should_use_machine_default
341
+ assert_equal 'parked', @record.status
342
+ end
343
+ end
344
+
345
+ class MachineWithConflictingPredicateTest < BaseTestCase
346
+ def setup
347
+ @model = new_model do
348
+ def state?(*args)
349
+ true
350
+ end
351
+ end
352
+
353
+ @machine = StateMachine::Machine.new(@model)
354
+ @record = @model.new
355
+ end
356
+
357
+ def test_should_not_define_attribute_predicate
358
+ assert @record.state?
359
+ end
360
+ end
361
+
362
+ class MachineWithColumnStateAttributeTest < BaseTestCase
363
+ def setup
364
+ @model = new_model
365
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
366
+ @machine.other_states(:idling)
367
+
368
+ @record = @model.new
369
+ end
370
+
371
+ def test_should_not_override_the_column_reader
372
+ @record[:state] = 'parked'
373
+ assert_equal 'parked', @record.state
374
+ end
375
+
376
+ def test_should_not_override_the_column_writer
377
+ @record.state = 'parked'
378
+ assert_equal 'parked', @record[:state]
379
+ end
380
+
381
+ def test_should_have_an_attribute_predicate
382
+ assert @record.respond_to?(:state?)
383
+ end
384
+
385
+ def test_should_test_for_existence_on_predicate_without_parameters
386
+ assert @record.state?
387
+
388
+ @record.state = nil
389
+ assert !@record.state?
390
+ end
391
+
392
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
393
+ assert !@record.state?(:idling)
394
+ end
395
+
396
+ def test_should_return_true_for_predicate_if_matches_current_value
397
+ assert @record.state?(:parked)
398
+ end
399
+
400
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
401
+ assert_raise(IndexError) { @record.state?(:invalid) }
402
+ end
403
+ end
404
+
405
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
406
+ def setup
407
+ @model = new_model do
408
+ def initialize
409
+ # Skip attribute initialization
410
+ @initialized_state_machines = true
411
+ super
412
+ end
413
+ end
414
+
415
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
416
+ @machine.other_states(:idling)
417
+ @record = @model.new
418
+ end
419
+
420
+ def test_should_not_define_a_column_for_the_attribute
421
+ assert_nil @model.columns_hash['status']
422
+ end
423
+
424
+ def test_should_define_a_reader_attribute_for_the_attribute
425
+ assert @record.respond_to?(:status)
426
+ end
427
+
428
+ def test_should_define_a_writer_attribute_for_the_attribute
429
+ assert @record.respond_to?(:status=)
430
+ end
431
+
432
+ def test_should_define_an_attribute_predicate
433
+ assert @record.respond_to?(:status?)
434
+ end
435
+ end
436
+
437
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
438
+ def setup
439
+ @model = new_model do
440
+ def status=(value)
441
+ self['status'] = value
442
+ end
443
+
444
+ def status
445
+ self['status']
446
+ end
447
+ end
448
+
449
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
450
+ @machine.other_states(:idling)
451
+ @record = @model.new
452
+ end
453
+
454
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
455
+ assert !@record.status?(:idling)
456
+ end
457
+
458
+ def test_should_return_true_for_predicate_if_matches_current_value
459
+ assert @record.status?(:parked)
460
+ end
461
+
462
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
463
+ assert_raise(IndexError) { @record.status?(:invalid) }
464
+ end
465
+
466
+ def test_should_set_initial_state_on_created_object
467
+ assert_equal 'parked', @record.status
468
+ end
469
+ end
470
+
471
+ class MachineWithAliasedAttributeTest < BaseTestCase
472
+ def setup
473
+ @model = new_model do
474
+ alias_attribute :vehicle_status, :state
475
+ end
476
+
477
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
478
+ @machine.state :parked
479
+
480
+ @record = @model.new
481
+ end
482
+
483
+ def test_should_check_custom_attribute_for_predicate
484
+ @record.vehicle_status = nil
485
+ assert !@record.status?(:parked)
486
+
487
+ @record.vehicle_status = 'parked'
488
+ assert @record.status?(:parked)
489
+ end
490
+ end
491
+
492
+ class MachineWithInitializedStateTest < BaseTestCase
493
+ def setup
494
+ @model = new_model
495
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
496
+ @machine.state :idling
497
+ end
498
+
499
+ def test_should_allow_nil_initial_state_when_static
500
+ @machine.state nil
501
+
502
+ record = @model.new(:state => nil)
503
+ assert_nil record.state
504
+ end
505
+
506
+ def test_should_allow_nil_initial_state_when_dynamic
507
+ @machine.state nil
508
+
509
+ @machine.initial_state = lambda {:parked}
510
+ record = @model.new(:state => nil)
511
+ assert_nil record.state
512
+ end
513
+
514
+ def test_should_allow_different_initial_state_when_static
515
+ record = @model.new(:state => 'idling')
516
+ assert_equal 'idling', record.state
517
+ end
518
+
519
+ def test_should_allow_different_initial_state_when_dynamic
520
+ @machine.initial_state = lambda {:parked}
521
+ record = @model.new(:state => 'idling')
522
+ assert_equal 'idling', record.state
523
+ end
524
+
525
+ def test_should_use_default_state_if_protected
526
+ @model.class_eval do
527
+ attr_protected :state
528
+ end
529
+
530
+ record = @model.new(:state => 'idling')
531
+ assert_equal 'parked', record.state
532
+ end
533
+ end
534
+
535
+ class MachineMultipleTest < BaseTestCase
536
+ def setup
537
+ @model = new_model do
538
+ connection.add_column :foo, :status, :string
539
+ end
540
+ @state_machine = StateMachine::Machine.new(@model, :initial => :parked)
541
+ @status_machine = StateMachine::Machine.new(@model, :status, :initial => :idling)
542
+ end
543
+
544
+ def test_should_should_initialize_each_state
545
+ record = @model.new
546
+ assert_equal 'parked', record.state
547
+ assert_equal 'idling', record.status
548
+ end
549
+ end
550
+
551
+ class MachineWithLoopbackTest < BaseTestCase
552
+ def setup
553
+ @model = new_model do
554
+ connection.add_column :foo, :updated_at, :datetime
555
+ end
556
+
557
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
558
+ @machine.event :park
559
+
560
+ @record = @model.create(:updated_at => Time.now - 1)
561
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
562
+
563
+ @timestamp = @record.updated_at
564
+ @transition.perform
565
+ end
566
+
567
+ def test_should_update_record
568
+ assert_not_equal @timestamp, @record.updated_at
569
+ end
570
+ end
571
+
572
+ if ActiveRecord.const_defined?(:Dirty) || ActiveRecord::AttributeMethods.const_defined?(:Dirty)
573
+ class MachineWithDirtyAttributesTest < BaseTestCase
574
+ def setup
575
+ @model = new_model
576
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
577
+ @machine.event :ignite
578
+ @machine.state :idling
579
+
580
+ @record = @model.create
581
+
582
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
583
+ @transition.perform(false)
584
+ end
585
+
586
+ def test_should_include_state_in_changed_attributes
587
+ assert_equal %w(state), @record.changed
588
+ end
589
+
590
+ def test_should_track_attribute_change
591
+ assert_equal %w(parked idling), @record.changes['state']
592
+ end
593
+
594
+ def test_should_not_reset_changes_on_multiple_transitions
595
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
596
+ transition.perform(false)
597
+
598
+ assert_equal %w(parked idling), @record.changes['state']
599
+ end
600
+
601
+ def test_should_not_have_changes_when_loaded_from_database
602
+ record = @model.find(@record.id)
603
+ assert !record.changed?
604
+ end
605
+ end
606
+
607
+ class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
608
+ def setup
609
+ @model = new_model
610
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
611
+ @machine.event :park
612
+
613
+ @record = @model.create
614
+
615
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
616
+ @transition.perform(false)
617
+ end
618
+
619
+ def test_should_include_state_in_changed_attributes
620
+ assert_equal %w(state), @record.changed
621
+ end
622
+
623
+ def test_should_track_attribute_changes
624
+ assert_equal %w(parked parked), @record.changes['state']
625
+ end
626
+ end
627
+
628
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
629
+ def setup
630
+ @model = new_model do
631
+ connection.add_column :foo, :status, :string, :default => 'idling'
632
+ end
633
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
634
+ @machine.event :ignite
635
+ @machine.state :idling
636
+
637
+ @record = @model.create
638
+
639
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
640
+ @transition.perform(false)
641
+ end
642
+
643
+ def test_should_include_state_in_changed_attributes
644
+ assert_equal %w(status), @record.changed
645
+ end
646
+
647
+ def test_should_track_attribute_change
648
+ assert_equal %w(parked idling), @record.changes['status']
649
+ end
650
+
651
+ def test_should_not_reset_changes_on_multiple_transitions
652
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
653
+ transition.perform(false)
654
+
655
+ assert_equal %w(parked idling), @record.changes['status']
656
+ end
657
+ end
658
+
659
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
660
+ def setup
661
+ @model = new_model do
662
+ connection.add_column :foo, :status, :string, :default => 'idling'
663
+ end
664
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
665
+ @machine.event :park
666
+
667
+ @record = @model.create
668
+
669
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
670
+ @transition.perform(false)
671
+ end
672
+
673
+ def test_should_include_state_in_changed_attributes
674
+ assert_equal %w(status), @record.changed
675
+ end
676
+
677
+ def test_should_track_attribute_changes
678
+ assert_equal %w(parked parked), @record.changes['status']
679
+ end
680
+ end
681
+
682
+ class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
683
+ def setup
684
+ @model = new_model
685
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
686
+ @machine.event :ignite
687
+
688
+ @record = @model.create
689
+ @record.state_event = 'ignite'
690
+ end
691
+
692
+ def test_should_include_state_in_changed_attributes
693
+ assert_equal %w(state), @record.changed
694
+ end
695
+
696
+ def test_should_track_attribute_change
697
+ assert_equal %w(parked parked), @record.changes['state']
698
+ end
699
+
700
+ def test_should_not_reset_changes_on_multiple_changes
701
+ @record.state_event = 'ignite'
702
+ assert_equal %w(parked parked), @record.changes['state']
703
+ end
704
+
705
+ def test_should_not_include_state_in_changed_attributes_if_nil
706
+ @record = @model.create
707
+ @record.state_event = nil
708
+
709
+ assert_equal [], @record.changed
710
+ end
711
+ end
712
+ else
713
+ $stderr.puts 'Skipping ActiveRecord Dirty tests. `gem install active_record` >= v2.1.0 and try again.'
714
+ end
715
+
716
+ class MachineWithoutTransactionsTest < BaseTestCase
717
+ def setup
718
+ @model = new_model
719
+ @machine = StateMachine::Machine.new(@model, :use_transactions => false)
720
+ end
721
+
722
+ def test_should_not_rollback_transaction_if_false
723
+ @machine.within_transaction(@model.new) do
724
+ @model.create
725
+ false
726
+ end
727
+
728
+ assert_equal 1, @model.count
729
+ end
730
+
731
+ def test_should_not_rollback_transaction_if_true
732
+ @machine.within_transaction(@model.new) do
733
+ @model.create
734
+ true
735
+ end
736
+
737
+ assert_equal 1, @model.count
738
+ end
739
+ end
740
+
741
+ class MachineWithTransactionsTest < BaseTestCase
742
+ def setup
743
+ @model = new_model
744
+ @machine = StateMachine::Machine.new(@model, :use_transactions => true)
745
+ end
746
+
747
+ def test_should_rollback_transaction_if_false
748
+ @machine.within_transaction(@model.new) do
749
+ @model.create
750
+ false
751
+ end
752
+
753
+ assert_equal 0, @model.count
754
+ end
755
+
756
+ def test_should_not_rollback_transaction_if_true
757
+ @machine.within_transaction(@model.new) do
758
+ @model.create
759
+ true
760
+ end
761
+
762
+ assert_equal 1, @model.count
763
+ end
764
+ end
765
+
766
+ class MachineWithCallbacksTest < BaseTestCase
767
+ def setup
768
+ @model = new_model
769
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
770
+ @machine.other_states :idling
771
+ @machine.event :ignite
772
+
773
+ @record = @model.new(:state => 'parked')
774
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
775
+ end
776
+
777
+ def test_should_run_before_callbacks
778
+ called = false
779
+ @machine.before_transition {called = true}
780
+
781
+ @transition.perform
782
+ assert called
783
+ end
784
+
785
+ def test_should_pass_record_to_before_callbacks_with_one_argument
786
+ record = nil
787
+ @machine.before_transition {|arg| record = arg}
788
+
789
+ @transition.perform
790
+ assert_equal @record, record
791
+ end
792
+
793
+ def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
794
+ callback_args = nil
795
+ @machine.before_transition {|*args| callback_args = args}
796
+
797
+ @transition.perform
798
+ assert_equal [@record, @transition], callback_args
799
+ end
800
+
801
+ def test_should_run_before_callbacks_outside_the_context_of_the_record
802
+ context = nil
803
+ @machine.before_transition {context = self}
804
+
805
+ @transition.perform
806
+ assert_equal self, context
807
+ end
808
+
809
+ def test_should_run_after_callbacks
810
+ called = false
811
+ @machine.after_transition {called = true}
812
+
813
+ @transition.perform
814
+ assert called
815
+ end
816
+
817
+ def test_should_pass_record_to_after_callbacks_with_one_argument
818
+ record = nil
819
+ @machine.after_transition {|arg| record = arg}
820
+
821
+ @transition.perform
822
+ assert_equal @record, record
823
+ end
824
+
825
+ def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
826
+ callback_args = nil
827
+ @machine.after_transition {|*args| callback_args = args}
828
+
829
+ @transition.perform
830
+ assert_equal [@record, @transition], callback_args
831
+ end
832
+
833
+ def test_should_run_after_callbacks_outside_the_context_of_the_record
834
+ context = nil
835
+ @machine.after_transition {context = self}
836
+
837
+ @transition.perform
838
+ assert_equal self, context
839
+ end
840
+
841
+ def test_should_run_around_callbacks
842
+ before_called = false
843
+ after_called = false
844
+ @machine.around_transition {|block| before_called = true; block.call; after_called = true}
845
+
846
+ @transition.perform
847
+ assert before_called
848
+ assert after_called
849
+ end
850
+
851
+ def test_should_include_transition_states_in_known_states
852
+ @machine.before_transition :to => :first_gear, :do => lambda {}
853
+
854
+ assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
855
+ end
856
+
857
+ def test_should_allow_symbolic_callbacks
858
+ callback_args = nil
859
+
860
+ klass = class << @record; self; end
861
+ klass.send(:define_method, :after_ignite) do |*args|
862
+ callback_args = args
863
+ end
864
+
865
+ @machine.before_transition(:after_ignite)
866
+
867
+ @transition.perform
868
+ assert_equal [@transition], callback_args
869
+ end
870
+
871
+ def test_should_allow_string_callbacks
872
+ class << @record
873
+ attr_reader :callback_result
874
+ end
875
+
876
+ @machine.before_transition('@callback_result = [1, 2, 3]')
877
+ @transition.perform
878
+
879
+ assert_equal [1, 2, 3], @record.callback_result
880
+ end
881
+ end
882
+
883
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
884
+ def setup
885
+ @callbacks = []
886
+
887
+ @model = new_model
888
+ @machine = StateMachine::Machine.new(@model)
889
+ @machine.state :parked, :idling
890
+ @machine.event :ignite
891
+ @machine.before_transition {@callbacks << :before_1; false}
892
+ @machine.before_transition {@callbacks << :before_2}
893
+ @machine.after_transition {@callbacks << :after}
894
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
895
+
896
+ @record = @model.new(:state => 'parked')
897
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
898
+ @result = @transition.perform
899
+ end
900
+
901
+ def test_should_not_be_successful
902
+ assert !@result
903
+ end
904
+
905
+ def test_should_not_change_current_state
906
+ assert_equal 'parked', @record.state
907
+ end
908
+
909
+ def test_should_not_run_action
910
+ assert @record.new_record?
911
+ end
912
+
913
+ def test_should_not_run_further_callbacks
914
+ assert_equal [:before_1], @callbacks
915
+ end
916
+ end
917
+
918
+ class MachineWithFailedActionTest < BaseTestCase
919
+ def setup
920
+ @model = new_model do
921
+ validates_inclusion_of :state, :in => %w(first_gear)
922
+ end
923
+
924
+ @machine = StateMachine::Machine.new(@model)
925
+ @machine.state :parked, :idling
926
+ @machine.event :ignite
927
+
928
+ @callbacks = []
929
+ @machine.before_transition {@callbacks << :before}
930
+ @machine.after_transition {@callbacks << :after}
931
+ @machine.after_failure {@callbacks << :after_failure}
932
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
933
+
934
+ @record = @model.new(:state => 'parked')
935
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
936
+ @result = @transition.perform
937
+ end
938
+
939
+ def test_should_not_be_successful
940
+ assert !@result
941
+ end
942
+
943
+ def test_should_not_change_current_state
944
+ assert_equal 'parked', @record.state
945
+ end
946
+
947
+ def test_should_not_save_record
948
+ assert @record.new_record?
949
+ end
950
+
951
+ def test_should_run_before_callbacks_and_after_callbacks_with_failures
952
+ assert_equal [:before, :around_before, :after_failure], @callbacks
953
+ end
954
+ end
955
+
956
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
957
+ def setup
958
+ @callbacks = []
959
+
960
+ @model = new_model
961
+ @machine = StateMachine::Machine.new(@model)
962
+ @machine.state :parked, :idling
963
+ @machine.event :ignite
964
+ @machine.after_transition {@callbacks << :after_1; false}
965
+ @machine.after_transition {@callbacks << :after_2}
966
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
967
+
968
+ @record = @model.new(:state => 'parked')
969
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
970
+ @result = @transition.perform
971
+ end
972
+
973
+ def test_should_be_successful
974
+ assert @result
975
+ end
976
+
977
+ def test_should_change_current_state
978
+ assert_equal 'idling', @record.state
979
+ end
980
+
981
+ def test_should_save_record
982
+ assert !@record.new_record?
983
+ end
984
+
985
+ def test_should_not_run_further_after_callbacks
986
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
987
+ end
988
+ end
989
+
990
+ class MachineWithValidationsTest < BaseTestCase
991
+ def setup
992
+ @model = new_model
993
+ @machine = StateMachine::Machine.new(@model)
994
+ @machine.state :parked
995
+
996
+ @record = @model.new
997
+ end
998
+
999
+ def test_should_invalidate_using_errors
1000
+ I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
1001
+ @record.state = 'parked'
1002
+
1003
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
1004
+ assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
1005
+ end
1006
+
1007
+ def test_should_auto_prefix_custom_attributes_on_invalidation
1008
+ @machine.invalidate(@record, :event, :invalid)
1009
+
1010
+ assert_equal ['State event is invalid'], @record.errors.full_messages
1011
+ end
1012
+
1013
+ def test_should_clear_errors_on_reset
1014
+ @record.state = 'parked'
1015
+ @record.errors.add(:state, 'is invalid')
1016
+
1017
+ @machine.reset(@record)
1018
+ assert_equal [], @record.errors.full_messages
1019
+ end
1020
+
1021
+ def test_should_be_valid_if_state_is_known
1022
+ @record.state = 'parked'
1023
+
1024
+ assert @record.valid?
1025
+ end
1026
+
1027
+ def test_should_not_be_valid_if_state_is_unknown
1028
+ @record.state = 'invalid'
1029
+
1030
+ assert !@record.valid?
1031
+ assert_equal ['State is invalid'], @record.errors.full_messages
1032
+ end
1033
+ end
1034
+
1035
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
1036
+ def setup
1037
+ @model = new_model do
1038
+ alias_attribute :status, :state
1039
+ end
1040
+
1041
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
1042
+ @machine.state :parked
1043
+
1044
+ @record = @model.new
1045
+ end
1046
+
1047
+ def test_should_add_validation_errors_to_custom_attribute
1048
+ @record.state = 'invalid'
1049
+
1050
+ assert !@record.valid?
1051
+ assert_equal ['State is invalid'], @record.errors.full_messages
1052
+
1053
+ @record.state = 'parked'
1054
+ assert @record.valid?
1055
+ end
1056
+ end
1057
+
1058
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
1059
+ def setup
1060
+ @model = new_model do
1061
+ attr_accessor :seatbelt
1062
+ end
1063
+
1064
+ @machine = StateMachine::Machine.new(@model)
1065
+ @machine.state :first_gear, :second_gear do
1066
+ validates_presence_of :seatbelt
1067
+ end
1068
+ @machine.other_states :parked
1069
+ end
1070
+
1071
+ def test_should_be_valid_if_validation_fails_outside_state_scope
1072
+ record = @model.new(:state => 'parked', :seatbelt => nil)
1073
+ assert record.valid?
1074
+ end
1075
+
1076
+ def test_should_be_invalid_if_validation_fails_within_state_scope
1077
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
1078
+ assert !record.valid?
1079
+ end
1080
+
1081
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
1082
+ record = @model.new(:state => 'second_gear', :seatbelt => true)
1083
+ assert record.valid?
1084
+ end
1085
+ end
1086
+
1087
+ class MachineWithEventAttributesOnValidationTest < BaseTestCase
1088
+ def setup
1089
+ @model = new_model
1090
+ @machine = StateMachine::Machine.new(@model)
1091
+ @machine.event :ignite do
1092
+ transition :parked => :idling
1093
+ end
1094
+
1095
+ @record = @model.new
1096
+ @record.state = 'parked'
1097
+ @record.state_event = 'ignite'
1098
+ end
1099
+
1100
+ def test_should_fail_if_event_is_invalid
1101
+ @record.state_event = 'invalid'
1102
+ assert !@record.valid?
1103
+ assert_equal ['State event is invalid'], @record.errors.full_messages
1104
+ end
1105
+
1106
+ def test_should_fail_if_event_has_no_transition
1107
+ @record.state = 'idling'
1108
+ assert !@record.valid?
1109
+ assert_equal ['State event cannot transition when idling'], @record.errors.full_messages
1110
+ end
1111
+
1112
+ def test_should_be_successful_if_event_has_transition
1113
+ assert @record.valid?
1114
+ end
1115
+
1116
+ def test_should_run_before_callbacks
1117
+ ran_callback = false
1118
+ @machine.before_transition { ran_callback = true }
1119
+
1120
+ @record.valid?
1121
+ assert ran_callback
1122
+ end
1123
+
1124
+ def test_should_run_around_callbacks_before_yield
1125
+ ran_callback = false
1126
+ @machine.around_transition {|block| ran_callback = true; block.call }
1127
+
1128
+ @record.valid?
1129
+ assert ran_callback
1130
+ end
1131
+
1132
+ def test_should_persist_new_state
1133
+ @record.valid?
1134
+ assert_equal 'idling', @record.state
1135
+ end
1136
+
1137
+ def test_should_not_run_after_callbacks
1138
+ ran_callback = false
1139
+ @machine.after_transition { ran_callback = true }
1140
+
1141
+ @record.valid?
1142
+ assert !ran_callback
1143
+ end
1144
+
1145
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
1146
+ @model.class_eval do
1147
+ attr_accessor :seatbelt
1148
+ validates_presence_of :seatbelt
1149
+ end
1150
+
1151
+ ran_callback = false
1152
+ @machine.after_transition { ran_callback = true }
1153
+
1154
+ @record.valid?
1155
+ assert !ran_callback
1156
+ end
1157
+
1158
+ def test_should_run_after_callbacks_if_validation_fails
1159
+ @model.class_eval do
1160
+ attr_accessor :seatbelt
1161
+ validates_presence_of :seatbelt
1162
+ end
1163
+
1164
+ ran_callback = false
1165
+ @machine.after_failure { ran_callback = true }
1166
+
1167
+ @record.valid?
1168
+ assert ran_callback
1169
+ end
1170
+
1171
+ def test_should_not_run_around_callbacks_after_yield
1172
+ ran_callback = false
1173
+ @machine.around_transition {|block| block.call; ran_callback = true }
1174
+
1175
+ @record.valid?
1176
+ assert !ran_callback
1177
+ end
1178
+
1179
+ def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
1180
+ @model.class_eval do
1181
+ attr_accessor :seatbelt
1182
+ validates_presence_of :seatbelt
1183
+ end
1184
+
1185
+ ran_callback = false
1186
+ @machine.around_transition {|block| block.call; ran_callback = true }
1187
+
1188
+ @record.valid?
1189
+ assert !ran_callback
1190
+ end
1191
+
1192
+ def test_should_not_run_before_transitions_within_transaction
1193
+ @machine.before_transition { @model.create; raise ActiveRecord::Rollback }
1194
+
1195
+ begin
1196
+ @record.valid?
1197
+ rescue Exception
1198
+ end
1199
+
1200
+ assert_equal 1, @model.count
1201
+ end
1202
+ end
1203
+
1204
+ class MachineWithEventAttributesOnSaveTest < BaseTestCase
1205
+ def setup
1206
+ @model = new_model
1207
+ @machine = StateMachine::Machine.new(@model)
1208
+ @machine.event :ignite do
1209
+ transition :parked => :idling
1210
+ end
1211
+
1212
+ @record = @model.new
1213
+ @record.state = 'parked'
1214
+ @record.state_event = 'ignite'
1215
+ end
1216
+
1217
+ def test_should_fail_if_event_is_invalid
1218
+ @record.state_event = 'invalid'
1219
+ assert_equal false, @record.save
1220
+ end
1221
+
1222
+ def test_should_fail_if_event_has_no_transition
1223
+ @record.state = 'idling'
1224
+ assert_equal false, @record.save
1225
+ end
1226
+
1227
+ def test_should_run_before_callbacks
1228
+ ran_callback = false
1229
+ @machine.before_transition { ran_callback = true }
1230
+
1231
+ @record.save
1232
+ assert ran_callback
1233
+ end
1234
+
1235
+ def test_should_run_before_callbacks_once
1236
+ before_count = 0
1237
+ @machine.before_transition { before_count += 1 }
1238
+
1239
+ @record.save
1240
+ assert_equal 1, before_count
1241
+ end
1242
+
1243
+ def test_should_run_around_callbacks_before_yield
1244
+ ran_callback = false
1245
+ @machine.around_transition {|block| ran_callback = true; block.call }
1246
+
1247
+ @record.save
1248
+ assert ran_callback
1249
+ end
1250
+
1251
+ def test_should_run_around_callbacks_before_yield_once
1252
+ around_before_count = 0
1253
+ @machine.around_transition {|block| around_before_count += 1; block.call }
1254
+
1255
+ @record.save
1256
+ assert_equal 1, around_before_count
1257
+ end
1258
+
1259
+ def test_should_persist_new_state
1260
+ @record.save
1261
+ assert_equal 'idling', @record.state
1262
+ end
1263
+
1264
+ def test_should_run_after_callbacks
1265
+ ran_callback = false
1266
+ @machine.after_transition { ran_callback = true }
1267
+
1268
+ @record.save
1269
+ assert ran_callback
1270
+ end
1271
+
1272
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1273
+ @model.before_create {|record| false}
1274
+
1275
+ ran_callback = false
1276
+ @machine.after_transition { ran_callback = true }
1277
+
1278
+ begin; @record.save; rescue; end
1279
+ assert !ran_callback
1280
+ end
1281
+
1282
+ def test_should_run_failure_callbacks__if_fails
1283
+ @model.before_create {|record| false}
1284
+
1285
+ ran_callback = false
1286
+ @machine.after_failure { ran_callback = true }
1287
+
1288
+ begin; @record.save; rescue; end
1289
+ assert ran_callback
1290
+ end
1291
+
1292
+ def test_should_not_run_around_callbacks_if_fails
1293
+ @model.before_create {|record| false}
1294
+
1295
+ ran_callback = false
1296
+ @machine.around_transition {|block| block.call; ran_callback = true }
1297
+
1298
+ begin; @record.save; rescue; end
1299
+ assert !ran_callback
1300
+ end
1301
+
1302
+ def test_should_run_around_callbacks_after_yield
1303
+ ran_callback = false
1304
+ @machine.around_transition {|block| block.call; ran_callback = true }
1305
+
1306
+ @record.save
1307
+ assert ran_callback
1308
+ end
1309
+
1310
+ def test_should_run_before_transitions_within_transaction
1311
+ @machine.before_transition { @model.create; raise ActiveRecord::Rollback }
1312
+
1313
+ begin
1314
+ @record.save
1315
+ rescue Exception
1316
+ end
1317
+
1318
+ assert_equal 0, @model.count
1319
+ end
1320
+
1321
+ def test_should_run_after_transitions_within_transaction
1322
+ @machine.after_transition { @model.create; raise ActiveRecord::Rollback }
1323
+
1324
+ begin
1325
+ @record.save
1326
+ rescue Exception
1327
+ end
1328
+
1329
+ assert_equal 0, @model.count
1330
+ end
1331
+
1332
+ def test_should_run_around_transition_within_transaction
1333
+ @machine.around_transition { @model.create; raise ActiveRecord::Rollback }
1334
+
1335
+ begin
1336
+ @record.save
1337
+ rescue Exception
1338
+ end
1339
+
1340
+ assert_equal 0, @model.count
1341
+ end
1342
+ end
1343
+
1344
+ class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
1345
+ def setup
1346
+ @model = new_model
1347
+ @machine = StateMachine::Machine.new(@model)
1348
+ @machine.event :ignite do
1349
+ transition :parked => :idling
1350
+ end
1351
+
1352
+ @record = @model.new
1353
+ @record.state = 'parked'
1354
+ @record.state_event = 'ignite'
1355
+ end
1356
+
1357
+ def test_should_fail_if_event_is_invalid
1358
+ @record.state_event = 'invalid'
1359
+ assert_raise(ActiveRecord::RecordInvalid) { @record.save! }
1360
+ end
1361
+
1362
+ def test_should_fail_if_event_has_no_transition
1363
+ @record.state = 'idling'
1364
+ assert_raise(ActiveRecord::RecordInvalid) { @record.save! }
1365
+ end
1366
+
1367
+ def test_should_be_successful_if_event_has_transition
1368
+ assert_equal true, @record.save!
1369
+ end
1370
+
1371
+ def test_should_run_before_callbacks
1372
+ ran_callback = false
1373
+ @machine.before_transition { ran_callback = true }
1374
+
1375
+ @record.save!
1376
+ assert ran_callback
1377
+ end
1378
+
1379
+ def test_should_run_before_callbacks_once
1380
+ before_count = 0
1381
+ @machine.before_transition { before_count += 1 }
1382
+
1383
+ @record.save!
1384
+ assert_equal 1, before_count
1385
+ end
1386
+
1387
+ def test_should_run_around_callbacks_before_yield
1388
+ ran_callback = false
1389
+ @machine.around_transition {|block| ran_callback = true; block.call }
1390
+
1391
+ @record.save!
1392
+ assert ran_callback
1393
+ end
1394
+
1395
+ def test_should_run_around_callbacks_before_yield_once
1396
+ around_before_count = 0
1397
+ @machine.around_transition {|block| around_before_count += 1; block.call }
1398
+
1399
+ @record.save!
1400
+ assert_equal 1, around_before_count
1401
+ end
1402
+
1403
+ def test_should_persist_new_state
1404
+ @record.save!
1405
+ assert_equal 'idling', @record.state
1406
+ end
1407
+
1408
+ def test_should_run_after_callbacks
1409
+ ran_callback = false
1410
+ @machine.after_transition { ran_callback = true }
1411
+
1412
+ @record.save!
1413
+ assert ran_callback
1414
+ end
1415
+
1416
+ def test_should_run_around_callbacks_after_yield
1417
+ ran_callback = false
1418
+ @machine.around_transition {|block| block.call; ran_callback = true }
1419
+
1420
+ @record.save!
1421
+ assert ran_callback
1422
+ end
1423
+ end
1424
+
1425
+ class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1426
+ def setup
1427
+ @superclass = new_model do
1428
+ def persist
1429
+ create_or_update
1430
+ end
1431
+ end
1432
+ @model = Class.new(@superclass)
1433
+ @machine = StateMachine::Machine.new(@model, :action => :persist)
1434
+ @machine.event :ignite do
1435
+ transition :parked => :idling
1436
+ end
1437
+
1438
+ @record = @model.new
1439
+ @record.state = 'parked'
1440
+ @record.state_event = 'ignite'
1441
+ end
1442
+
1443
+ def test_should_not_transition_on_valid?
1444
+ @record.valid?
1445
+ assert_equal 'parked', @record.state
1446
+ end
1447
+
1448
+ def test_should_not_transition_on_save
1449
+ @record.save
1450
+ assert_equal 'parked', @record.state
1451
+ end
1452
+
1453
+ def test_should_not_transition_on_save!
1454
+ @record.save!
1455
+ assert_equal 'parked', @record.state
1456
+ end
1457
+
1458
+ def test_should_transition_on_custom_action
1459
+ @record.persist
1460
+ assert_equal 'idling', @record.state
1461
+ end
1462
+ end
1463
+
1464
+ class MachineWithObserversTest < BaseTestCase
1465
+ def setup
1466
+ @model = new_model
1467
+ @machine = StateMachine::Machine.new(@model)
1468
+ @machine.state :parked, :idling
1469
+ @machine.event :ignite
1470
+ @record = @model.new(:state => 'parked')
1471
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1472
+ end
1473
+
1474
+ def test_should_call_all_transition_callback_permutations
1475
+ callbacks = [
1476
+ :before_ignite_from_parked_to_idling,
1477
+ :before_ignite_from_parked,
1478
+ :before_ignite_to_idling,
1479
+ :before_ignite,
1480
+ :before_transition_state_from_parked_to_idling,
1481
+ :before_transition_state_from_parked,
1482
+ :before_transition_state_to_idling,
1483
+ :before_transition_state,
1484
+ :before_transition
1485
+ ]
1486
+
1487
+ notified = false
1488
+ observer = new_observer(@model) do
1489
+ callbacks.each do |callback|
1490
+ define_method(callback) do |*args|
1491
+ notifications << callback
1492
+ end
1493
+ end
1494
+ end
1495
+
1496
+ instance = observer.instance
1497
+
1498
+ @transition.perform
1499
+ assert_equal callbacks, instance.notifications
1500
+ end
1501
+
1502
+ def test_should_pass_record_and_transition_to_before_callbacks
1503
+ observer = new_observer(@model) do
1504
+ def before_transition(*args)
1505
+ notifications << args
1506
+ end
1507
+ end
1508
+ instance = observer.instance
1509
+
1510
+ @transition.perform
1511
+ assert_equal [[@record, @transition]], instance.notifications
1512
+ end
1513
+
1514
+ def test_should_pass_record_and_transition_to_after_callbacks
1515
+ observer = new_observer(@model) do
1516
+ def after_transition(*args)
1517
+ notifications << args
1518
+ end
1519
+ end
1520
+ instance = observer.instance
1521
+
1522
+ @transition.perform
1523
+ assert_equal [[@record, @transition]], instance.notifications
1524
+ end
1525
+
1526
+ def test_should_call_methods_outside_the_context_of_the_record
1527
+ observer = new_observer(@model) do
1528
+ def before_ignite(*args)
1529
+ notifications << self
1530
+ end
1531
+ end
1532
+ instance = observer.instance
1533
+
1534
+ @transition.perform
1535
+ assert_equal [instance], instance.notifications
1536
+ end
1537
+
1538
+ def test_should_continue_to_handle_non_state_machine_callbacks
1539
+ observer = new_observer(@model) do
1540
+ def before_save(object)
1541
+ notifications << [:before_save, @object]
1542
+ end
1543
+
1544
+ def before_ignite(*args)
1545
+ notifications << :before_ignite
1546
+ end
1547
+ end
1548
+
1549
+ instance = observer.instance
1550
+
1551
+ @transition.perform
1552
+ assert_equal [:before_ignite, [:before_save, @object]], instance.notifications
1553
+ end
1554
+ end
1555
+
1556
+ class MachineWithNamespacedObserversTest < BaseTestCase
1557
+ def setup
1558
+ @model = new_model
1559
+ @machine = StateMachine::Machine.new(@model, :state, :namespace => 'alarm')
1560
+ @machine.state :active, :off
1561
+ @machine.event :enable
1562
+ @record = @model.new(:state => 'off')
1563
+ @transition = StateMachine::Transition.new(@record, @machine, :enable, :off, :active)
1564
+ end
1565
+
1566
+ def test_should_call_namespaced_before_event_method
1567
+ observer = new_observer(@model) do
1568
+ def before_enable_alarm(*args)
1569
+ notifications << args
1570
+ end
1571
+ end
1572
+ instance = observer.instance
1573
+
1574
+ @transition.perform
1575
+ assert_equal [[@record, @transition]], instance.notifications
1576
+ end
1577
+
1578
+ def test_should_call_namespaced_after_event_method
1579
+ observer = new_observer(@model) do
1580
+ def after_enable_alarm(*args)
1581
+ notifications << args
1582
+ end
1583
+ end
1584
+ instance = observer.instance
1585
+
1586
+ @transition.perform
1587
+ assert_equal [[@record, @transition]], instance.notifications
1588
+ end
1589
+ end
1590
+
1591
+ class MachineWithFailureCallbacksTest < BaseTestCase
1592
+ def setup
1593
+ @model = new_model
1594
+ @machine = StateMachine::Machine.new(@model)
1595
+ @machine.state :parked, :idling
1596
+ @machine.event :ignite
1597
+ @record = @model.new(:state => 'parked')
1598
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1599
+
1600
+ @notifications = []
1601
+
1602
+ # Create callbacks
1603
+ @machine.before_transition {false}
1604
+ @machine.after_failure {@notifications << :callback_after_failure}
1605
+
1606
+ # Create observer callbacks
1607
+ observer = new_observer(@model) do
1608
+ def after_failure_to_ignite(*args)
1609
+ notifications << :observer_after_failure_ignite
1610
+ end
1611
+
1612
+ def after_failure_to_transition(*args)
1613
+ notifications << :observer_after_failure_transition
1614
+ end
1615
+ end
1616
+ instance = observer.instance
1617
+ instance.notifications = @notifications
1618
+
1619
+ @transition.perform
1620
+ end
1621
+
1622
+ def test_should_invoke_callbacks_in_specific_order
1623
+ expected = [
1624
+ :callback_after_failure,
1625
+ :observer_after_failure_ignite,
1626
+ :observer_after_failure_transition
1627
+ ]
1628
+
1629
+ assert_equal expected, @notifications
1630
+ end
1631
+ end
1632
+
1633
+ class MachineWithMixedCallbacksTest < BaseTestCase
1634
+ def setup
1635
+ @model = new_model
1636
+ @machine = StateMachine::Machine.new(@model)
1637
+ @machine.state :parked, :idling
1638
+ @machine.event :ignite
1639
+ @record = @model.new(:state => 'parked')
1640
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1641
+
1642
+ @notifications = []
1643
+
1644
+ # Create callbacks
1645
+ @machine.before_transition {@notifications << :callback_before_transition}
1646
+ @machine.after_transition {@notifications << :callback_after_transition}
1647
+ @machine.around_transition do |block|
1648
+ @notifications << :callback_around_before_transition
1649
+ block.call
1650
+ @notifications << :callback_arond_after_transition
1651
+ end
1652
+
1653
+ # Create observer callbacks
1654
+ observer = new_observer(@model) do
1655
+ def before_ignite(*args)
1656
+ notifications << :observer_before_ignite
1657
+ end
1658
+
1659
+ def before_transition(*args)
1660
+ notifications << :observer_before_transition
1661
+ end
1662
+
1663
+ def after_ignite(*args)
1664
+ notifications << :observer_after_ignite
1665
+ end
1666
+
1667
+ def after_transition(*args)
1668
+ notifications << :observer_after_transition
1669
+ end
1670
+ end
1671
+ instance = observer.instance
1672
+ instance.notifications = @notifications
1673
+
1674
+ @transition.perform
1675
+ end
1676
+
1677
+ def test_should_invoke_callbacks_in_specific_order
1678
+ expected = [
1679
+ :callback_before_transition,
1680
+ :callback_around_before_transition,
1681
+ :observer_before_ignite,
1682
+ :observer_before_transition,
1683
+ :callback_arond_after_transition,
1684
+ :callback_after_transition,
1685
+ :observer_after_ignite,
1686
+ :observer_after_transition
1687
+ ]
1688
+
1689
+ assert_equal expected, @notifications
1690
+ end
1691
+ end
1692
+
1693
+ if ActiveRecord.const_defined?(:NamedScope)
1694
+ class MachineWithScopesTest < BaseTestCase
1695
+ def setup
1696
+ @model = new_model
1697
+ @machine = StateMachine::Machine.new(@model)
1698
+ @machine.state :parked, :first_gear
1699
+ @machine.state :idling, :value => lambda {'idling'}
1700
+ end
1701
+
1702
+ def test_should_create_singular_with_scope
1703
+ assert @model.respond_to?(:with_state)
1704
+ end
1705
+
1706
+ def test_should_only_include_records_with_state_in_singular_with_scope
1707
+ parked = @model.create :state => 'parked'
1708
+ idling = @model.create :state => 'idling'
1709
+
1710
+ assert_equal [parked], @model.with_state(:parked).find(:all)
1711
+ end
1712
+
1713
+ def test_should_create_plural_with_scope
1714
+ assert @model.respond_to?(:with_states)
1715
+ end
1716
+
1717
+ def test_should_only_include_records_with_states_in_plural_with_scope
1718
+ parked = @model.create :state => 'parked'
1719
+ idling = @model.create :state => 'idling'
1720
+
1721
+ assert_equal [parked, idling], @model.with_states(:parked, :idling).find(:all)
1722
+ end
1723
+
1724
+ def test_should_create_singular_without_scope
1725
+ assert @model.respond_to?(:without_state)
1726
+ end
1727
+
1728
+ def test_should_only_include_records_without_state_in_singular_without_scope
1729
+ parked = @model.create :state => 'parked'
1730
+ idling = @model.create :state => 'idling'
1731
+
1732
+ assert_equal [parked], @model.without_state(:idling).find(:all)
1733
+ end
1734
+
1735
+ def test_should_create_plural_without_scope
1736
+ assert @model.respond_to?(:without_states)
1737
+ end
1738
+
1739
+ def test_should_only_include_records_without_states_in_plural_without_scope
1740
+ parked = @model.create :state => 'parked'
1741
+ idling = @model.create :state => 'idling'
1742
+ first_gear = @model.create :state => 'first_gear'
1743
+
1744
+ assert_equal [parked, idling], @model.without_states(:first_gear).find(:all)
1745
+ end
1746
+
1747
+ def test_should_allow_chaining_scopes
1748
+ parked = @model.create :state => 'parked'
1749
+ idling = @model.create :state => 'idling'
1750
+
1751
+ assert_equal [idling], @model.without_state(:parked).with_state(:idling).find(:all)
1752
+ end
1753
+ end
1754
+
1755
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1756
+ def setup
1757
+ @model = new_model
1758
+ @machine = StateMachine::Machine.new(@model, :state)
1759
+
1760
+ @subclass = Class.new(@model)
1761
+ @subclass_machine = @subclass.state_machine(:state) {}
1762
+ @subclass_machine.state :parked, :idling, :first_gear
1763
+ end
1764
+
1765
+ def test_should_only_include_records_with_subclass_states_in_with_scope
1766
+ parked = @subclass.create :state => 'parked'
1767
+ idling = @subclass.create :state => 'idling'
1768
+
1769
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling).find(:all)
1770
+ end
1771
+
1772
+ def test_should_only_include_records_without_subclass_states_in_without_scope
1773
+ parked = @subclass.create :state => 'parked'
1774
+ idling = @subclass.create :state => 'idling'
1775
+ first_gear = @subclass.create :state => 'first_gear'
1776
+
1777
+ assert_equal [parked, idling], @subclass.without_states(:first_gear).find(:all)
1778
+ end
1779
+ end
1780
+
1781
+ class MachineWithComplexPluralizationScopesTest < BaseTestCase
1782
+ def setup
1783
+ @model = new_model
1784
+ @machine = StateMachine::Machine.new(@model, :status)
1785
+ end
1786
+
1787
+ def test_should_create_singular_with_scope
1788
+ assert @model.respond_to?(:with_status)
1789
+ end
1790
+
1791
+ def test_should_create_plural_with_scope
1792
+ assert @model.respond_to?(:with_statuses)
1793
+ end
1794
+ end
1795
+
1796
+ class MachineWithScopesAndJoinsTest < BaseTestCase
1797
+ def setup
1798
+ @company = new_model(:company)
1799
+ ActiveRecordTest.const_set('Company', @company)
1800
+
1801
+ @vehicle = new_model(:vehicle) do
1802
+ connection.add_column :vehicle, :company_id, :integer
1803
+ belongs_to :company, :class_name => 'ActiveRecordTest::Company'
1804
+ end
1805
+ ActiveRecordTest.const_set('Vehicle', @vehicle)
1806
+
1807
+ @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1808
+ @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1809
+ @vehicle_machine.state :idling
1810
+
1811
+ @ford = @company.create
1812
+ @mustang = @vehicle.create(:company => @ford)
1813
+ end
1814
+
1815
+ def test_should_find_records_in_with_scope
1816
+ assert_equal [@mustang], @vehicle.with_states(:parked).find(:all, :include => :company, :conditions => 'company.state = "active"')
1817
+ end
1818
+
1819
+ def test_should_find_records_in_without_scope
1820
+ assert_equal [@mustang], @vehicle.without_states(:idling).find(:all, :include => :company, :conditions => 'company.state = "active"')
1821
+ end
1822
+
1823
+ def teardown
1824
+ ActiveRecordTest.class_eval do
1825
+ remove_const('Vehicle')
1826
+ remove_const('Company')
1827
+ end
1828
+ end
1829
+ end
1830
+ else
1831
+ $stderr.puts 'Skipping ActiveRecord Scope tests. `gem install active_record` >= v2.1.0 and try again.'
1832
+ end
1833
+
1834
+ if ActiveRecord.const_defined?(:Relation)
1835
+ class MachineWithDefaultScope < BaseTestCase
1836
+ def setup
1837
+ @model = new_model
1838
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
1839
+ @machine.state :idling
1840
+
1841
+ @model.class_eval do
1842
+ default_scope with_state(:parked, :idling)
1843
+ end
1844
+ end
1845
+
1846
+ def test_should_set_initial_state_on_created_object
1847
+ object = @model.new
1848
+ assert_equal 'parked', object.state
1849
+ end
1850
+ end
1851
+ else
1852
+ $stderr.puts 'Skipping ActiveRecord Default Scope tests. `gem install active_record` >= v3.0.0 and try again.'
1853
+ end
1854
+
1855
+ if Object.const_defined?(:I18n)
1856
+ class MachineWithInternationalizationTest < BaseTestCase
1857
+ def setup
1858
+ I18n.backend = I18n::Backend::Simple.new
1859
+
1860
+ # Initialize the backend
1861
+ StateMachine::Machine.new(new_model)
1862
+ I18n.backend.translate(:en, 'activerecord.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
1863
+
1864
+ @model = new_model
1865
+ end
1866
+
1867
+ def test_should_use_defaults
1868
+ I18n.backend.store_translations(:en, {
1869
+ :activerecord => {:errors => {:messages => {:invalid_transition => "cannot #{interpolation_key('event')}"}}}
1870
+ })
1871
+
1872
+ machine = StateMachine::Machine.new(@model)
1873
+ machine.state :parked, :idling
1874
+ machine.event :ignite
1875
+
1876
+ record = @model.new(:state => 'idling')
1877
+
1878
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1879
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1880
+ end
1881
+
1882
+ def test_should_allow_customized_error_key
1883
+ I18n.backend.store_translations(:en, {
1884
+ :activerecord => {:errors => {:messages => {:bad_transition => "cannot #{interpolation_key('event')}"}}}
1885
+ })
1886
+
1887
+ machine = StateMachine::Machine.new(@model, :messages => {:invalid_transition => :bad_transition})
1888
+ machine.state :parked, :idling
1889
+
1890
+ record = @model.new(:state => 'idling')
1891
+
1892
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1893
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1894
+ end
1895
+
1896
+ def test_should_allow_customized_error_string
1897
+ machine = StateMachine::Machine.new(@model, :messages => {:invalid_transition => "cannot #{interpolation_key('event')}"})
1898
+ machine.state :parked, :idling
1899
+
1900
+ record = @model.new(:state => 'idling')
1901
+
1902
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1903
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1904
+ end
1905
+
1906
+ def test_should_allow_customized_state_key_scoped_to_class_and_machine
1907
+ I18n.backend.store_translations(:en, {
1908
+ :activerecord => {:state_machines => {:'active_record_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
1909
+ })
1910
+
1911
+ machine = StateMachine::Machine.new(@model)
1912
+ machine.state :parked
1913
+
1914
+ assert_equal 'shutdown', machine.state(:parked).human_name
1915
+ end
1916
+
1917
+ def test_should_allow_customized_state_key_scoped_to_machine
1918
+ I18n.backend.store_translations(:en, {
1919
+ :activerecord => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
1920
+ })
1921
+
1922
+ machine = StateMachine::Machine.new(@model)
1923
+ machine.state :parked
1924
+
1925
+ assert_equal 'shutdown', machine.state(:parked).human_name
1926
+ end
1927
+
1928
+ def test_should_allow_customized_state_key_unscoped
1929
+ I18n.backend.store_translations(:en, {
1930
+ :activerecord => {:state_machines => {:states => {:parked => 'shutdown'}}}
1931
+ })
1932
+
1933
+ machine = StateMachine::Machine.new(@model)
1934
+ machine.state :parked
1935
+
1936
+ assert_equal 'shutdown', machine.state(:parked).human_name
1937
+ end
1938
+
1939
+ def test_should_allow_customized_event_key_scoped_to_class_and_machine
1940
+ I18n.backend.store_translations(:en, {
1941
+ :activerecord => {:state_machines => {:'active_record_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
1942
+ })
1943
+
1944
+ machine = StateMachine::Machine.new(@model)
1945
+ machine.event :park
1946
+
1947
+ assert_equal 'stop', machine.event(:park).human_name
1948
+ end
1949
+
1950
+ def test_should_allow_customized_event_key_scoped_to_machine
1951
+ I18n.backend.store_translations(:en, {
1952
+ :activerecord => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
1953
+ })
1954
+
1955
+ machine = StateMachine::Machine.new(@model)
1956
+ machine.event :park
1957
+
1958
+ assert_equal 'stop', machine.event(:park).human_name
1959
+ end
1960
+
1961
+ def test_should_allow_customized_event_key_unscoped
1962
+ I18n.backend.store_translations(:en, {
1963
+ :activerecord => {:state_machines => {:events => {:park => 'stop'}}}
1964
+ })
1965
+
1966
+ machine = StateMachine::Machine.new(@model)
1967
+ machine.event :park
1968
+
1969
+ assert_equal 'stop', machine.event(:park).human_name
1970
+ end
1971
+
1972
+ def test_should_only_add_locale_once_in_load_path
1973
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_record/locale\.rb$}}.length
1974
+
1975
+ # Create another ActiveRecord model that will triger the i18n feature
1976
+ new_model
1977
+
1978
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_record/locale\.rb$}}.length
1979
+ end
1980
+
1981
+ def test_should_add_locale_to_beginning_of_load_path
1982
+ @original_load_path = I18n.load_path
1983
+ I18n.backend = I18n::Backend::Simple.new
1984
+
1985
+ app_locale = File.dirname(__FILE__) + '/../../files/en.yml'
1986
+ default_locale = File.dirname(__FILE__) + '/../../../lib/state_machine/integrations/active_record/locale.rb'
1987
+ I18n.load_path = [app_locale]
1988
+
1989
+ StateMachine::Machine.new(@model)
1990
+
1991
+ assert_equal [default_locale, app_locale].map {|path| File.expand_path(path)}, I18n.load_path.map {|path| File.expand_path(path)}
1992
+ ensure
1993
+ I18n.load_path = @original_load_path
1994
+ end
1995
+
1996
+ def test_should_prefer_other_locales_first
1997
+ @original_load_path = I18n.load_path
1998
+ I18n.backend = I18n::Backend::Simple.new
1999
+ I18n.load_path = [File.dirname(__FILE__) + '/../../files/en.yml']
2000
+
2001
+ machine = StateMachine::Machine.new(@model)
2002
+ machine.state :parked, :idling
2003
+ machine.event :ignite
2004
+
2005
+ record = @model.new(:state => 'idling')
2006
+
2007
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
2008
+ assert_equal ['State cannot transition'], record.errors.full_messages
2009
+ ensure
2010
+ I18n.load_path = @original_load_path
2011
+ end
2012
+
2013
+ private
2014
+ def interpolation_key(key)
2015
+ !defined?(I18n::VERSION) || I18n::VERSION < '0.4.0' ? "{{#{key}}}" : "%{#{key}}"
2016
+ end
2017
+ end
2018
+ else
2019
+ $stderr.puts 'Skipping ActiveRecord I18n tests. `gem install active_record` >= v2.2.0 and try again.'
2020
+ end
2021
+ end