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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +9 -0
- data/aasm.gemspec +1 -1
- data/lib/aasm/base.rb +12 -4
- data/lib/aasm/persistence/active_record_persistence.rb +1 -1
- data/lib/aasm/version.rb +1 -1
- data/spec/models/active_record/derivate_new_dsl.rb +3 -0
- data/spec/models/active_record/false_state.rb +17 -0
- data/spec/models/active_record/gate.rb +19 -0
- data/spec/models/active_record/localizer_test_model.rb +34 -0
- data/spec/models/active_record/no_direct_assignment.rb +10 -0
- data/spec/models/active_record/no_scope.rb +10 -0
- data/spec/models/active_record/persisted_state.rb +12 -0
- data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
- data/spec/models/active_record/reader.rb +7 -0
- data/spec/models/active_record/simple_new_dsl.rb +8 -0
- data/spec/models/active_record/thief.rb +14 -0
- data/spec/models/active_record/transient.rb +6 -0
- data/spec/models/active_record/with_enum.rb +19 -0
- data/spec/models/active_record/with_false_enum.rb +15 -0
- data/spec/models/active_record/with_true_enum.rb +19 -0
- data/spec/models/active_record/writer.rb +6 -0
- data/spec/models/{auth_machine.rb → complex_example.rb} +1 -1
- data/spec/models/default_state.rb +12 -0
- data/spec/models/initial_state_proc.rb +15 -0
- data/spec/models/{bar.rb → no_initial_state.rb} +1 -4
- data/spec/models/provided_state.rb +24 -0
- data/spec/models/simple_example.rb +15 -0
- data/spec/models/state_machine_with_failed_event.rb +12 -0
- data/spec/models/sub_class.rb +3 -0
- data/spec/models/sub_class_with_more_states.rb +7 -0
- data/spec/models/super_class.rb +18 -0
- data/spec/models/{argument.rb → valid_state_name.rb} +1 -1
- data/spec/models/validator.rb +4 -4
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/api_spec.rb +6 -1
- data/spec/unit/complex_example_spec.rb +2 -2
- data/spec/unit/event_naming_spec.rb +2 -15
- data/spec/unit/initial_state_spec.rb +3 -18
- data/spec/unit/inspection_spec.rb +7 -7
- data/spec/unit/localizer_spec.rb +0 -38
- data/spec/unit/persistence/active_record_persistence_spec.rb +21 -3
- data/spec/unit/simple_example_spec.rb +26 -42
- data/spec/unit/subclassing_spec.rb +10 -10
- metadata +60 -19
- data/spec/models/active_record/api.rb +0 -75
- data/spec/models/father.rb +0 -21
- data/spec/models/persistence.rb +0 -164
- 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
|
data/spec/models/validator.rb
CHANGED
@@ -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 =
|
32
|
+
def change_name_on_sleep name
|
33
|
+
self.name = name
|
34
34
|
save!
|
35
35
|
end
|
36
36
|
|
data/spec/spec_helper.rb
CHANGED
@@ -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'])
|
data/spec/unit/api_spec.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'models/
|
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) {
|
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) {
|
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) {
|
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(
|
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(
|
25
|
-
expect(
|
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
|
66
|
-
expect(
|
67
|
-
expect(
|
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 =
|
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(
|
89
|
-
froms =
|
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 =
|
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
|
data/spec/unit/localizer_spec.rb
CHANGED
@@ -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(:
|
4
|
+
let(:simple) { SimpleExample.new }
|
21
5
|
|
22
6
|
it 'starts with an initial state' do
|
23
|
-
expect(
|
24
|
-
expect(
|
25
|
-
expect(
|
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(
|
30
|
-
expect(
|
31
|
-
|
32
|
-
expect(
|
33
|
-
expect(
|
34
|
-
|
35
|
-
expect(
|
36
|
-
expect(
|
37
|
-
|
38
|
-
expect(
|
39
|
-
expect(
|
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 {
|
44
|
-
expect {
|
45
|
-
|
46
|
-
expect {
|
47
|
-
expect {
|
48
|
-
|
49
|
-
expect {
|
50
|
-
expect {
|
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(
|
55
|
-
expect(
|
56
|
-
expect(
|
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
|
-
|
8
|
-
expect(
|
6
|
+
SuperClass.aasm.states.each do |state|
|
7
|
+
expect(SubClassWithMoreStates.aasm.states).to include(state)
|
9
8
|
end
|
10
|
-
expect(
|
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(
|
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(
|
17
|
+
expect(SubClass.aasm.events).to eq(SuperClass.aasm.events)
|
19
18
|
end
|
20
19
|
|
21
|
-
it 'should know how to respond to
|
22
|
-
expect(
|
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
|
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(:
|
27
|
+
expect(son.aasm.current_state).to eq(:ended)
|
28
28
|
end
|
29
29
|
|
30
30
|
end
|