can_has_state 0.8.0 → 0.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b2d2008ba870961e06cebe22f027b133b46c5f078315938c9338edfd99678f5
4
- data.tar.gz: 2d4e7dbfd0bb896e01dfdaf66933b3b483f71915579f5d66d42fde1cbd0416de
3
+ metadata.gz: 516a5676e62907d7868c5a0812c760e292b87c8c785cb246c5eb3bd2db2d7a41
4
+ data.tar.gz: 1e4b230a4a4c933b1db2884b2f7490439c9fb2f4f18b1ee686286decc2640903
5
5
  SHA512:
6
- metadata.gz: 9e18bb675dd1acc0b84bb8539f67ca9c483f7ff618927b9baef5cdc738ba7a723cac0cccbaed68dfd0291925a2af5a8cda4ebf22b250ead0d39d813e389ecd86
7
- data.tar.gz: 2cc03d656532de9061758242d7daaab074f979665323a071d432b1c220e050951da7750ca6f38c5ef1c8e1e4633f76ce16bd8f404fbe9e5e31ba8fe0a6426f17
6
+ metadata.gz: cb9ef6c65385ef382a01d9f73c40da6217136f9939fea20eb6aae0183ce57f492e119302fc560f9273b2f6c2933cde3fa2c50daf3e0f13d038cf19dbfa377d5b
7
+ data.tar.gz: f1aee70c43b00b6ae153c63583db1d1bf4bd9adb072fa4b5adf33eecd1b4f3c1668aa1aba54438b296158ad858475a1d52333ce95f0e2e9959d1d1fb8b7d499b
@@ -111,23 +111,23 @@ module CanHasState
111
111
  end
112
112
  end
113
113
 
114
- def can_has_state_errors
115
- err = {}
114
+ def can_has_state_errors(reset: true)
115
+ @can_has_state_errors = {} if reset || !@can_has_state_errors
116
116
  state_machines.each do |column, sm|
117
117
  from, to = send("#{column}_was"), send(column)
118
118
  if !sm.known?(to)
119
- err[column] = [:invalid_state]
119
+ @can_has_state_errors[column] = [:invalid_state]
120
120
  elsif from == to
121
121
  next
122
122
  elsif !sm.allow?(self, to) #state_machine_allow?(column, to)
123
- err[column] = [sm.message(to), {from: "'#{from}'", to: "'#{to}'"}]
123
+ @can_has_state_errors[column] = [sm.message(to), {from: "'#{from}'", to: "'#{to}'"}]
124
124
  end
125
125
  end
126
- err
126
+ @can_has_state_errors
127
127
  end
128
128
 
129
129
  def validate_state_machines
130
- can_has_state_errors.each do |column, (msg, opts)|
130
+ can_has_state_errors(reset: false).each do |column, (msg, opts)|
131
131
  errors.add column, msg, **(opts||{})
132
132
  end
133
133
  end
@@ -1,3 +1,3 @@
1
1
  module CanHasState
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -406,6 +406,74 @@ class CanHasStateTest < Minitest::Test
406
406
  end
407
407
 
408
408
 
409
+ def test_errors_resolved_in_later_callback_are_kept
410
+ # Triggers are skipped when the state_machine has validation errors.
411
+ # Whether or not to skip is decided in an early before_validation callback.
412
+ # Even if a later callback resolves the validation error, it's important to
413
+ # still bubble up the errors since triggers did not run.
414
+ kl = build_from_skeleton do
415
+ attr_accessor :have_beans, :coffee_in_hand
416
+ extend_state_machine :state do
417
+ state :incredible,
418
+ require: proc{ coffee_in_hand > 0 }
419
+ end
420
+ before_validation { self.coffee_in_hand += 1 if have_beans }
421
+ def initialize
422
+ @coffee_in_hand = 0
423
+ end
424
+ end
425
+ m = kl.new
426
+ m.state = 'incredible'
427
+ refute m.valid?
428
+ assert m.errors.of_kind?(:state, :invalid_transition)
429
+ assert_equal 0, m.coffee_in_hand
430
+
431
+ m = kl.new
432
+ m.have_beans = true
433
+ m.state = 'incredible'
434
+ refute m.valid?
435
+ assert m.errors.of_kind?(:state, :invalid_transition)
436
+ assert_equal 1, m.coffee_in_hand
437
+ end
438
+
439
+ def test_rerunning_triggers_clears_pre_callback_errors
440
+ # To resolve the issue above, it is possible to re-eval the state_machine
441
+ # and run triggers if validations now pass. This is highly discouraged as
442
+ # complex callback callback interactions like this should be refactored out
443
+ # if at all possible.
444
+ # Not an officially supported solution and subject to change, but
445
+ # documented here to show that it is (presently) possible.
446
+ kl = build_from_skeleton do
447
+ attr_accessor :have_beans, :coffee_in_hand, :we_are_incredible
448
+ extend_state_machine :state do
449
+ state :incredible,
450
+ require: proc{ coffee_in_hand > 0 },
451
+ on_enter: proc{ self.we_are_incredible = true }
452
+ end
453
+ before_validation do
454
+ self.coffee_in_hand += 1 if have_beans
455
+ run_state_triggers
456
+ end
457
+ def initialize
458
+ @coffee_in_hand = 0
459
+ end
460
+ end
461
+ m = kl.new
462
+ m.state = 'incredible'
463
+ refute m.valid?
464
+ assert m.errors.of_kind?(:state, :invalid_transition)
465
+ assert_equal 0, m.coffee_in_hand
466
+ refute m.we_are_incredible
467
+
468
+ m = kl.new
469
+ m.have_beans = true
470
+ m.state = 'incredible'
471
+ assert m.valid?
472
+ assert_equal 1, m.coffee_in_hand
473
+ assert m.we_are_incredible
474
+ end
475
+
476
+
409
477
 
410
478
  def build_from_skeleton(&block)
411
479
  Class.new(Skeleton).tap do |kl|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: can_has_state
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thomas morgan