aasm 4.1.1 → 4.2.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/CODE_OF_CONDUCT.md +13 -0
  4. data/Gemfile +1 -1
  5. data/LICENSE +1 -1
  6. data/README.md +9 -0
  7. data/aasm.gemspec +1 -1
  8. data/lib/aasm/base.rb +12 -4
  9. data/lib/aasm/persistence/active_record_persistence.rb +1 -1
  10. data/lib/aasm/version.rb +1 -1
  11. data/spec/models/active_record/derivate_new_dsl.rb +3 -0
  12. data/spec/models/active_record/false_state.rb +17 -0
  13. data/spec/models/active_record/gate.rb +19 -0
  14. data/spec/models/active_record/localizer_test_model.rb +34 -0
  15. data/spec/models/active_record/no_direct_assignment.rb +10 -0
  16. data/spec/models/active_record/no_scope.rb +10 -0
  17. data/spec/models/active_record/persisted_state.rb +12 -0
  18. data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
  19. data/spec/models/active_record/reader.rb +7 -0
  20. data/spec/models/active_record/simple_new_dsl.rb +8 -0
  21. data/spec/models/active_record/thief.rb +14 -0
  22. data/spec/models/active_record/transient.rb +6 -0
  23. data/spec/models/active_record/with_enum.rb +19 -0
  24. data/spec/models/active_record/with_false_enum.rb +15 -0
  25. data/spec/models/active_record/with_true_enum.rb +19 -0
  26. data/spec/models/active_record/writer.rb +6 -0
  27. data/spec/models/{auth_machine.rb → complex_example.rb} +1 -1
  28. data/spec/models/default_state.rb +12 -0
  29. data/spec/models/initial_state_proc.rb +15 -0
  30. data/spec/models/{bar.rb → no_initial_state.rb} +1 -4
  31. data/spec/models/provided_state.rb +24 -0
  32. data/spec/models/simple_example.rb +15 -0
  33. data/spec/models/state_machine_with_failed_event.rb +12 -0
  34. data/spec/models/sub_class.rb +3 -0
  35. data/spec/models/sub_class_with_more_states.rb +7 -0
  36. data/spec/models/super_class.rb +18 -0
  37. data/spec/models/{argument.rb → valid_state_name.rb} +1 -1
  38. data/spec/models/validator.rb +4 -4
  39. data/spec/spec_helper.rb +1 -1
  40. data/spec/unit/api_spec.rb +6 -1
  41. data/spec/unit/complex_example_spec.rb +2 -2
  42. data/spec/unit/event_naming_spec.rb +2 -15
  43. data/spec/unit/initial_state_spec.rb +3 -18
  44. data/spec/unit/inspection_spec.rb +7 -7
  45. data/spec/unit/localizer_spec.rb +0 -38
  46. data/spec/unit/persistence/active_record_persistence_spec.rb +21 -3
  47. data/spec/unit/simple_example_spec.rb +26 -42
  48. data/spec/unit/subclassing_spec.rb +10 -10
  49. metadata +60 -19
  50. data/spec/models/active_record/api.rb +0 -75
  51. data/spec/models/father.rb +0 -21
  52. data/spec/models/persistence.rb +0 -164
  53. data/spec/models/son.rb +0 -3
@@ -0,0 +1,15 @@
1
+ class SimpleExample
2
+ include AASM
3
+ aasm do
4
+ state :initialised, :initial => true
5
+ state :filled_out
6
+ state :authorised
7
+
8
+ event :fill_out do
9
+ transitions :from => :initialised, :to => :filled_out
10
+ end
11
+ event :authorise do
12
+ transitions :from => :filled_out, :to => :authorised
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ class StateMachineWithFailedEvent
2
+ include AASM
3
+
4
+ aasm do
5
+ state :init, :initial => true
6
+ state :failed
7
+
8
+ event :failed do
9
+ transitions :from => :init, :to => :failed
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'super_class'
2
+ class SubClass < SuperClass
3
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'super_class'
2
+ class SubClassWithMoreStates < SuperClass
3
+ include AASM
4
+ aasm do
5
+ state :foo
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ class SuperClass
2
+ include AASM
3
+
4
+ aasm do
5
+ state :read
6
+ state :ended
7
+
8
+ event :foo do
9
+ transitions :to => :ended, :from => [:read]
10
+ end
11
+ end
12
+
13
+ def update_state
14
+ if may_foo?
15
+ foo!
16
+ end
17
+ end
18
+ end
@@ -1,4 +1,4 @@
1
- class Argument
1
+ class ValidStateName
2
2
  include AASM
