spree-state_machine 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +502 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +20 -0
  8. data/README.md +1246 -0
  9. data/Rakefile +20 -0
  10. data/examples/AutoShop_state.png +0 -0
  11. data/examples/Car_state.png +0 -0
  12. data/examples/Gemfile +5 -0
  13. data/examples/Gemfile.lock +14 -0
  14. data/examples/TrafficLight_state.png +0 -0
  15. data/examples/Vehicle_state.png +0 -0
  16. data/examples/auto_shop.rb +13 -0
  17. data/examples/car.rb +21 -0
  18. data/examples/doc/AutoShop.html +2856 -0
  19. data/examples/doc/AutoShop_state.png +0 -0
  20. data/examples/doc/Car.html +919 -0
  21. data/examples/doc/Car_state.png +0 -0
  22. data/examples/doc/TrafficLight.html +2230 -0
  23. data/examples/doc/TrafficLight_state.png +0 -0
  24. data/examples/doc/Vehicle.html +7921 -0
  25. data/examples/doc/Vehicle_state.png +0 -0
  26. data/examples/doc/_index.html +136 -0
  27. data/examples/doc/class_list.html +47 -0
  28. data/examples/doc/css/common.css +1 -0
  29. data/examples/doc/css/full_list.css +55 -0
  30. data/examples/doc/css/style.css +322 -0
  31. data/examples/doc/file_list.html +46 -0
  32. data/examples/doc/frames.html +13 -0
  33. data/examples/doc/index.html +136 -0
  34. data/examples/doc/js/app.js +205 -0
  35. data/examples/doc/js/full_list.js +173 -0
  36. data/examples/doc/js/jquery.js +16 -0
  37. data/examples/doc/method_list.html +734 -0
  38. data/examples/doc/top-level-namespace.html +105 -0
  39. data/examples/merb-rest/controller.rb +51 -0
  40. data/examples/merb-rest/model.rb +28 -0
  41. data/examples/merb-rest/view_edit.html.erb +24 -0
  42. data/examples/merb-rest/view_index.html.erb +23 -0
  43. data/examples/merb-rest/view_new.html.erb +13 -0
  44. data/examples/merb-rest/view_show.html.erb +17 -0
  45. data/examples/rails-rest/controller.rb +43 -0
  46. data/examples/rails-rest/migration.rb +7 -0
  47. data/examples/rails-rest/model.rb +23 -0
  48. data/examples/rails-rest/view__form.html.erb +34 -0
  49. data/examples/rails-rest/view_edit.html.erb +6 -0
  50. data/examples/rails-rest/view_index.html.erb +25 -0
  51. data/examples/rails-rest/view_new.html.erb +5 -0
  52. data/examples/rails-rest/view_show.html.erb +19 -0
  53. data/examples/traffic_light.rb +9 -0
  54. data/examples/vehicle.rb +33 -0
  55. data/lib/state_machine/assertions.rb +36 -0
  56. data/lib/state_machine/branch.rb +225 -0
  57. data/lib/state_machine/callback.rb +236 -0
  58. data/lib/state_machine/core.rb +7 -0
  59. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  60. data/lib/state_machine/core_ext.rb +2 -0
  61. data/lib/state_machine/error.rb +13 -0
  62. data/lib/state_machine/eval_helpers.rb +87 -0
  63. data/lib/state_machine/event.rb +257 -0
  64. data/lib/state_machine/event_collection.rb +141 -0
  65. data/lib/state_machine/extensions.rb +149 -0
  66. data/lib/state_machine/graph.rb +92 -0
  67. data/lib/state_machine/helper_module.rb +17 -0
  68. data/lib/state_machine/initializers/rails.rb +25 -0
  69. data/lib/state_machine/initializers.rb +4 -0
  70. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  71. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  72. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  73. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  74. data/lib/state_machine/integrations/active_model.rb +585 -0
  75. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  76. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  77. data/lib/state_machine/integrations/active_record.rb +525 -0
  78. data/lib/state_machine/integrations/base.rb +100 -0
  79. data/lib/state_machine/integrations.rb +121 -0
  80. data/lib/state_machine/machine.rb +2287 -0
  81. data/lib/state_machine/machine_collection.rb +74 -0
  82. data/lib/state_machine/macro_methods.rb +522 -0
  83. data/lib/state_machine/matcher.rb +123 -0
  84. data/lib/state_machine/matcher_helpers.rb +54 -0
  85. data/lib/state_machine/node_collection.rb +222 -0
  86. data/lib/state_machine/path.rb +120 -0
  87. data/lib/state_machine/path_collection.rb +90 -0
  88. data/lib/state_machine/state.rb +297 -0
  89. data/lib/state_machine/state_collection.rb +112 -0
  90. data/lib/state_machine/state_context.rb +138 -0
  91. data/lib/state_machine/transition.rb +470 -0
  92. data/lib/state_machine/transition_collection.rb +245 -0
  93. data/lib/state_machine/version.rb +3 -0
  94. data/lib/state_machine/yard/handlers/base.rb +32 -0
  95. data/lib/state_machine/yard/handlers/event.rb +25 -0
  96. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  97. data/lib/state_machine/yard/handlers/state.rb +25 -0
  98. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  99. data/lib/state_machine/yard/handlers.rb +12 -0
  100. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  101. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  102. data/lib/state_machine/yard/templates.rb +3 -0
  103. data/lib/state_machine/yard.rb +8 -0
  104. data/lib/state_machine.rb +8 -0
  105. data/lib/yard-state_machine.rb +2 -0
  106. data/state_machine.gemspec +22 -0
  107. data/test/files/en.yml +17 -0
  108. data/test/files/switch.rb +15 -0
  109. data/test/functional/state_machine_test.rb +1066 -0
  110. data/test/test_helper.rb +7 -0
  111. data/test/unit/assertions_test.rb +40 -0
  112. data/test/unit/branch_test.rb +969 -0
  113. data/test/unit/callback_test.rb +704 -0
  114. data/test/unit/error_test.rb +43 -0
  115. data/test/unit/eval_helpers_test.rb +270 -0
  116. data/test/unit/event_collection_test.rb +398 -0
  117. data/test/unit/event_test.rb +1196 -0
  118. data/test/unit/graph_test.rb +98 -0
  119. data/test/unit/helper_module_test.rb +17 -0
  120. data/test/unit/integrations/active_model_test.rb +1245 -0
  121. data/test/unit/integrations/active_record_test.rb +2551 -0
  122. data/test/unit/integrations/base_test.rb +104 -0
  123. data/test/unit/integrations_test.rb +71 -0
  124. data/test/unit/invalid_event_test.rb +20 -0
  125. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  126. data/test/unit/invalid_transition_test.rb +115 -0
  127. data/test/unit/machine_collection_test.rb +603 -0
  128. data/test/unit/machine_test.rb +3395 -0
  129. data/test/unit/matcher_helpers_test.rb +37 -0
  130. data/test/unit/matcher_test.rb +155 -0
  131. data/test/unit/node_collection_test.rb +362 -0
  132. data/test/unit/path_collection_test.rb +266 -0
  133. data/test/unit/path_test.rb +485 -0
  134. data/test/unit/state_collection_test.rb +352 -0
  135. data/test/unit/state_context_test.rb +441 -0
  136. data/test/unit/state_machine_test.rb +31 -0
  137. data/test/unit/state_test.rb +1101 -0
  138. data/test/unit/transition_collection_test.rb +2168 -0
  139. data/test/unit/transition_test.rb +1558 -0
  140. metadata +264 -0
