aasm 5.0.5 → 5.1.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 (53) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +26 -23
  5. data/Appraisals +6 -10
  6. data/CHANGELOG.md +30 -0
  7. data/Dockerfile +1 -6
  8. data/Gemfile +1 -1
  9. data/README.md +63 -12
  10. data/aasm.gemspec +2 -0
  11. data/gemfiles/norails.gemfile +1 -1
  12. data/gemfiles/rails_4.2.gemfile +1 -0
  13. data/gemfiles/rails_4.2_mongoid_5.gemfile +1 -0
  14. data/gemfiles/rails_5.0.gemfile +1 -0
  15. data/gemfiles/rails_5.1.gemfile +1 -0
  16. data/gemfiles/rails_5.2.gemfile +1 -0
  17. data/lib/aasm.rb +0 -2
  18. data/lib/aasm/aasm.rb +29 -27
  19. data/lib/aasm/base.rb +19 -0
  20. data/lib/aasm/core/event.rb +3 -3
  21. data/lib/aasm/instance_base.rb +16 -4
  22. data/lib/aasm/persistence/active_record_persistence.rb +18 -0
  23. data/lib/aasm/persistence/orm.rb +23 -19
  24. data/lib/aasm/rspec/transition_from.rb +5 -1
  25. data/lib/aasm/version.rb +1 -1
  26. data/spec/database.rb +6 -1
  27. data/spec/en.yml +0 -3
  28. data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +0 -4
  29. data/spec/localizer_test_model_new_style.yml +5 -0
  30. data/spec/models/active_record/active_record_callback.rb +93 -0
  31. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  32. data/spec/models/active_record/localizer_test_model.rb +3 -3
  33. data/spec/models/callbacks/with_state_arg.rb +5 -1
  34. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  35. data/spec/models/default_state.rb +1 -1
  36. data/spec/models/simple_example.rb +6 -0
  37. data/spec/spec_helper.rb +15 -0
  38. data/spec/unit/api_spec.rb +4 -0
  39. data/spec/unit/callback_multiple_spec.rb +4 -0
  40. data/spec/unit/callbacks_spec.rb +4 -0
  41. data/spec/unit/complex_example_spec.rb +0 -1
  42. data/spec/unit/event_spec.rb +13 -0
  43. data/spec/unit/inspection_multiple_spec.rb +9 -5
  44. data/spec/unit/inspection_spec.rb +7 -3
  45. data/spec/unit/localizer_spec.rb +9 -10
  46. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
  47. data/spec/unit/persistence/active_record_persistence_spec.rb +93 -4
  48. data/spec/unit/rspec_matcher_spec.rb +3 -0
  49. data/spec/unit/simple_example_spec.rb +15 -0
  50. data/spec/unit/state_spec.rb +21 -5
  51. metadata +50 -11
  52. data/callbacks.txt +0 -51
  53. data/gemfiles/rails_3.2.gemfile +0 -14
@@ -0,0 +1,19 @@
1
+ class InstanceLevelSkipValidationExample < ActiveRecord::Base
2
+ include AASM
3
+
4
+ aasm :state do
5
+ state :new, :initial => true
6
+ state :draft
7
+ state :complete
8
+
9
+ event :set_draft do
10
+ transitions from: :new, to: :draft
11
+ end
12
+
13
+ event :complete do
14
+ transitions from: %i[draft new], to: :complete
15
+ end
16
+ end
17
+
18
+ validates :some_string, presence: true
19
+ end
@@ -11,13 +11,13 @@ end
11
11
 
12
12
  describe 'localized state names' do
13
13
  before(:all) do
14
- I18n.load_path << 'spec/en.yml'
15
- I18n.default_locale = :en
14
+ I18n.load_path << 'spec/localizer_test_model_new_style.yml'
16
15
  I18n.reload!
17
16
  end
18
17
 
19
18
  after(:all) do
20
- I18n.load_path.clear
19
+ I18n.load_path.delete('spec/localizer_test_model_new_style.yml')
20
+ I18n.backend.load_translations
21
21
  end
22
22
 
23
23
  it 'should localize' do
@@ -8,7 +8,7 @@ module Callbacks
8
8
  state :closed
9
9
  state :out_to_lunch
10
10
 