3
3
  aasm do
4
4
  state :invalid, :initial => true
@@ -12,8 +12,8 @@ class Validator < ActiveRecord::Base
12
12
  transitions :to => :running, :from => :sleeping
13
13
  end
14
14
  event :sleep do
15
- after_commit do
16
- change_name_on_sleep
15
+ after_commit do |name|
16
+ change_name_on_sleep name
17
17
  end
18
18
  transitions :to => :sleeping, :from => :running
19
19
  end
@@ -29,8 +29,8 @@ class Validator < ActiveRecord::Base
29
29
  save!
30
30
  end
31
31
 
32
- def change_name_on_sleep
33
- self.name = "sleeper"
32
+ def change_name_on_sleep name
33
+ self.name = name
34
34
  save!
35
35
  end
36
36
 
@@ -1,7 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
2
2
  $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
3
  require 'aasm'
4
-
5
4
  require 'rspec'
6
5
 
7
6
  # require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
@@ -11,6 +10,7 @@ require 'rspec'
11
10
  SEQUEL_DB = defined?(JRUBY_VERSION) ? 'jdbc:sqlite::memory:' : 'sqlite:/'
12
11
 
13
12
  def load_schema
13
+ require 'logger'
14
14
  config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
15
15
  ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
16
16
  ActiveRecord::Base.establish_connection(config['sqlite3'])
@@ -1,5 +1,10 @@
1
1
  require 'spec_helper'
2
- require 'models/active_record/api.rb'
2
+ require 'models/default_state.rb'
3
+ require 'models/provided_state.rb'
4
+ require 'models/active_record/persisted_state.rb'
5
+ require 'models/active_record/provided_and_persisted_state.rb'
6
+
7
+ load_schema
3
8
 
4
9
  describe "reading the current state" do
5
10
  it "uses the AASM default" do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'on initialization' do
4
- let(:auth) {AuthMachine.new}
4
+ let(:auth) {ComplexExample.new}
5
5
 
6
6
  it 'should be in the pending state' do
7
7
  expect(auth.aasm.current_state).to eq(:pending)
@@ -14,7 +14,7 @@ describe 'on initialization' do
14
14
  end
15
15
 
16
16
  describe 'when being unsuspended' do
17
- let(:auth) {AuthMachine.new}
17
+ let(:auth) {ComplexExample.new}
18
18
 
19
19
  it 'should be able to be unsuspended' do
20
20
  auth.activate!
@@ -1,22 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- class SimpleStateMachine
4
- include AASM
5
-
6
- aasm do
7
- state :init, :initial => true
8
- state :failed
9
-
10
- event :failed do
11
- transitions :from => :init, :to => :failed
12
- end
13
- end
14
- end
15
-
16
3
  describe "event naming" do
17
- let(:state_machine) { SimpleStateMachine.new }
4
+ let(:state_machine) { StateMachineWithFailedEvent.new }
18
5
 
19
- it "allows an event of failed without blowing the stack" do
6
+ it "allows an event of failed without blowing the stack aka stack level too deep" do
20
7
  state_machine.failed
21
8
 
22
9
  expect { state_machine.failed }.to raise_error(AASM::InvalidTransition)
