state_machines-activemodel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/cssxfire.xml +9 -0
  5. data/.idea/encodings.xml +5 -0
  6. data/.idea/misc.xml +5 -0
  7. data/.idea/modules.xml +9 -0
  8. data/.idea/scopes/scope_settings.xml +5 -0
  9. data/.idea/state_machine2_activemodel.iml +32 -0
  10. data/.idea/vcs.xml +7 -0
  11. data/.idea/workspace.xml +50 -0
  12. data/.rspec +3 -0
  13. data/.travis.yml +19 -0
  14. data/Appraisals +34 -0
  15. data/Gemfile +4 -0
  16. data/LICENSE.txt +23 -0
  17. data/README.md +86 -0
  18. data/Rakefile +10 -0
  19. data/gemfiles/active_model_3.2.gemfile +7 -0
  20. data/gemfiles/active_model_3.2.gemfile.lock +50 -0
  21. data/gemfiles/active_model_4.0.gemfile +7 -0
  22. data/gemfiles/active_model_4.0.gemfile.lock +56 -0
  23. data/gemfiles/active_model_4.0_obs.gemfile +8 -0
  24. data/gemfiles/active_model_4.0_obs.gemfile.lock +59 -0
  25. data/gemfiles/active_model_4.1.gemfile +7 -0
  26. data/gemfiles/active_model_4.1.gemfile.lock +57 -0
  27. data/gemfiles/active_model_4.1_obs.gemfile +8 -0
  28. data/gemfiles/active_model_4.1_obs.gemfile.lock +60 -0
  29. data/gemfiles/active_model_edge.gemfile +7 -0
  30. data/gemfiles/active_model_edge.gemfile.lock +62 -0
  31. data/gemfiles/active_model_edge_obs.gemfile +8 -0
  32. data/gemfiles/active_model_edge_obs.gemfile.lock +65 -0
  33. data/lib/state_machines-activemodel.rb +1 -0
  34. data/lib/state_machines/integrations/active_model.rb +593 -0
  35. data/lib/state_machines/integrations/active_model/locale.rb +11 -0
  36. data/lib/state_machines/integrations/active_model/observer.rb +33 -0
  37. data/lib/state_machines/integrations/active_model/observer_update.rb +42 -0
  38. data/lib/state_machines/integrations/version.rb +7 -0
  39. data/spec/active_model_spec.rb +639 -0
  40. data/spec/integration_spec.rb +27 -0
  41. data/spec/observer_spec.rb +519 -0
  42. data/spec/spec_helper.rb +8 -0
  43. data/spec/support/en.yml +5 -0
  44. data/spec/support/helpers.rb +64 -0
  45. data/spec/support/migration_helpers.rb +43 -0
  46. data/state_machines-activemodel.gemspec +27 -0
  47. metadata +182 -0
