state_machines-activemodel 0.0.1

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