@@ -1,27 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Banker
4
- include AASM
5
- aasm do
6
- state :retired
7
- state :selling_bad_mortgages
8
- initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages }
9
- end
10
- RICH = 1_000_000
11
- attr_accessor :balance
12
- def initialize(balance = 0); self.balance = balance; end
13
- def rich?; self.balance >= RICH; end
14
- end
15
-
16
3
  describe 'initial states' do
17
- let(:bar) {Bar.new}
18
-
19
4
  it 'should use the first state defined if no initial state is given' do
20
- expect(bar.aasm.current_state).to eq(:read)
5
+ expect(NoInitialState.new.aasm.current_state).to eq(:read)
21
6
  end
22
7
 
23
8
  it 'should determine initial state from the Proc results' do
24
- expect(Banker.new(Banker::RICH - 1).aasm.current_state).to eq(:selling_bad_mortgages)
25
- expect(Banker.new(Banker::RICH + 1).aasm.current_state).to eq(:retired)
9
+ expect(InitialStateProc.new(InitialStateProc::RICH - 1).aasm.current_state).to eq(:selling_bad_mortgages)
10
+ expect(InitialStateProc.new(InitialStateProc::RICH + 1).aasm.current_state).to eq(:retired)
26
11
  end
27
12
  end
@@ -62,11 +62,11 @@ describe 'inspection for common cases' do
62
62
  end
63
63
 
64
64
  describe "special cases" do
65
- it "should support valid a state name" do
66
- expect(Argument.aasm.states).to include(:invalid)
67
- expect(Argument.aasm.states).to include(:valid)
65
+ it "should support valid as state name" do
66
+ expect(ValidStateName.aasm.states).to include(:invalid)
67
+ expect(ValidStateName.aasm.states).to include(:valid)
68
68
 
69
- argument = Argument.new
69
+ argument = ValidStateName.new
70
70
  expect(argument.invalid?).to be_truthy
71
71
  expect(argument.aasm.current_state).to eq(:invalid)
72
72
 
@@ -85,13 +85,13 @@ end
85
85
 
86
86
  describe 'aasm.from_states_for_state' do
87
87
  it "should return all from states for a state" do
88
- expect(AuthMachine.aasm).to respond_to(:from_states_for_state)
89
- froms = AuthMachine.aasm.from_states_for_state(:active)
88
+ expect(ComplexExample.aasm).to respond_to(:from_states_for_state)
89
+ froms = ComplexExample.aasm.from_states_for_state(:active)
90
90
  [:pending, :passive, :suspended].each {|from| expect(froms).to include(from)}
91
91
  end
92
92
 
93
93
  it "should return from states for a state for a particular transition only" do
94
- froms = AuthMachine.aasm.from_states_for_state(:active, :transition => :unsuspend)
94
+ froms = ComplexExample.aasm.from_states_for_state(:active, :transition => :unsuspend)
95
95
  [:suspended].each {|from| expect(froms).to include(from)}
96
96
  end
97
97
  end
@@ -1,48 +1,10 @@
1
1
  require 'spec_helper'
2
2
  require 'active_record'
3
- require 'logger'
4
3
  require 'i18n'
5
4
 
6
5
  I18n.enforce_available_locales = false
7
6
  load_schema
8
7
 
9
- class LocalizerTestModel < ActiveRecord::Base
10
- include AASM
11
-
12
- attr_accessor :aasm_state
13
-
14
- aasm do
15
- state :opened, :initial => true
16
- state :closed
17
- event :close
18
- event :open
19
- end
20
- end
21
-
22
- describe 'localized state names' do
23
- before(:all) do
24
- I18n.load_path << 'spec/en.yml'
25
- I18n.default_locale = :en
26
- I18n.reload!
27
- end
28
-
29
- after(:all) do
30
- I18n.load_path.clear
31
- end
32
-
33
- it 'should localize' do
34
- state = LocalizerTestModel.aasm.states.detect {|s| s == :opened}
35
- expect(state.localized_name).to eq("It's open now!")
36
- expect(state.human_name).to eq("It's open now!")
37
- end
38
-
39
- it 'should use fallback' do
40
- state = LocalizerTestModel.aasm.states.detect {|s| s == :closed}
41
- expect(state.localized_name).to eq('Closed')
42
- expect(state.human_name).to eq('Closed')
43
- end
44
- end
45
-
46
8
  describe AASM::Localizer, "new style" do