@@ -0,0 +1,11 @@
1
+ {:en => {
2
+ :activemodel => {
3
+ :errors => {
4
+ :messages => {
5
+ :invalid => StateMachines::Machine.default_messages[:invalid],
6
+ :invalid_event => StateMachines::Machine.default_messages[:invalid_event] % ['%{state}'],
7
+ :invalid_transition => StateMachines::Machine.default_messages[:invalid_transition] % ['%{event}']
8
+ }
9
+ }
10
+ }
11
+ }}
@@ -0,0 +1,33 @@
1
+ module StateMachines
2
+ module Integrations #:nodoc:
3
+ module ActiveModel
4
+ # Adds support for invoking callbacks on ActiveModel observers with more
5
+ # than one argument (e.g. the record *and* the state transition). By
6
+ # default, ActiveModel only supports passing the record into the
7
+ # callbacks.
8
+ #
9
+ # For example:
10
+ #
11
+ # class VehicleObserver < ActiveModel::Observer
12
+ # # The default behavior: only pass in the record
13
+ # def after_save(vehicle)
14
+ # end
15
+ #
16
+ # # Custom behavior: allow the transition to be passed in as well
17
+ # def after_transition(vehicle, transition)
18
+ # Audit.log(vehicle, transition)
19
+ # end
20
+ # end
21
+ module Observer
22
+ def update_with_transition(observer_update)
23
+ method = observer_update.method
24
+ send(method, *observer_update.args) if respond_to?(method)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ ActiveModel::Observer.class_eval do
32
+ include StateMachines::Integrations::ActiveModel::Observer
33
+ end
@@ -0,0 +1,42 @@
1
+ module StateMachines
2
+ module Integrations #:nodoc:
3
+ module ActiveModel
4
+ # Represents the encapsulation of all of the details to be included in an
5
+ # update to state machine observers. This allows multiple arguments to
6
+ # get passed to an observer method (instead of just a single +object+)
7
+ # while still respecting the way in which ActiveModel checks for the
8
+ # object's list of observers.
9
+ class ObserverUpdate
10
+ # The method to invoke on the observer
11
+ attr_reader :method
12
+
13
+ # The object being transitioned
14
+ attr_reader :object
15
+
16
+ # The transition being run
17
+ attr_reader :transition
18
+
19
+ def initialize(method, object, transition) #:nodoc:
20
+ @method, @object, @transition = method, object, transition
21
+ end
22
+
23
+ # The arguments to pass into the method
24
+ def args
25
+ [object, transition]
26
+ end
27
+
28
+ # The class of the object being transitioned. Normally the object
29
+ # getting passed into observer methods is the actual instance of the
30
+ # ActiveModel class. ActiveModel uses that instance's class to check
31
+ # for enabled / disabled observers.
32
+ #
33
+ # Since state_machine is passing an ObserverUpdate instance into observer
34
+ # methods, +class+ needs to be overridden so that ActiveModel can still
35
+ # get access to the enabled / disabled observers.
36
+ def class
37
+ object.class
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ module StateMachines
2
+ module Integrations
3
+ module ActiveModel
4
+ VERSION = '0.0.1'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,639 @@
1
+ require 'spec_helper'
2
+
3
+ describe StateMachines::Integrations::ActiveModel do
4
+ context 'ByDefault' do
5
+ let(:model) { new_model }
6
+ let!(:machine) { StateMachines::Machine.new(model, :integration => :active_model) }
7
+
8
+ it 'should_not_have_action' do
9
+ expect(machine.action).to be_nil
10
+ end
11
+
12
+ it 'should_use_transactions' do
13
+ expect(machine.use_transactions).to be_truthy
14
+ end
15
+
16
+ it 'should_not_have_any_before_callbacks' do
17
+ expect(machine.callbacks[:before].size).to eq(0)
18
+ end
19
+
20
+ it 'should_not_have_any_after_callbacks' do
21
+ expect(machine.callbacks[:after].size).to eq(0)
22
+ end
23
+ end
24
+
25
+ context 'WithStates' do
26
+ before(:each) do
27
+ @model = new_model
28
+ @machine = StateMachines::Machine.new(@model)
29
+ @machine.state :first_gear
30
+ end
31
+
32
+ it 'should_humanize_name' do
33
+ expect(@machine.state(:first_gear).human_name).to eq('first gear')
34
+ end
35
+ end
36
+
37
+ context 'WithStaticInitialState' do
38
+ before(:each) do
39
+ @model = new_model
40
+ @machine = StateMachines::Machine.new(@model, :initial => :parked, :integration => :active_model)
41
+ end
42
+
43
+ it 'should_set_initial_state_on_created_object' do
44
+ record = @model.new
45
+ expect(record.state).to eq('parked')
46
+ end
47
+ end
48
+
49
+ context 'WithDynamicInitialState' do
50
+ before(:each) do
51
+ @model = new_model
52
+ @machine = StateMachines::Machine.new(@model, :initial => lambda { |object| :parked }, :integration => :active_model)
53
+ @machine.state :parked
54
+ end
55
+
56
+ it 'should_set_initial_state_on_created_object' do
57
+ record = @model.new
58
+ assert_equal 'parked', record.state
59
+ end
60
+ end
61
+
62
+ context 'WithEvents' do
63
+ before(:each) do
64
+ @model = new_model
65
+ @machine = StateMachines::Machine.new(@model)
66
+ @machine.event :shift_up
67
+ end
68
+
69
+ it 'should_humanize_name' do
70
+ assert_equal 'shift up', @machine.event(:shift_up).human_name
71
+ end
72
+ end
73
+
74
+ context 'WithModelStateAttribute' do
75
+ before(:each) do
76
+ @model = new_model
77
+ @machine = StateMachines::Machine.new(@model, :initial => :parked, :integration => :active_model)
78
+ @machine.other_states(:idling)
79
+
80
+ @record = @model.new
81
+ end
82
+
83
+ it 'should_have_an_attribute_predicate' do
84
+ assert @record.respond_to?(:state?)
85
+ end
86
+
87
+ it 'should_raise_exception_for_predicate_without_parameters' do
88
+ assert_raise(ArgumentError) { @record.state? }
89
+ end
90
+
91
+ it 'should_return_false_for_predicate_if_does_not_match_current_value' do
92
+ assert !@record.state?(:idling)
93
+ end
94
+
95
+ it 'should_return_true_for_predicate_if_matches_current_value' do
96
+ assert @record.state?(:parked)
97
+ end
98
+
99
+ it 'should_raise_exception_for_predicate_if_invalid_state_specified' do
100
+ assert_raise(IndexError) { @record.state?(:invalid) }
101
+ end
102
+ end
103
+
104
+ context 'WithNonModelStateAttributeUndefined' do
105
+ before(:each) do
106
+ @model = new_model do
107
+ def initialize
108
+ end
109
+ end
110
+
111
+ @machine = StateMachines::Machine.new(@model, :status, :initial => :parked, :integration => :active_model)
112
+ @machine.other_states(:idling)
113
+ @record = @model.new
114
+ end
115
+
116
+ it 'should_not_define_a_reader_attribute_for_the_attribute' do
117
+ assert !@record.respond_to?(:status)
118
+ end
119
+
120
+ it 'should_not_define_a_writer_attribute_for_the_attribute' do
121
+ assert !@record.respond_to?(:status=)
122
+ end
123
+
124
+ it 'should_define_an_attribute_predicate' do
125
+ assert @record.respond_to?(:status?)
126
+ end
127
+ end
128
+
129
+ context 'WithInitializedState' do
130
+ before(:each) do
131
+ @model = new_model
132
+ @machine = StateMachines::Machine.new(@model, :initial => :parked, :integration => :active_model)
133
+ @machine.state :idling
134
+ end
135
+
136
+ it 'should_allow_nil_initial_state_when_static' do
137
+ @machine.state nil
138
+
139
+ record = @model.new(:state => nil)
140
+ assert_nil record.state
141
+ end
142
+
143
+ it 'should_allow_nil_initial_state_when_dynamic' do
144
+ @machine.state nil
145
+
146
+ @machine.initial_state = lambda { :parked }
147
+ record = @model.new(:state => nil)
148
+ assert_nil record.state
149
+ end
150
+
151
+ it 'should_allow_different_initial_state_when_static' do
152
+ record = @model.new(:state => 'idling')
153
+ assert_equal 'idling', record.state
154
+ end
155
+
156
+ it 'should_allow_different_initial_state_when_dynamic' do
157
+ @machine.initial_state = lambda { :parked }
158
+ record = @model.new(:state => 'idling')
159
+ assert_equal 'idling', record.state
160
+ end
161
+
162
+ it 'should_use_default_state_if_protected' do
163
+ if defined?(ActiveModel::MassAssignmentSecurity)
164
+ @model.class_eval do
165
+ include ActiveModel::MassAssignmentSecurity
166
+ attr_protected :state
167
+
168
+ def initialize(attrs = {})
169
+ initialize_state_machines do
170
+ sanitize_for_mass_assignment(attrs).each { |attr, value| send("#{attr}=", value) } if attrs
171
+ @changed_attributes = {}
172
+ end
173
+ end
174
+ end
175
+
176
+ record = @model.new(:state => 'idling')
177
+ assert_equal 'parked', record.state
178
+
179
+ record = @model.new(nil)
180
+ assert_equal 'parked', record.state
181
+ end
182
+ end
183
+ end
184
+
185
+ context 'Multiple' do
186
+ before(:each) do
187
+ @model = new_model do
188
+ model_attribute :status
189
+ end
190
+
191
+ @state_machine = StateMachines::Machine.new(@model, :initial => :parked, :integration => :active_model)
192
+ @status_machine = StateMachines::Machine.new(@model, :status, :initial => :idling, :integration => :active_model)
193
+ end
194
+
195
+ it 'should_should_initialize_each_state' do
196
+ record = @model.new
197
+ assert_equal 'parked', record.state
198
+ assert_equal 'idling', record.status
199
+ end
200
+ end
201
+
202
+ context 'WithDirtyAttributes' do
203
+ before(:each) do
204
+ @model = new_model do
205
+ include ActiveModel::Dirty
206
+ define_attribute_methods [:state]
207
+ end
208
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
209
+ @machine.event :ignite
210
+ @machine.state :idling
211
+
212
+ @record = @model.create
213
+
214
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
215
+ @transition.perform
216
+ end
217
+
218
+ it 'should_include_state_in_changed_attributes' do
219
+ assert_equal %w(state), @record.changed
220
+ end
221
+
222
+ it 'should_track_attribute_change' do
223
+ assert_equal %w(parked idling), @record.changes['state']
224
+ end
225
+
226
+ it 'should_not_reset_changes_on_multiple_transitions' do
227
+ transition = StateMachines::Transition.new(@record, @machine, :ignite, :idling, :idling)
228
+ transition.perform
229
+
230
+ assert_equal %w(parked idling), @record.changes['state']
231
+ end
232
+ end
233
+
234
+ context 'WithDirtyAttributesDuringLoopback' do
235
+ before(:each) do
236
+ @model = new_model do
237
+ include ActiveModel::Dirty
238
+ define_attribute_methods [:state]
239
+ end
240
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
241
+ @machine.event :park
242
+
243
+ @record = @model.create
244
+
245
+ @transition = StateMachines::Transition.new(@record, @machine, :park, :parked, :parked)
246
+ @transition.perform
247
+ end
248
+
249
+ it 'should_not_include_state_in_changed_attributes' do
250
+ assert_equal [], @record.changed
251
+ end
252
+
253
+ it 'should_not_track_attribute_changes' do
254
+ assert_equal nil, @record.changes['state']
255
+ end
256
+ end
257
+
258
+ context 'WithDirtyAttributesAndCustomAttribute' do
259
+ before(:each) do
260
+ @model = new_model do
261
+ include ActiveModel::Dirty
262
+ model_attribute :status
263
+ define_attribute_methods [:status]
264
+ end
265
+ @machine = StateMachines::Machine.new(@model, :status, :initial => :parked)
266
+ @machine.event :ignite
267
+ @machine.state :idling
268
+
269
+ @record = @model.create
270
+
271
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
272
+ @transition.perform
273
+ end
274
+
275
+ it 'should_include_state_in_changed_attributes' do
276
+ assert_equal %w(status), @record.changed
277
+ end
278
+
279
+ it 'should_track_attribute_change' do
280
+ assert_equal %w(parked idling), @record.changes['status']
281
+ end
282
+
283
+ it 'should_not_reset_changes_on_multiple_transitions' do
284
+ transition = StateMachines::Transition.new(@record, @machine, :ignite, :idling, :idling)
285
+ transition.perform
286
+
287
+ assert_equal %w(parked idling), @record.changes['status']
288
+ end
289
+ end
290
+
291
+ context 'WithDirtyAttributeAndCustomAttributesDuringLoopback' do
292
+ before(:each) do
293
+ @model = new_model do
294
+ include ActiveModel::Dirty
295
+ model_attribute :status
296
+ define_attribute_methods [:status]
297
+ end
298
+ @machine = StateMachines::Machine.new(@model, :status, :initial => :parked)
299
+ @machine.event :park
300
+
301
+ @record = @model.create
302
+
303
+ @transition = StateMachines::Transition.new(@record, @machine, :park, :parked, :parked)
304
+ @transition.perform
305
+ end
306
+
307
+ it 'should_not_include_state_in_changed_attributes' do
308
+ assert_equal [], @record.changed
309
+ end
310
+
311
+ it 'should_not_track_attribute_changes' do
312
+ assert_equal nil, @record.changes['status']
313
+ end
314
+ end
315
+
316
+ context 'WithDirtyAttributeAndStateEvents' do
317
+ before(:each) do
318
+ @model = new_model do
319
+ include ActiveModel::Dirty
320
+ define_attribute_methods [:state]
321
+ end
322
+ @machine = StateMachines::Machine.new(@model, :action => :save, :initial => :parked)
323
+ @machine.event :ignite
324
+
325
+ @record = @model.create
326
+ @record.state_event = 'ignite'
327
+ end
328
+
329
+ it 'should_not_include_state_in_changed_attributes' do
330
+ assert_equal [], @record.changed
331
+ end
332
+
333
+ it 'should_not_track_attribute_change' do
334
+ assert_equal nil, @record.changes['state']
335
+ end
336
+ end
337
+
338
+ context 'WithCallbacks' do
339
+ before(:each) do
340
+ @model = new_model
341
+ @machine = StateMachines::Machine.new(@model, :initial => :parked, :integration => :active_model)
342
+ @machine.other_states :idling
343
+ @machine.event :ignite
344
+
345
+ @record = @model.new(:state => 'parked')
346
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
347
+ end
348
+
349
+ it 'should_run_before_callbacks' do
350
+ called = false
351
+ @machine.before_transition {called = true}
352
+
353
+ @transition.perform
354
+ assert called
355
+ end
356
+
357
+ it 'should_pass_record_to_before_callbacks_with_one_argument' do
358
+ record = nil
359
+ @machine.before_transition {|arg| record = arg}
360
+
361
+ @transition.perform
362
+ assert_equal @record, record
363
+ end
364
+
365
+ it 'should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments' do
366
+ callback_args = nil
367
+ @machine.before_transition {|*args| callback_args = args}
368
+
369
+ @transition.perform
370
+ assert_equal [@record, @transition], callback_args
371
+ end
372
+
373
+ it 'should_run_before_callbacks_outside_the_context_of_the_record' do
374
+ context = nil
375
+ @machine.before_transition {context = self}
376
+
377
+ @transition.perform
378
+ assert_equal self, context
379
+ end
380
+
381
+ it 'should_run_after_callbacks' do
382
+ called = false
383
+ @machine.after_transition {called = true}
384
+
385
+ @transition.perform
386
+ assert called
387
+ end
388
+
389
+ it 'should_pass_record_to_after_callbacks_with_one_argument' do
390
+ record = nil
391
+ @machine.after_transition {|arg| record = arg}
392
+
393
+ @transition.perform
394
+ assert_equal @record, record
395
+ end
396
+
397
+ it 'should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments' do
398
+ callback_args = nil
399
+ @machine.after_transition {|*args| callback_args = args}
400
+
401
+ @transition.perform
402
+ assert_equal [@record, @transition], callback_args
403
+ end
404
+
405
+ it 'should_run_after_callbacks_outside_the_context_of_the_record' do
406
+ context = nil
407
+ @machine.after_transition {context = self}
408
+
409
+ @transition.perform
410
+ assert_equal self, context
411
+ end
412
+
413
+ it 'should_run_around_callbacks' do
414
+ before_called = false
415
+ after_called = false
416
+ ensure_called = 0
417
+ @machine.around_transition do |block|
418
+ before_called = true
419
+ begin
420
+ block.call
421
+ ensure
422
+ ensure_called += 1
423
+ end
424
+ after_called = true
425
+ end
426
+
427
+ @transition.perform
428
+ assert before_called
429
+ assert after_called
430
+ assert_equal ensure_called, 1
431
+ end
432
+
433
+ it 'should_include_transition_states_in_known_states' do
434
+ @machine.before_transition :to => :first_gear, :do => lambda {}
435
+
436
+ assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
437
+ end
438
+
439
+ it 'should_allow_symbolic_callbacks' do
440
+ callback_args = nil
441
+
442
+ klass = class << @record; self; end
443
+ klass.send(:define_method, :after_ignite) do |*args|
444
+ callback_args = args
445
+ end
446
+
447
+ @machine.before_transition(:after_ignite)
448
+
449
+ @transition.perform
450
+ assert_equal [@transition], callback_args
451
+ end
452
+
453
+ it 'should_allow_string_callbacks' do
454
+ class << @record
455
+ attr_reader :callback_result
456
+ end
457
+
458
+ @machine.before_transition('@callback_result = [1, 2, 3]')
459
+ @transition.perform
460
+
461
+ assert_equal [1, 2, 3], @record.callback_result
462
+ end
463
+ end
464
+
465
+ context 'WithFailedBeforeCallbacks' do
466
+ before(:each) do
467
+ @callbacks = []
468
+
469
+ @model = new_model
470
+ @machine = StateMachines::Machine.new(@model, :integration => :active_model)
471
+ @machine.state :parked, :idling
472
+ @machine.event :ignite
473
+ @machine.before_transition {@callbacks << :before_1; false}
474
+ @machine.before_transition {@callbacks << :before_2}
475
+ @machine.after_transition {@callbacks << :after}
476
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
477
+
478
+ @record = @model.new(:state => 'parked')
479
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
480
+ @result = @transition.perform
481
+ end
482
+
483
+ it 'should_not_be_successful' do
484
+ assert !@result
485
+ end
486
+
487
+ it 'should_not_change_current_state' do
488
+ assert_equal 'parked', @record.state
489
+ end
490
+
491
+ it 'should_not_run_further_callbacks' do
492
+ assert_equal [:before_1], @callbacks
493
+ end
494
+ end
495
+
496
+ context 'WithFailedAfterCallbacks' do
497
+ before(:each) do
498
+ @callbacks = []
499
+
500
+ @model = new_model
501
+ @machine = StateMachines::Machine.new(@model, :integration => :active_model)
502
+ @machine.state :parked, :idling
503
+ @machine.event :ignite
504
+ @machine.after_transition {@callbacks << :after_1; false}
505
+ @machine.after_transition {@callbacks << :after_2}
506
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
507
+
508
+ @record = @model.new(:state => 'parked')
509
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
510
+ @result = @transition.perform
511
+ end
512
+
513
+ it 'should_be_successful' do
514
+ assert @result
515
+ end
516
+
517
+ it 'should_change_current_state' do
518
+ assert_equal 'idling', @record.state
519
+ end
520
+
521
+ it 'should_not_run_further_after_callbacks' do
522
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
523
+ end
524
+ end
525
+
526
+ context 'WithValidations' do
527
+ before(:each) do
528
+ @model = new_model { include ActiveModel::Validations }
529
+ @machine = StateMachines::Machine.new(@model, :action => :save)
530
+ @machine.state :parked
531
+
532
+ @record = @model.new
533
+ end
534
+
535
+ it 'should_invalidate_using_errors' do
536
+ I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
537
+ @record.state = 'parked'
538
+
539
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
540
+ assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
541
+ end
542
+
543
+ it 'should_auto_prefix_custom_attributes_on_invalidation' do
544
+ @machine.invalidate(@record, :event, :invalid)
545
+
546
+ assert_equal ['State event is invalid'], @record.errors.full_messages
547
+ end
548
+
549
+ it 'should_clear_errors_on_reset' do
550
+ @record.state = 'parked'
551
+ @record.errors.add(:state, 'is invalid')
552
+
553
+ @machine.reset(@record)
554
+ assert_equal [], @record.errors.full_messages
555
+ end
556
+
557
+ it 'should_be_valid_if_state_is_known' do
558
+ @record.state = 'parked'
559
+
560
+ assert @record.valid?
561
+ end
562
+
563
+ it 'should_not_be_valid_if_state_is_unknown' do
564
+ @record.state = 'invalid'
565
+
566
+ assert !@record.valid?
567
+ assert_equal ['State is invalid'], @record.errors.full_messages
568
+ end
569
+ end
570
+
571
+ context 'WithValidationsAndCustomAttribute' do
572
+ before(:each) do
573
+ @model = new_model { include ActiveModel::Validations }
574
+
575
+ @machine = StateMachines::Machine.new(@model, :status, :attribute => :state)
576
+ @machine.state :parked
577
+
578
+ @record = @model.new
579
+ end
580
+
581
+ it 'should_add_validation_errors_to_custom_attribute' do
582
+ @record.state = 'invalid'
583
+
584
+ assert !@record.valid?
585
+ assert_equal ['State is invalid'], @record.errors.full_messages
586
+
587
+ @record.state = 'parked'
588
+ assert @record.valid?
589
+ end
590
+ end
591
+
592
+ context 'Errors' do
593
+ before(:each) do
594
+ @model = new_model { include ActiveModel::Validations }
595
+ @machine = StateMachines::Machine.new(@model)
596
+ @record = @model.new
597
+ end
598
+
599
+ it 'should_be_able_to_describe_current_errors' do
600
+ @record.errors.add(:id, 'cannot be blank')
601
+ @record.errors.add(:state, 'is invalid')
602
+ assert_equal ['Id cannot be blank', 'State is invalid'], @machine.errors_for(@record).split(', ').sort
603
+ end
604
+
605
+ it 'should_describe_as_halted_with_no_errors' do
606
+ assert_equal 'Transition halted', @machine.errors_for(@record)
607
+ end
608
+ end
609
+
610
+ context 'WithStateDrivenValidations' do
611
+ before(:each) do
612
+ @model = new_model do
613
+ include ActiveModel::Validations
614
+ attr_accessor :seatbelt
615
+ end
616
+
617
+ @machine = StateMachines::Machine.new(@model)
618
+ @machine.state :first_gear, :second_gear do
619
+ validates_presence_of :seatbelt
620
+ end
621
+ @machine.other_states :parked
622
+ end
623
+
624
+ it 'should_be_valid_if_validation_fails_outside_state_scope' do
625
+ record = @model.new(:state => 'parked', :seatbelt => nil)
626
+ assert record.valid?
627
+ end
628
+
629
+ it 'should_be_invalid_if_validation_fails_within_state_scope' do
630
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
631
+ assert !record.valid?
632
+ end
633
+
634
+ it 'should_be_valid_if_validation_succeeds_within_state_scope' do
635
+ record = @model.new(:state => 'second_gear', :seatbelt => true)
636
+ assert record.valid?
637
+ end
638
+ end
639
+ end