11
- event :close, :before => :before_method, :after => :after_method do
11
+ event :close, :before => :before_method, :after => :after_method, :before_success => :before_success_method, :success => :success_method3 do
12
12
  transitions :to => :closed, :from => [:open], :after => :transition_method, :success => :success_method
13
13
  transitions :to => :out_to_lunch, :from => [:open], :after => :transition_method2, :success => :success_method2
14
14
  end
@@ -16,6 +16,8 @@ module Callbacks
16
16
 
17
17
  def before_method(arg); end
18
18
 
19
+ def before_success_method(arg); end
20
+
19
21
  def after_method(arg); end
20
22
 
21
23
  def transition_method(arg); end
@@ -26,5 +28,7 @@ module Callbacks
26
28
 
27
29
  def success_method2(arg); end
28
30
 
31
+ def success_method3(arg); end
32
+
29
33
  end
30
34
  end
@@ -8,7 +8,7 @@ module Callbacks
8
8
  state :closed
9
9
  state :out_to_lunch
10
10
 
11
- event :close, :before => :before_method, :after => :after_method do
11
+ event :close, :before => :before_method, :after => :after_method, :before_success => :before_success_method, :success => :success_method do
12
12
  transitions :to => :closed, :from => [:open], :after => :transition_method
13
13
  transitions :to => :out_to_lunch, :from => [:open], :after => :transition_method2
14
14
  end
@@ -16,11 +16,14 @@ module Callbacks
16
16
 
17
17
  def before_method(arg); end
18
18
 
19
+ def before_success_method(arg); end
20
+
19
21
  def after_method(arg); end
20
22
 
21
23
  def transition_method(arg); end
22
24
 
23
25
  def transition_method2(arg); end
24
26
 
27
+ def success_method(arg); end
25
28
  end
26
29
  end
@@ -2,7 +2,7 @@ class DefaultState
2
2
  attr_accessor :transient_store, :persisted_store
3
3
  include AASM
4
4
  aasm do
5
- state :alpha, :initial => true
5
+ state :alpha, :initial => true, display: 'ALPHA'
6
6
  state :beta
7
7
  state :gamma
8
8
  event :release do
@@ -3,11 +3,17 @@ class SimpleExample
3
3
  aasm do
4
4
  state :initialised, :initial => true
5
5
  state :filled_out
6
+ state :denied
6
7
  state :authorised
7
8
 
8
9
  event :fill_out do
9
10
  transitions :from => :initialised, :to => :filled_out
10
11
  end
12
+
13
+ event :deny do
14
+ transitions from: :initialised, to: :denied
15
+ end
16
+
11
17
  event :authorise do
12
18
  transitions :from => :filled_out, :to => :authorised
13
19
  end
@@ -1,8 +1,19 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/spec/'
4
+ end
5
+
6
+ if ENV['CI'] == 'true'
7
+ require 'codecov'
8
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
9
+ end
10
+
1
11
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
2
12
  $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
13
  require 'aasm'
4
14
  require 'rspec'
5
15
  require 'aasm/rspec'
16
+ require 'i18n'
6
17
  require 'pry'
7
18
 
8
19
  # require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