47
9
  before(:all) do
48
10
  I18n.load_path << 'spec/en.yml'
@@ -1,10 +1,10 @@
1
1
  require 'active_record'
2
- require 'logger'
3
2
  require 'spec_helper'
4
-
3
+ Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each { |f| require File.expand_path(f) }
5
4
  load_schema
6
5
 
7
6
  # if you want to see the statements while running the spec enable the following line
7
+ # require 'logger'
8
8
  # ActiveRecord::Base.logger = Logger.new(STDERR)
9
9
 
10
10
  shared_examples_for "aasm model" do
@@ -321,6 +321,24 @@ describe "direct assignment" do
321
321
  expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
322
322
  expect(obj.aasm_state.to_sym).to eql :pending
323
323
  end
324
+
325
+ it 'can be turned off and on again' do
326
+ obj = NoDirectAssignment.create
327
+ expect(obj.aasm_state.to_sym).to eql :pending
328
+
329
+ expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
330
+ expect(obj.aasm_state.to_sym).to eql :pending
331
+
332
+ # allow it temporarily
333
+ NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
334
+ obj.aasm_state = :pending
335
+ expect(obj.aasm_state.to_sym).to eql :pending
336
+
337
+ # and forbid it again
338
+ NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = true
339
+ expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
340
+ expect(obj.aasm_state.to_sym).to eql :pending
341
+ end
324
342
  end # direct assignment
325
343
 
326
344
  describe 'initial states' do
@@ -436,7 +454,7 @@ describe 'transitions with persistence' do
436
454
  expect(validator).to be_running
437
455
  expect(validator.name).to eq("name changed")
438
456
 
439
- validator.sleep!
457
+ validator.sleep!("sleeper")
440
458
  expect(validator).to be_sleeping
441
459
  expect(validator.name).to eq("sleeper")
442
460
  end
@@ -1,58 +1,42 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Payment
4
- include AASM
5
- aasm do
6
- state :initialised, :initial => true
7
- state :filled_out
8
- state :authorised
9
-
10
- event :fill_out do
11
- transitions :from => :initialised, :to => :filled_out
12
- end
13
- event :authorise do
14
- transitions :from => :filled_out, :to => :authorised
15
- end
16
- end
17
- end
18
-
19
3
  describe 'state machine' do
20
- let(:payment) {Payment.new}
4
+ let(:simple) { SimpleExample.new }
21
5
 
22
6
  it 'starts with an initial state' do
23
- expect(payment.aasm.current_state).to eq(:initialised)
24
- expect(payment).to respond_to(:initialised?)
25
- expect(payment).to be_initialised
7
+ expect(simple.aasm.current_state).to eq(:initialised)
8
+ expect(simple).to respond_to(:initialised?)
9
+ expect(simple).to be_initialised
26
10
  end
27
11
 
28
12
  it 'allows transitions to other states' do
29
- expect(payment).to respond_to(:fill_out)
30
- expect(payment).to respond_to(:fill_out!)
31
- payment.fill_out!
32
- expect(payment).to respond_to(:filled_out?)
33
- expect(payment).to be_filled_out
34
-
35
- expect(payment).to respond_to(:authorise)
36
- expect(payment).to respond_to(:authorise!)
37
- payment.authorise
38
- expect(payment).to respond_to(:authorised?)
39
- expect(payment).to be_authorised
13
+ expect(simple).to respond_to(:fill_out)
14
+ expect(simple).to respond_to(:fill_out!)
15
+ simple.fill_out!
16
+ expect(simple).to respond_to(:filled_out?)
17
+ expect(simple).to be_filled_out
18
+
19
+ expect(simple).to respond_to(:authorise)
20
+ expect(simple).to respond_to(:authorise!)
21
+ simple.authorise
22
+ expect(simple).to respond_to(:authorised?)
23
+ expect(simple).to be_authorised
40
24
  end