@@ -0,0 +1,1245 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ require 'active_model'
4
+ if defined?(ActiveModel::VERSION) && ActiveModel::VERSION::MAJOR >= 4
5
+ require 'rails/observers/active_model/active_model'
6
+ require 'active_model/mass_assignment_security'
7
+ else
8
+ require 'active_model/observing'
9
+ end
10
+ require 'active_support/all'
11
+
12
+ module ActiveModelTest
13
+ class BaseTestCase < Test::Unit::TestCase
14
+ def default_test
15
+ end
16
+
17
+ protected
18
+ # Creates a new ActiveModel model (and the associated table)
19
+ def new_model(&block)
20
+ # Simple ActiveModel superclass
21
+ parent = Class.new do
22
+ def self.model_attribute(name)
23
+ define_method(name) { instance_variable_defined?("@#{name}") ? instance_variable_get("@#{name}") : nil }
24
+ define_method("#{name}=") do |value|
25
+ send("#{name}_will_change!") if self.class <= ActiveModel::Dirty && value != send(name)
26
+ instance_variable_set("@#{name}", value)
27
+ end
28
+ end
29
+
30
+ def self.create
31
+ object = new
32
+ object.save
33
+ object
34
+ end
35
+
36
+ def initialize(attrs = {})
37
+ attrs.each {|attr, value| send("#{attr}=", value)}
38
+ @changed_attributes = {}
39
+ end
40
+
41
+ def attributes
42
+ @attributes ||= {}
43
+ end
44
+
45
+ def save
46
+ @changed_attributes = {}
47
+ true
48
+ end
49
+ end
50
+
51
+ model = Class.new(parent) do
52
+ def self.name
53
+ 'ActiveModelTest::Foo'
54
+ end
55
+
56
+ model_attribute :state
57
+ end
58
+ model.class_eval(&block) if block_given?
59
+ model
60
+ end
61
+
62
+ # Creates a new ActiveModel observer
63
+ def new_observer(model, &block)
64
+ observer = Class.new(ActiveModel::Observer) do
65
+ attr_accessor :notifications
66
+
67
+ def initialize
68
+ super
69
+ @notifications = []
70
+ end
71
+ end
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_model, StateMachine::Integrations::ActiveModel.integration_name
81
+ end
82
+
83
+ def test_should_be_available
84
+ assert StateMachine::Integrations::ActiveModel.available?
85
+ end
86
+
87
+ def test_should_match_if_class_includes_observing_feature
88
+ assert StateMachine::Integrations::ActiveModel.matches?(new_model { include ActiveModel::Observing })
89
+ end
90
+
91
+ def test_should_match_if_class_includes_validations_feature
92
+ assert StateMachine::Integrations::ActiveModel.matches?(new_model { include ActiveModel::Validations })
93
+ end
94
+
95
+ def test_should_not_match_if_class_does_not_include_active_model_features
96
+ assert !StateMachine::Integrations::ActiveModel.matches?(new_model)
97
+ end
98
+
99
+ def test_should_have_no_defaults
100
+ assert_equal({}, StateMachine::Integrations::ActiveModel.defaults)
101
+ end
102
+
103
+ def test_should_have_a_locale_path
104
+ assert_not_nil StateMachine::Integrations::ActiveModel.locale_path
105
+ end
106
+ end
107
+
108
+ class MachineByDefaultTest < BaseTestCase
109
+ def setup
110
+ @model = new_model
111
+ @machine = StateMachine::Machine.new(@model, :integration => :active_model)
112
+ end
113
+
114
+ def test_should_not_have_action
115
+ assert_nil @machine.action
116
+ end
117
+
118
+ def test_should_use_transactions
119
+ assert_equal true, @machine.use_transactions
120
+ end
121
+
122
+ def test_should_not_have_any_before_callbacks
123
+ assert_equal 0, @machine.callbacks[:before].size
124
+ end
125
+
126
+ def test_should_not_have_any_after_callbacks
127
+ assert_equal 0, @machine.callbacks[:after].size
128
+ end
129
+ end
130
+
131
+ class MachineWithStatesTest < BaseTestCase
132
+ def setup
133
+ @model = new_model
134
+ @machine = StateMachine::Machine.new(@model)
135
+ @machine.state :first_gear
136
+ end
137
+
138
+ def test_should_humanize_name
139
+ assert_equal 'first gear', @machine.state(:first_gear).human_name
140
+ end
141
+ end
142
+
143
+ class MachineWithStaticInitialStateTest < BaseTestCase
144
+ def setup
145
+ @model = new_model
146
+ @machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
147
+ end
148
+
149
+ def test_should_set_initial_state_on_created_object
150
+ record = @model.new
151
+ assert_equal 'parked', record.state
152
+ end
153
+ end
154
+
155
+ class MachineWithDynamicInitialStateTest < BaseTestCase
156
+ def setup
157
+ @model = new_model
158
+ @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked}, :integration => :active_model)
159
+ @machine.state :parked
160
+ end
161
+
162
+ def test_should_set_initial_state_on_created_object
163
+ record = @model.new
164
+ assert_equal 'parked', record.state
165
+ end
166
+ end
167
+
168
+ class MachineWithEventsTest < BaseTestCase
169
+ def setup
170
+ @model = new_model
171
+ @machine = StateMachine::Machine.new(@model)
172
+ @machine.event :shift_up
173
+ end
174
+
175
+ def test_should_humanize_name
176
+ assert_equal 'shift up', @machine.event(:shift_up).human_name
177
+ end
178
+ end
179
+
180
+ class MachineWithModelStateAttributeTest < BaseTestCase
181
+ def setup
182
+ @model = new_model
183
+ @machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
184
+ @machine.other_states(:idling)
185
+
186
+ @record = @model.new
187
+ end
188
+
189
+ def test_should_have_an_attribute_predicate
190
+ assert @record.respond_to?(:state?)
191
+ end
192
+
193
+ def test_should_raise_exception_for_predicate_without_parameters
194
+ assert_raise(ArgumentError) { @record.state? }
195
+ end
196
+
197
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
198
+ assert !@record.state?(:idling)
199
+ end
200
+
201
+ def test_should_return_true_for_predicate_if_matches_current_value
202
+ assert @record.state?(:parked)
203
+ end
204
+
205
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
206
+ assert_raise(IndexError) { @record.state?(:invalid) }
207
+ end
208
+ end
209
+
210
+ class MachineWithNonModelStateAttributeUndefinedTest < BaseTestCase
211
+ def setup
212
+ @model = new_model do
213
+ def initialize
214
+ end
215
+ end
216
+
217
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked, :integration => :active_model)
218
+ @machine.other_states(:idling)
219
+ @record = @model.new
220
+ end
221
+
222
+ def test_should_not_define_a_reader_attribute_for_the_attribute
223
+ assert !@record.respond_to?(:status)
224
+ end
225
+
226
+ def test_should_not_define_a_writer_attribute_for_the_attribute
227
+ assert !@record.respond_to?(:status=)
228
+ end
229
+
230
+ def test_should_define_an_attribute_predicate
231
+ assert @record.respond_to?(:status?)
232
+ end
233
+ end
234
+
235
+ class MachineWithInitializedStateTest < BaseTestCase
236
+ def setup
237
+ @model = new_model
238
+ @machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
239
+ @machine.state :idling
240
+ end
241
+
242
+ def test_should_allow_nil_initial_state_when_static
243
+ @machine.state nil
244
+
245
+ record = @model.new(:state => nil)
246
+ assert_nil record.state
247
+ end
248
+
249
+ def test_should_allow_nil_initial_state_when_dynamic
250
+ @machine.state nil
251
+
252
+ @machine.initial_state = lambda {:parked}
253
+ record = @model.new(:state => nil)
254
+ assert_nil record.state
255
+ end
256
+
257
+ def test_should_allow_different_initial_state_when_static
258
+ record = @model.new(:state => 'idling')
259
+ assert_equal 'idling', record.state
260
+ end
261
+
262
+ def test_should_allow_different_initial_state_when_dynamic
263
+ @machine.initial_state = lambda {:parked}
264
+ record = @model.new(:state => 'idling')
265
+ assert_equal 'idling', record.state
266
+ end
267
+
268
+ def test_should_use_default_state_if_protected
269
+ @model.class_eval do
270
+ include ActiveModel::MassAssignmentSecurity
271
+ attr_protected :state
272
+
273
+ def initialize(attrs = {})
274
+ initialize_state_machines do
275
+ sanitize_for_mass_assignment(attrs).each {|attr, value| send("#{attr}=", value)} if attrs
276
+ @changed_attributes = {}
277
+ end
278
+ end
279
+ end
280
+
281
+ record = @model.new(:state => 'idling')
282
+ assert_equal 'parked', record.state
283
+
284
+ record = @model.new(nil)
285
+ assert_equal 'parked', record.state
286
+ end
287
+ end
288
+
289
+ class MachineMultipleTest < BaseTestCase
290
+ def setup
291
+ @model = new_model do
292
+ model_attribute :status
293
+ end
294
+
295
+ @state_machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
296
+ @status_machine = StateMachine::Machine.new(@model, :status, :initial => :idling, :integration => :active_model)
297
+ end
298
+
299
+ def test_should_should_initialize_each_state
300
+ record = @model.new
301
+ assert_equal 'parked', record.state
302
+ assert_equal 'idling', record.status
303
+ end
304
+ end
305
+
306
+ class MachineWithDirtyAttributesTest < BaseTestCase
307
+ def setup
308
+ @model = new_model do
309
+ include ActiveModel::Dirty
310
+ define_attribute_methods [:state]
311
+ end
312
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
313
+ @machine.event :ignite
314
+ @machine.state :idling
315
+
316
+ @record = @model.create
317
+
318
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
319
+ @transition.perform
320
+ end
321
+
322
+ def test_should_include_state_in_changed_attributes
323
+ assert_equal %w(state), @record.changed
324
+ end
325
+
326
+ def test_should_track_attribute_change
327
+ assert_equal %w(parked idling), @record.changes['state']
328
+ end
329
+
330
+ def test_should_not_reset_changes_on_multiple_transitions
331
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
332
+ transition.perform
333
+
334
+ assert_equal %w(parked idling), @record.changes['state']
335
+ end
336
+ end
337
+
338
+ class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
339
+ def setup
340
+ @model = new_model do
341
+ include ActiveModel::Dirty
342
+ define_attribute_methods [:state]
343
+ end
344
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
345
+ @machine.event :park
346
+
347
+ @record = @model.create
348
+
349
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
350
+ @transition.perform
351
+ end
352
+
353
+ def test_should_not_include_state_in_changed_attributes
354
+ assert_equal [], @record.changed
355
+ end
356
+
357
+ def test_should_not_track_attribute_changes
358
+ assert_equal nil, @record.changes['state']
359
+ end
360
+ end
361
+
362
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
363
+ def setup
364
+ @model = new_model do
365
+ include ActiveModel::Dirty
366
+ model_attribute :status
367
+ define_attribute_methods [:status]
368
+ end
369
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
370
+ @machine.event :ignite
371
+ @machine.state :idling
372
+
373
+ @record = @model.create
374
+
375
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
376
+ @transition.perform
377
+ end
378
+
379
+ def test_should_include_state_in_changed_attributes
380
+ assert_equal %w(status), @record.changed
381
+ end
382
+
383
+ def test_should_track_attribute_change
384
+ assert_equal %w(parked idling), @record.changes['status']
385
+ end
386
+
387
+ def test_should_not_reset_changes_on_multiple_transitions
388
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
389
+ transition.perform
390
+
391
+ assert_equal %w(parked idling), @record.changes['status']
392
+ end
393
+ end
394
+
395
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
396
+ def setup
397
+ @model = new_model do
398
+ include ActiveModel::Dirty
399
+ model_attribute :status
400
+ define_attribute_methods [:status]
401
+ end
402
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
403
+ @machine.event :park
404
+
405
+ @record = @model.create
406
+
407
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
408
+ @transition.perform
409
+ end
410
+
411
+ def test_should_not_include_state_in_changed_attributes
412
+ assert_equal [], @record.changed
413
+ end
414
+
415
+ def test_should_not_track_attribute_changes
416
+ assert_equal nil, @record.changes['status']
417
+ end
418
+ end
419
+
420
+ class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
421
+ def setup
422
+ @model = new_model do
423
+ include ActiveModel::Dirty
424
+ define_attribute_methods [:state]
425
+ end
426
+ @machine = StateMachine::Machine.new(@model, :action => :save, :initial => :parked)
427
+ @machine.event :ignite
428
+
429
+ @record = @model.create
430
+ @record.state_event = 'ignite'
431
+ end
432
+
433
+ def test_should_not_include_state_in_changed_attributes
434
+ assert_equal [], @record.changed
435
+ end
436
+
437
+ def test_should_not_track_attribute_change
438
+ assert_equal nil, @record.changes['state']
439
+ end
440
+ end
441
+
442
+ class MachineWithCallbacksTest < BaseTestCase
443
+ def setup
444
+ @model = new_model
445
+ @machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
446
+ @machine.other_states :idling
447
+ @machine.event :ignite
448
+
449
+ @record = @model.new(:state => 'parked')
450
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
451
+ end
452
+
453
+ def test_should_run_before_callbacks
454
+ called = false
455
+ @machine.before_transition {called = true}
456
+
457
+ @transition.perform
458
+ assert called
459
+ end
460
+
461
+ def test_should_pass_record_to_before_callbacks_with_one_argument
462
+ record = nil
463
+ @machine.before_transition {|arg| record = arg}
464
+
465
+ @transition.perform
466
+ assert_equal @record, record
467
+ end
468
+
469
+ def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
470
+ callback_args = nil
471
+ @machine.before_transition {|*args| callback_args = args}
472
+
473
+ @transition.perform
474
+ assert_equal [@record, @transition], callback_args
475
+ end
476
+
477
+ def test_should_run_before_callbacks_outside_the_context_of_the_record
478
+ context = nil
479
+ @machine.before_transition {context = self}
480
+
481
+ @transition.perform
482
+ assert_equal self, context
483
+ end
484
+
485
+ def test_should_run_after_callbacks
486
+ called = false
487
+ @machine.after_transition {called = true}
488
+
489
+ @transition.perform
490
+ assert called
491
+ end
492
+
493
+ def test_should_pass_record_to_after_callbacks_with_one_argument
494
+ record = nil
495
+ @machine.after_transition {|arg| record = arg}
496
+
497
+ @transition.perform
498
+ assert_equal @record, record
499
+ end
500
+
501
+ def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
502
+ callback_args = nil
503
+ @machine.after_transition {|*args| callback_args = args}
504
+
505
+ @transition.perform
506
+ assert_equal [@record, @transition], callback_args
507
+ end
508
+
509
+ def test_should_run_after_callbacks_outside_the_context_of_the_record
510
+ context = nil
511
+ @machine.after_transition {context = self}
512
+
513
+ @transition.perform
514
+ assert_equal self, context
515
+ end
516
+
517
+ def test_should_run_around_callbacks
518
+ before_called = false
519
+ after_called = false
520
+ ensure_called = 0
521
+ @machine.around_transition do |block|
522
+ before_called = true
523
+ begin
524
+ block.call
525
+ ensure
526
+ ensure_called += 1
527
+ end
528
+ after_called = true
529
+ end
530
+
531
+ @transition.perform
532
+ assert before_called
533
+ assert after_called
534
+ assert_equal ensure_called, 1
535
+ end
536
+
537
+ def test_should_include_transition_states_in_known_states
538
+ @machine.before_transition :to => :first_gear, :do => lambda {}
539
+
540
+ assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
541
+ end
542
+
543
+ def test_should_allow_symbolic_callbacks
544
+ callback_args = nil
545
+
546
+ klass = class << @record; self; end
547
+ klass.send(:define_method, :after_ignite) do |*args|
548
+ callback_args = args
549
+ end
550
+
551
+ @machine.before_transition(:after_ignite)
552
+
553
+ @transition.perform
554
+ assert_equal [@transition], callback_args
555
+ end
556
+
557
+ def test_should_allow_string_callbacks
558
+ class << @record
559
+ attr_reader :callback_result
560
+ end
561
+
562
+ @machine.before_transition('@callback_result = [1, 2, 3]')
563
+ @transition.perform
564
+
565
+ assert_equal [1, 2, 3], @record.callback_result
566
+ end
567
+ end
568
+
569
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
570
+ def setup
571
+ @callbacks = []
572
+
573
+ @model = new_model
574
+ @machine = StateMachine::Machine.new(@model, :integration => :active_model)
575
+ @machine.state :parked, :idling
576
+ @machine.event :ignite
577
+ @machine.before_transition {@callbacks << :before_1; false}
578
+ @machine.before_transition {@callbacks << :before_2}
579
+ @machine.after_transition {@callbacks << :after}
580
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
581
+
582
+ @record = @model.new(:state => 'parked')
583
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
584
+ @result = @transition.perform
585
+ end
586
+
587
+ def test_should_not_be_successful
588
+ assert !@result
589
+ end
590
+
591
+ def test_should_not_change_current_state
592
+ assert_equal 'parked', @record.state
593
+ end
594
+
595
+ def test_should_not_run_further_callbacks
596
+ assert_equal [:before_1], @callbacks
597
+ end
598
+ end
599
+
600
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
601
+ def setup
602
+ @callbacks = []
603
+
604
+ @model = new_model
605
+ @machine = StateMachine::Machine.new(@model, :integration => :active_model)
606
+ @machine.state :parked, :idling
607
+ @machine.event :ignite
608
+ @machine.after_transition {@callbacks << :after_1; false}
609
+ @machine.after_transition {@callbacks << :after_2}
610
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
611
+
612
+ @record = @model.new(:state => 'parked')
613
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
614
+ @result = @transition.perform
615
+ end
616
+
617
+ def test_should_be_successful
618
+ assert @result
619
+ end
620
+
621
+ def test_should_change_current_state
622
+ assert_equal 'idling', @record.state
623
+ end
624
+
625
+ def test_should_not_run_further_after_callbacks
626
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
627
+ end
628
+ end
629
+
630
+ class MachineWithValidationsTest < BaseTestCase
631
+ def setup
632
+ @model = new_model { include ActiveModel::Validations }
633
+ @machine = StateMachine::Machine.new(@model, :action => :save)
634
+ @machine.state :parked
635
+
636
+ @record = @model.new
637
+ end
638
+
639
+ def test_should_invalidate_using_errors
640
+ I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
641
+ @record.state = 'parked'
642
+
643
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
644
+ assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
645
+ end
646
+
647
+ def test_should_auto_prefix_custom_attributes_on_invalidation
648
+ @machine.invalidate(@record, :event, :invalid)
649
+
650
+ assert_equal ['State event is invalid'], @record.errors.full_messages
651
+ end
652
+
653
+ def test_should_clear_errors_on_reset
654
+ @record.state = 'parked'
655
+ @record.errors.add(:state, 'is invalid')
656
+
657
+ @machine.reset(@record)
658
+ assert_equal [], @record.errors.full_messages
659
+ end
660
+
661
+ def test_should_be_valid_if_state_is_known
662
+ @record.state = 'parked'
663
+
664
+ assert @record.valid?
665
+ end
666
+
667
+ def test_should_not_be_valid_if_state_is_unknown
668
+ @record.state = 'invalid'
669
+
670
+ assert !@record.valid?
671
+ assert_equal ['State is invalid'], @record.errors.full_messages
672
+ end
673
+ end
674
+
675
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
676
+ def setup
677
+ @model = new_model { include ActiveModel::Validations }
678
+
679
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
680
+ @machine.state :parked
681
+
682
+ @record = @model.new
683
+ end
684
+
685
+ def test_should_add_validation_errors_to_custom_attribute
686
+ @record.state = 'invalid'
687
+
688
+ assert !@record.valid?
689
+ assert_equal ['State is invalid'], @record.errors.full_messages
690
+
691
+ @record.state = 'parked'
692
+ assert @record.valid?
693
+ end
694
+ end
695
+
696
+ class MachineErrorsTest < BaseTestCase
697
+ def setup
698
+ @model = new_model { include ActiveModel::Validations }
699
+ @machine = StateMachine::Machine.new(@model)
700
+ @record = @model.new
701
+ end
702
+
703
+ def test_should_be_able_to_describe_current_errors
704
+ @record.errors.add(:id, 'cannot be blank')
705
+ @record.errors.add(:state, 'is invalid')
706
+ assert_equal ['Id cannot be blank', 'State is invalid'], @machine.errors_for(@record).split(', ').sort
707
+ end
708
+
709
+ def test_should_describe_as_halted_with_no_errors
710
+ assert_equal 'Transition halted', @machine.errors_for(@record)
711
+ end
712
+ end
713
+
714
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
715
+ def setup
716
+ @model = new_model do
717
+ include ActiveModel::Validations
718
+ attr_accessor :seatbelt
719
+ end
720
+
721
+ @machine = StateMachine::Machine.new(@model)
722
+ @machine.state :first_gear, :second_gear do
723
+ validates_presence_of :seatbelt
724
+ end
725
+ @machine.other_states :parked
726
+ end
727
+
728
+ def test_should_be_valid_if_validation_fails_outside_state_scope
729
+ record = @model.new(:state => 'parked', :seatbelt => nil)
730
+ assert record.valid?
731
+ end
732
+
733
+ def test_should_be_invalid_if_validation_fails_within_state_scope
734
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
735
+ assert !record.valid?
736
+ end
737
+
738
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
739
+ record = @model.new(:state => 'second_gear', :seatbelt => true)
740
+ assert record.valid?
741
+ end
742
+ end
743
+
744
+ class ObserverUpdateTest < BaseTestCase
745
+ def setup
746
+ @model = new_model { include ActiveModel::Observing }
747
+ @machine = StateMachine::Machine.new(@model)
748
+ @machine.state :parked, :idling
749
+ @machine.event :ignite
750
+
751
+ @record = @model.new(:state => 'parked')
752
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
753
+
754
+ @observer_update = StateMachine::Integrations::ActiveModel::ObserverUpdate.new(:before_transition, @record, @transition)
755
+ end
756
+
757
+ def test_should_have_method
758
+ assert_equal :before_transition, @observer_update.method
759
+ end
760
+
761
+ def test_should_have_object
762
+ assert_equal @record, @observer_update.object
763
+ end
764
+
765
+ def test_should_have_transition
766
+ assert_equal @transition, @observer_update.transition
767
+ end
768
+
769
+ def test_should_include_object_and_transition_in_args
770
+ assert_equal [@record, @transition], @observer_update.args
771
+ end
772
+
773
+ def test_should_use_record_class_as_class
774
+ assert_equal @model, @observer_update.class
775
+ end
776
+ end
777
+
778
+ class MachineWithObserversTest < BaseTestCase
779
+ def setup
780
+ @model = new_model { include ActiveModel::Observing }
781
+ @machine = StateMachine::Machine.new(@model)
782
+ @machine.state :parked, :idling
783
+ @machine.event :ignite
784
+ @record = @model.new(:state => 'parked')
785
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
786
+ end
787
+
788
+ def test_should_call_all_transition_callback_permutations
789
+ callbacks = [
790
+ :before_ignite_from_parked_to_idling,
791
+ :before_ignite_from_parked,
792
+ :before_ignite_to_idling,
793
+ :before_ignite,
794
+ :before_transition_state_from_parked_to_idling,
795
+ :before_transition_state_from_parked,
796
+ :before_transition_state_to_idling,
797
+ :before_transition_state,
798
+ :before_transition
799
+ ]
800
+
801
+ observer = new_observer(@model) do
802
+ callbacks.each do |callback|
803
+ define_method(callback) do |*args|
804
+ notifications << callback
805
+ end
806
+ end
807
+ end
808
+
809
+ instance = observer.instance
810
+
811
+ @transition.perform
812
+ assert_equal callbacks, instance.notifications
813
+ end
814
+
815
+ def test_should_call_no_transition_callbacks_when_observers_disabled
816
+ return unless ActiveModel::VERSION::MAJOR >= 3 && ActiveModel::VERSION::MINOR >= 1
817
+
818
+ callbacks = [
819
+ :before_ignite,
820
+ :before_transition
821
+ ]
822
+
823
+ observer = new_observer(@model) do
824
+ callbacks.each do |callback|
825
+ define_method(callback) do |*args|
826
+ notifications << callback
827
+ end
828
+ end
829
+ end
830
+
831
+ instance = observer.instance
832
+
833
+ @model.observers.disable(observer) do
834
+ @transition.perform
835
+ end
836
+
837
+ assert_equal [], instance.notifications
838
+ end
839
+
840
+ def test_should_pass_record_and_transition_to_before_callbacks
841
+ observer = new_observer(@model) do
842
+ def before_transition(*args)
843
+ notifications << args
844
+ end
845
+ end
846
+ instance = observer.instance
847
+
848
+ @transition.perform
849
+ assert_equal [[@record, @transition]], instance.notifications
850
+ end
851
+
852
+ def test_should_pass_record_and_transition_to_after_callbacks
853
+ observer = new_observer(@model) do
854
+ def after_transition(*args)
855
+ notifications << args
856
+ end
857
+ end
858
+ instance = observer.instance
859
+
860
+ @transition.perform
861
+ assert_equal [[@record, @transition]], instance.notifications
862
+ end
863
+
864
+ def test_should_call_methods_outside_the_context_of_the_record
865
+ observer = new_observer(@model) do
866
+ def before_ignite(*args)
867
+ notifications << self
868
+ end
869
+ end
870
+ instance = observer.instance
871
+
872
+ @transition.perform
873
+ assert_equal [instance], instance.notifications
874
+ end
875
+
876
+ def test_should_support_nil_from_states
877
+ callbacks = [
878
+ :before_ignite_from_nil_to_idling,
879
+ :before_ignite_from_nil,
880
+ :before_transition_state_from_nil_to_idling,
881
+ :before_transition_state_from_nil
882
+ ]
883
+
884
+ observer = new_observer(@model) do
885
+ callbacks.each do |callback|
886
+ define_method(callback) do |*args|
887
+ notifications << callback
888
+ end
889
+ end
890
+ end
891
+
892
+ instance = observer.instance
893
+
894
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, nil, :idling)
895
+ transition.perform
896
+ assert_equal callbacks, instance.notifications
897
+ end
898
+
899
+ def test_should_support_nil_to_states
900
+ callbacks = [
901
+ :before_ignite_from_parked_to_nil,
902
+ :before_ignite_to_nil,
903
+ :before_transition_state_from_parked_to_nil,
904
+ :before_transition_state_to_nil
905
+ ]
906
+
907
+ observer = new_observer(@model) do
908
+ callbacks.each do |callback|
909
+ define_method(callback) do |*args|
910
+ notifications << callback
911
+ end
912
+ end
913
+ end
914
+
915
+ instance = observer.instance
916
+
917
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, nil)
918
+ transition.perform
919
+ assert_equal callbacks, instance.notifications
920
+ end
921
+ end
922
+
923
+ class MachineWithNamespacedObserversTest < BaseTestCase
924
+ def setup
925
+ @model = new_model { include ActiveModel::Observing }
926
+ @machine = StateMachine::Machine.new(@model, :state, :namespace => 'alarm')
927
+ @machine.state :active, :off
928
+ @machine.event :enable
929
+ @record = @model.new(:state => 'off')
930
+ @transition = StateMachine::Transition.new(@record, @machine, :enable, :off, :active)
931
+ end
932
+
933
+ def test_should_call_namespaced_before_event_method
934
+ observer = new_observer(@model) do
935
+ def before_enable_alarm(*args)
936
+ notifications << args
937
+ end
938
+ end
939
+ instance = observer.instance
940
+
941
+ @transition.perform
942
+ assert_equal [[@record, @transition]], instance.notifications
943
+ end
944
+
945
+ def test_should_call_namespaced_after_event_method
946
+ observer = new_observer(@model) do
947
+ def after_enable_alarm(*args)
948
+ notifications << args
949
+ end
950
+ end
951
+ instance = observer.instance
952
+
953
+ @transition.perform
954
+ assert_equal [[@record, @transition]], instance.notifications
955
+ end
956
+ end
957
+
958
+ class MachineWithFailureCallbacksTest < BaseTestCase
959
+ def setup
960
+ @model = new_model { include ActiveModel::Observing }
961
+ @machine = StateMachine::Machine.new(@model)
962
+ @machine.state :parked, :idling
963
+ @machine.event :ignite
964
+ @record = @model.new(:state => 'parked')
965
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
966
+
967
+ @notifications = []
968
+
969
+ # Create callbacks
970
+ @machine.before_transition {false}
971
+ @machine.after_failure {@notifications << :callback_after_failure}
972
+
973
+ # Create observer callbacks
974
+ observer = new_observer(@model) do
975
+ def after_failure_to_ignite(*args)
976
+ notifications << :observer_after_failure_ignite
977
+ end
978
+
979
+ def after_failure_to_transition(*args)
980
+ notifications << :observer_after_failure_transition
981
+ end
982
+ end
983
+ instance = observer.instance
984
+ instance.notifications = @notifications
985
+
986
+ @transition.perform
987
+ end
988
+
989
+ def test_should_invoke_callbacks_in_specific_order
990
+ expected = [
991
+ :callback_after_failure,
992
+ :observer_after_failure_ignite,
993
+ :observer_after_failure_transition
994
+ ]
995
+
996
+ assert_equal expected, @notifications
997
+ end
998
+ end
999
+
1000
+ class MachineWithMixedCallbacksTest < BaseTestCase
1001
+ def setup
1002
+ @model = new_model { include ActiveModel::Observing }
1003
+ @machine = StateMachine::Machine.new(@model)
1004
+ @machine.state :parked, :idling
1005
+ @machine.event :ignite
1006
+ @record = @model.new(:state => 'parked')
1007
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1008
+
1009
+ @notifications = []
1010
+
1011
+ # Create callbacks
1012
+ @machine.before_transition {@notifications << :callback_before_transition}
1013
+ @machine.after_transition {@notifications << :callback_after_transition}
1014
+ @machine.around_transition {|block| @notifications << :callback_around_before_transition; block.call; @notifications << :callback_around_after_transition}
1015
+
1016
+ # Create observer callbacks
1017
+ observer = new_observer(@model) do
1018
+ def before_ignite(*args)
1019
+ notifications << :observer_before_ignite
1020
+ end
1021
+
1022
+ def before_transition(*args)
1023
+ notifications << :observer_before_transition
1024
+ end
1025
+
1026
+ def after_ignite(*args)
1027
+ notifications << :observer_after_ignite
1028
+ end
1029
+
1030
+ def after_transition(*args)
1031
+ notifications << :observer_after_transition
1032
+ end
1033
+ end
1034
+ instance = observer.instance
1035
+ instance.notifications = @notifications
1036
+
1037
+ @transition.perform
1038
+ end
1039
+
1040
+ def test_should_invoke_callbacks_in_specific_order
1041
+ expected = [
1042
+ :callback_before_transition,
1043
+ :callback_around_before_transition,
1044
+ :observer_before_ignite,
1045
+ :observer_before_transition,
1046
+ :callback_around_after_transition,
1047
+ :callback_after_transition,
1048
+ :observer_after_ignite,
1049
+ :observer_after_transition
1050
+ ]
1051
+
1052
+ assert_equal expected, @notifications
1053
+ end
1054
+ end
1055
+
1056
+ class MachineWithInternationalizationTest < BaseTestCase
1057
+ def setup
1058
+ I18n.backend = I18n::Backend::Simple.new
1059
+
1060
+ # Initialize the backend
1061
+ I18n.backend.translate(:en, 'activemodel.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
1062
+
1063
+ @model = new_model { include ActiveModel::Validations }
1064
+ end
1065
+
1066
+ def test_should_use_defaults
1067
+ I18n.backend.store_translations(:en, {
1068
+ :activemodel => {:errors => {:messages => {:invalid_transition => 'cannot %{event}'}}}
1069
+ })
1070
+
1071
+ machine = StateMachine::Machine.new(@model, :action => :save)
1072
+ machine.state :parked, :idling
1073
+ machine.event :ignite
1074
+
1075
+ record = @model.new(:state => 'idling')
1076
+
1077
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1078
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1079
+ end
1080
+
1081
+ def test_should_allow_customized_error_key
1082
+ I18n.backend.store_translations(:en, {
1083
+ :activemodel => {:errors => {:messages => {:bad_transition => 'cannot %{event}'}}}
1084
+ })
1085
+
1086
+ machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => :bad_transition})
1087
+ machine.state :parked, :idling
1088
+
1089
+ record = @model.new
1090
+ record.state = 'idling'
1091
+
1092
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1093
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1094
+ end
1095
+
1096
+ def test_should_allow_customized_error_string
1097
+ machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => 'cannot %{event}'})
1098
+ machine.state :parked, :idling
1099
+
1100
+ record = @model.new(:state => 'idling')
1101
+
1102
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1103
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1104
+ end
1105
+
1106
+ def test_should_allow_customized_state_key_scoped_to_class_and_machine
1107
+ I18n.backend.store_translations(:en, {
1108
+ :activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
1109
+ })
1110
+
1111
+ machine = StateMachine::Machine.new(@model)
1112
+ machine.state :parked
1113
+
1114
+ assert_equal 'shutdown', machine.state(:parked).human_name
1115
+ end
1116
+
1117
+ def test_should_allow_customized_state_key_scoped_to_class
1118
+ I18n.backend.store_translations(:en, {
1119
+ :activemodel => {:state_machines => {:'active_model_test/foo' => {:states => {:parked => 'shutdown'}}}}
1120
+ })
1121
+
1122
+ machine = StateMachine::Machine.new(@model)
1123
+ machine.state :parked
1124
+
1125
+ assert_equal 'shutdown', machine.state(:parked).human_name
1126
+ end
1127
+
1128
+ def test_should_allow_customized_state_key_scoped_to_machine
1129
+ I18n.backend.store_translations(:en, {
1130
+ :activemodel => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
1131
+ })
1132
+
1133
+ machine = StateMachine::Machine.new(@model)
1134
+ machine.state :parked
1135
+
1136
+ assert_equal 'shutdown', machine.state(:parked).human_name
1137
+ end
1138
+
1139
+ def test_should_allow_customized_state_key_unscoped
1140
+ I18n.backend.store_translations(:en, {
1141
+ :activemodel => {:state_machines => {:states => {:parked => 'shutdown'}}}
1142
+ })
1143
+
1144
+ machine = StateMachine::Machine.new(@model)
1145
+ machine.state :parked
1146
+
1147
+ assert_equal 'shutdown', machine.state(:parked).human_name
1148
+ end
1149
+
1150
+ def test_should_support_nil_state_key
1151
+ I18n.backend.store_translations(:en, {
1152
+ :activemodel => {:state_machines => {:states => {:nil => 'empty'}}}
1153
+ })
1154
+
1155
+ machine = StateMachine::Machine.new(@model)
1156
+
1157
+ assert_equal 'empty', machine.state(nil).human_name
1158
+ end
1159
+
1160
+ def test_should_allow_customized_event_key_scoped_to_class_and_machine
1161
+ I18n.backend.store_translations(:en, {
1162
+ :activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
1163
+ })
1164
+
1165
+ machine = StateMachine::Machine.new(@model)
1166
+ machine.event :park
1167
+
1168
+ assert_equal 'stop', machine.event(:park).human_name
1169
+ end
1170
+
1171
+ def test_should_allow_customized_event_key_scoped_to_class
1172
+ I18n.backend.store_translations(:en, {
1173
+ :activemodel => {:state_machines => {:'active_model_test/foo' => {:events => {:park => 'stop'}}}}
1174
+ })
1175
+
1176
+ machine = StateMachine::Machine.new(@model)
1177
+ machine.event :park
1178
+
1179
+ assert_equal 'stop', machine.event(:park).human_name
1180
+ end
1181
+
1182
+ def test_should_allow_customized_event_key_scoped_to_machine
1183
+ I18n.backend.store_translations(:en, {
1184
+ :activemodel => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
1185
+ })
1186
+
1187
+ machine = StateMachine::Machine.new(@model)
1188
+ machine.event :park
1189
+
1190
+ assert_equal 'stop', machine.event(:park).human_name
1191
+ end
1192
+
1193
+ def test_should_allow_customized_event_key_unscoped
1194
+ I18n.backend.store_translations(:en, {
1195
+ :activemodel => {:state_machines => {:events => {:park => 'stop'}}}
1196
+ })
1197
+
1198
+ machine = StateMachine::Machine.new(@model)
1199
+ machine.event :park
1200
+
1201
+ assert_equal 'stop', machine.event(:park).human_name
1202
+ end
1203
+
1204
+ def test_should_only_add_locale_once_in_load_path
1205
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_model/locale\.rb$}}.length
1206
+
1207
+ # Create another ActiveModel model that will triger the i18n feature
1208
+ new_model
1209
+
1210
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_model/locale\.rb$}}.length
1211
+ end
1212
+
1213
+ def test_should_add_locale_to_beginning_of_load_path
1214
+ @original_load_path = I18n.load_path
1215
+ I18n.backend = I18n::Backend::Simple.new
1216
+
1217
+ app_locale = File.dirname(__FILE__) + '/../../files/en.yml'
1218
+ default_locale = File.dirname(__FILE__) + '/../../../lib/state_machine/integrations/active_model/locale.rb'
1219
+ I18n.load_path = [app_locale]
1220
+
1221
+ StateMachine::Machine.new(@model)
1222
+
1223
+ assert_equal [default_locale, app_locale].map {|path| File.expand_path(path)}, I18n.load_path.map {|path| File.expand_path(path)}
1224
+ ensure
1225
+ I18n.load_path = @original_load_path
1226
+ end
1227
+
1228
+ def test_should_prefer_other_locales_first
1229
+ @original_load_path = I18n.load_path
1230
+ I18n.backend = I18n::Backend::Simple.new
1231
+ I18n.load_path = [File.dirname(__FILE__) + '/../../files/en.yml']
1232
+
1233
+ machine = StateMachine::Machine.new(@model)
1234
+ machine.state :parked, :idling
1235
+ machine.event :ignite
1236
+
1237
+ record = @model.new(:state => 'idling')
1238
+
1239
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
1240
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1241
+ ensure
1242
+ I18n.load_path = @original_load_path
1243
+ end
1244
+ end
1245
+ end