@@ -24,3 +35,7 @@ Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].sort.each { |f| require Fi
24
35
 
25
36
  # example model classes
26
37
  Dir[File.dirname(__FILE__) + "/models/*.rb"].sort.each { |f| require File.expand_path(f) }
38
+
39
+ I18n.load_path << 'spec/en.yml'
40
+ I18n.enforce_available_locales = false
41
+ I18n.default_locale = :en
@@ -13,6 +13,10 @@ if defined?(ActiveRecord)
13
13
  expect(DefaultState.new.aasm.current_state).to eql :alpha
14
14
  end
15
15
 
16
+ it "uses display option" do
17
+ expect(DefaultState.new.aasm.human_state).to eql "ALPHA"
18
+ end
19
+
16
20
  it "uses the provided method" do
17
21
  expect(ProvidedState.new.aasm.current_state).to eql :beta
18
22
  end
@@ -154,6 +154,8 @@ describe 'callbacks for the new DSL' do
154
154
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
155
155
  expect(cb).to receive(:transition_method).never
156
156
  expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
157
+ expect(cb).to receive(:before_success_method).with(:arg1).once.ordered
158
+ expect(cb).to receive(:success_method).with(:arg1).once.ordered
157
159
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
158
160
  cb.close!(:out_to_lunch, :arg1)
159
161
 
@@ -161,6 +163,8 @@ describe 'callbacks for the new DSL' do
161
163
  some_object = double('some object')
162
164
  expect(cb).to receive(:before_method).with(some_object).once.ordered
163
165
  expect(cb).to receive(:transition_method2).with(some_object).once.ordered
166
+ expect(cb).to receive(:before_success_method).with(some_object).once.ordered
167
+ expect(cb).to receive(:success_method).with(some_object).once.ordered
164
168
  expect(cb).to receive(:after_method).with(some_object).once.ordered
165
169
  cb.close!(:out_to_lunch, some_object)
166
170
  end
@@ -315,7 +315,9 @@ describe 'callbacks for the new DSL' do
315
315
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
316
316
  expect(cb).to receive(:transition_method).with(:arg1).once.ordered
317
317
  expect(cb).to receive(:transition_method).never
318
+ expect(cb).to receive(:before_success_method).with(:arg1).once.ordered
318
319
  expect(cb).to receive(:success_method).with(:arg1).once.ordered
320
+ expect(cb).to receive(:success_method3).with(:arg1).once.ordered
319
321
  expect(cb).to receive(:success_method).never
320
322
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
321
323
  cb.close!(:arg1)
@@ -325,7 +327,9 @@ describe 'callbacks for the new DSL' do
325
327
  expect(cb).to receive(:before_method).with(some_object).once.ordered
326
328
  expect(cb).to receive(:transition_method).with(some_object).once.ordered
327
329
  expect(cb).to receive(:transition_method).never
330
+ expect(cb).to receive(:before_success_method).with(some_object).once.ordered
328
331
  expect(cb).to receive(:success_method).with(some_object).once.ordered
332
+ expect(cb).to receive(:success_method3).with(some_object).once.ordered
329
333
  expect(cb).to receive(:success_method).never
330
334
  expect(cb).to receive(:after_method).with(some_object).once.ordered
331
335
  cb.close!(some_object)
@@ -90,5 +90,4 @@ describe 'when being unsuspended' do
90
90
  it "should not be able to fire unknown events" do
91
91
  expect(auth.aasm.may_fire_event?(:unknown)).to be false
92
92
  end
93
-
94
93
  end
@@ -294,6 +294,19 @@ describe 'current event' do
294
294
  pe.wakeup!
295
295
  expect(pe.aasm.current_event).to eql :wakeup!
296
296
  end
297
+
298
+ describe "when calling events with fire/fire!" do
299
+ it "fire should populate aasm.current_event and transition (sleeping to showering)" do
300
+ pe.aasm.fire(:wakeup)
301
+ expect(pe.aasm.current_event).to eq :wakeup
302
+ expect(pe.aasm.current_state).to eq :showering
303
+ end
304
+ it "fire! should populate aasm.current_event and transition (sleeping to showering)" do
305
+ pe.aasm.fire!(:wakeup)
306
+ expect(pe.aasm.current_event).to eq :wakeup!
307
+ expect(pe.aasm.current_state).to eq :showering
308
+ end
309
+ end
297
310
  end
298
311
 
299
312
  describe 'parametrised events' do
@@ -166,11 +166,15 @@ describe "special cases" do
166
166
  end
167
167
 
168
168
  describe 'aasm.states_for_select' do
169
- it "should return a select friendly array of states" do
170
- expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
171
- expect(FooMultiple.aasm(:left).states_for_select).to eq(
172
- [['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
173
- )
169
+ context 'without I18n' do
170
+ before { allow(Module).to receive(:const_defined?).with(:I18n).and_return(nil) }
171
+
172
+ it "should return a select friendly array of states" do
173
+ expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
174
+ expect(FooMultiple.aasm(:left).states_for_select).to eq(
175
+ [['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
176
+ )
177
+ end
174
178
  end
175
179
  end
176
180
 
@@ -102,9 +102,13 @@ describe "special cases" do
102
102
  end
103
103
 
104
104
  describe 'aasm.states_for_select' do
105
- it "should return a select friendly array of states" do
106
- expect(Foo.aasm).to respond_to(:states_for_select)
107
- expect(Foo.aasm.states_for_select).to eq([['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']])
105
+ context 'without I18n' do
106
+ before { allow(Module).to receive(:const_defined?).with(:I18n).and_return(nil) }
107
+
108
+ it "should return a select friendly array of states" do
109
+ expect(Foo.aasm).to respond_to(:states_for_select)
110
+ expect(Foo.aasm.states_for_select).to eq([['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']])
111
+ end
108
112
  end
109
113
  end
110
114
 
@@ -1,20 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
- if defined?(ActiceRecord)
4
- require 'i18n'
5
-
6
- I18n.enforce_available_locales = false
3
+ if defined?(ActiveRecord)
4
+ require 'models/active_record/localizer_test_model'
7
5
  load_schema
8
6
 
9
7
  describe AASM::Localizer, "new style" do
10
8
  before(:all) do
11
- I18n.load_path << 'spec/en.yml'
12
- I18n.default_locale = :en
9
+ I18n.load_path << 'spec/localizer_test_model_new_style.yml'
13
10
  I18n.reload!
14
11
  end
15
12
 
16
13
  after(:all) do
17
- I18n.load_path.clear
14
+ I18n.load_path.delete('spec/localizer_test_model_new_style.yml')
15
+ I18n.backend.load_translations
18
16
  end
19
17
 
20
18
  let (:foo_opened) { LocalizerTestModel.new }
@@ -43,13 +41,14 @@ if defined?(ActiceRecord)
43
41
 
44
42
  describe AASM::Localizer, "deprecated style" do
45
43
  before(:all) do
46
- I18n.load_path << 'spec/en_deprecated_style.yml'
47
- I18n.default_locale = :en
44
+ I18n.load_path << 'spec/localizer_test_model_deprecated_style.yml'
48
45
  I18n.reload!
46
+ I18n.backend.load_translations
49
47
  end
50
48
 
51
49
  after(:all) do
52
- I18n.load_path.clear
50
+ I18n.load_path.delete('spec/localizer_test_model_deprecated_style.yml')
51
+ I18n.backend.load_translations
53
52
  end
54
53
 
55
54
  let (:foo_opened) { LocalizerTestModel.new }
@@ -359,13 +359,13 @@ if defined?(ActiveRecord)
359
359
 
360
360
  # allow it temporarily
361
361
  MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = false
362
- obj.aasm_state = :pending
363
- expect(obj.aasm_state.to_sym).to eql :pending
362
+ obj.aasm_state = :running
363
+ expect(obj.aasm_state.to_sym).to eql :running
364
364
 
365
365
  # and forbid it again
366
366
  MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = true
367
- expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
368
- expect(obj.aasm_state.to_sym).to eql :pending
367
+ expect {obj.aasm_state = :pending}.to raise_error(AASM::NoDirectAssignmentError)
368
+ expect(obj.aasm_state.to_sym).to eql :running
369
369
  end
370
370
  end # direct assignment
371
371
 
@@ -393,13 +393,13 @@ if defined?(ActiveRecord)
393
393
 
394
394
  # allow it temporarily
395
395
  NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
396
- obj.aasm_state = :pending
397
- expect(obj.aasm_state.to_sym).to eql :pending
396
+ obj.aasm_state = :running
397
+ expect(obj.aasm_state.to_sym).to eql :running
398
398
 
399
399
  # and forbid it again
400
400
  NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = true
401
- expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
402
- expect(obj.aasm_state.to_sym).to eql :pending
401
+ expect {obj.aasm_state = :pending}.to raise_error(AASM::NoDirectAssignmentError)
402
+ expect(obj.aasm_state.to_sym).to eql :running
403
403
  end
404
404
  end # direct assignment
405
405
 
@@ -613,6 +613,73 @@ if defined?(ActiveRecord)
613
613
  expect(validator).to be_running
614
614
  expect(validator.name).to eq("name")
615
615
  end
616
+
617
+ context "nested transaction" do
618
+ it "should fire :after_commit if root transaction was successful" do
619
+ validator = Validator.create(:name => 'name')
620
+ expect(validator).to be_sleeping
621
+
622
+ validator.transaction do
623
+ validator.run!
624
+ expect(validator.name).to eq("name")
625
+ expect(validator).to be_running
626
+ end
627
+
628
+ expect(validator.name).to eq("name changed")
629
+ expect(validator.reload).to be_running
630
+ end
631
+
632
+ it "should not fire :after_commit if root transaction failed" do
633
+ validator = Validator.create(:name => 'name')
634
+ expect(validator).to be_sleeping
635
+
636
+ validator.transaction do
637
+ validator.run!
638
+ expect(validator.name).to eq("name")
639
+ expect(validator).to be_running
640
+
641
+ raise ActiveRecord::Rollback, "failed on purpose"
642
+ end
643
+
644
+ expect(validator.name).to eq("name")
645
+ expect(validator.reload).to be_sleeping
646
+ end
647
+ end
648
+ end
649
+
650
+ describe 'callbacks for the new DSL' do
651
+
652
+ it "be called in order" do
653
+ show_debug_log = false
654
+
655
+ callback = ActiveRecordCallback.create
656
+ callback.aasm.current_state
657
+
658
+ unless show_debug_log
659
+ expect(callback).to receive(:before_all_events).once.ordered
660
+ expect(callback).to receive(:before_event).once.ordered
661
+ expect(callback).to receive(:event_guard).once.ordered.and_return(true)
662
+ expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
663
+ expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
664
+ expect(callback).to receive(:exit_open).once.ordered
665
+ # expect(callback).to receive(:event_guard).once.ordered.and_return(true)
666
+ # expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
667
+ expect(callback).to receive(:after_all_transitions).once.ordered
668
+ expect(callback).to receive(:after_transition).once.ordered
669
+ expect(callback).to receive(:before_enter_closed).once.ordered
670
+ expect(callback).to receive(:enter_closed).once.ordered
671
+ expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
672
+ expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
673
+ expect(callback).to receive(:after_enter_closed).once.ordered
674
+ expect(callback).to receive(:after_event).once.ordered
675
+ expect(callback).to receive(:after_all_events).once.ordered
676
+ expect(callback).to receive(:ensure_event).once.ordered
677
+ expect(callback).to receive(:ensure_on_all_events).once.ordered
678
+ expect(callback).to receive(:event_after_commit).once.ordered
679
+ end
680
+
681
+ callback.close!
682
+ end
616
683
  end
617
684
 
618
685
  describe 'before and after transaction callbacks' do
@@ -748,4 +815,26 @@ if defined?(ActiveRecord)
748
815
  expect { job.run }.to raise_error(AASM::InvalidTransition)
749
816
  end
750
817
  end
818
+
819
+ describe 'testing the instance_level skip validation with _without_validation method' do
820
+ let(:example) do
821
+ obj = InstanceLevelSkipValidationExample.new(state: 'new')
822
+ obj.save(validate: false)
823
+ obj
824
+ end
825
+
826
+ it 'should be able to change the state with invalid record' do
827
+ expect(example.valid?).to be_falsey
828
+ expect(example.complete!).to be_falsey
829
+ expect(example.complete_without_validation!).to be_truthy
830
+ expect(example.state).to eq('complete')
831
+ end
832
+
833
+ it 'shouldn\'t affect the behaviour of existing method after calling _without_validation! method' do
834
+ expect(example.set_draft!).to be_falsey
835
+ expect(example.set_draft_without_validation!).to be_truthy
836
+ expect(example.state).to eq('draft')
837
+ expect(example.complete!).to be_falsey
838
+ end
839
+ end
751
840
  end
@@ -8,14 +8,17 @@ describe 'state machine' do
8
8
  it "works for simple state machines" do
9
9
  expect(simple).to transition_from(:initialised).to(:filled_out).on_event(:fill_out)
10
10
  expect(simple).to_not transition_from(:initialised).to(:authorised).on_event(:fill_out)
11
+ expect(simple).to_not transition_from(:authorised).to(:filled_out).on_event(:fill_out)
11
12
  end
12
13
 
13
14
  it "works for multiple state machines" do
14
15
  expect(multiple).to transition_from(:standing).to(:walking).on_event(:walk).on(:move)
15
16
  expect(multiple).to_not transition_from(:standing).to(:running).on_event(:walk).on(:move)
17
+ expect(multiple).to_not transition_from(:running).to(:walking).on_event(:walk).on(:move)
16
18
 
17
19
  expect(multiple).to transition_from(:sleeping).to(:processing).on_event(:start).on(:work)
18
20
  expect(multiple).to_not transition_from(:sleeping).to(:sleeping).on_event(:start).on(:work)
21
+ expect(multiple).to_not transition_from(:processing).to(:sleeping).on_event(:start).on(:work)
19
22
  end
20
23
  end
21
24