41
25
 
42
26
  it 'denies transitions to other states' do
43
- expect {payment.authorise}.to raise_error(AASM::InvalidTransition)
44
- expect {payment.authorise!}.to raise_error(AASM::InvalidTransition)
45
- payment.fill_out
46
- expect {payment.fill_out}.to raise_error(AASM::InvalidTransition)
47
- expect {payment.fill_out!}.to raise_error(AASM::InvalidTransition)
48
- payment.authorise
49
- expect {payment.fill_out}.to raise_error(AASM::InvalidTransition)
50
- expect {payment.fill_out!}.to raise_error(AASM::InvalidTransition)
27
+ expect {simple.authorise}.to raise_error(AASM::InvalidTransition)
28
+ expect {simple.authorise!}.to raise_error(AASM::InvalidTransition)
29
+ simple.fill_out
30
+ expect {simple.fill_out}.to raise_error(AASM::InvalidTransition)
31
+ expect {simple.fill_out!}.to raise_error(AASM::InvalidTransition)
32
+ simple.authorise
33
+ expect {simple.fill_out}.to raise_error(AASM::InvalidTransition)
34
+ expect {simple.fill_out!}.to raise_error(AASM::InvalidTransition)
51
35
  end
52
36
 
53
37
  it 'defines constants for each state name' do
54
- expect(Payment::STATE_INITIALISED).to eq(:initialised)
55
- expect(Payment::STATE_FILLED_OUT).to eq(:filled_out)
56
- expect(Payment::STATE_AUTHORISED).to eq(:authorised)
38
+ expect(SimpleExample::STATE_INITIALISED).to eq(:initialised)
39
+ expect(SimpleExample::STATE_FILLED_OUT).to eq(:filled_out)
40
+ expect(SimpleExample::STATE_AUTHORISED).to eq(:authorised)
57
41
  end
58
42
  end
@@ -1,30 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'subclassing' do
4
- let(:son) {Son.new}
5
4
 
6
5
  it 'should have the parent states' do
7
- Foo.aasm.states.each do |state|
8
- expect(FooTwo.aasm.states).to include(state)
6
+ SuperClass.aasm.states.each do |state|
7
+ expect(SubClassWithMoreStates.aasm.states).to include(state)
9
8
  end
10
- expect(Baz.aasm.states).to eq(Bar.aasm.states)
9
+ expect(SubClass.aasm.states).to eq(SuperClass.aasm.states)
11
10
  end
12
11
 
13
12
  it 'should not add the child states to the parent machine' do
14
- expect(Foo.aasm.states).not_to include(:foo)
13
+ expect(SuperClass.aasm.states).not_to include(:foo)
15
14
  end
16
15
 
17
16
  it "should have the same events as its parent" do
18
- expect(Baz.aasm.events).to eq(Bar.aasm.events)
17
+ expect(SubClass.aasm.events).to eq(SuperClass.aasm.events)
19
18
  end
20
19
 
21
- it 'should know how to respond to `may_add_details?`' do
22
- expect(son.may_add_details?).to be_truthy
20
+ it 'should know how to respond to question methods' do
21
+ expect(SubClass.new.may_foo?).to be_truthy
23
22
  end
24
23
 
25
- it 'should not break if I call Son#update_state' do
24
+ it 'should not break if I call methods from super class' do
25
+ son = SubClass.new
26
26
  son.update_state
27
- expect(son.aasm.current_state).to eq(:pending_details_confirmation)
27
+ expect(son.aasm.current_state).to eq(:ended)
28
28
  end
29
29
 
30
30
  end