aasm 4.6.0 → 4.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +17 -0
- data/lib/aasm/aasm.rb +17 -1
- data/lib/aasm/base.rb +24 -0
- data/lib/aasm/core/event.rb +16 -4
- data/lib/aasm/core/state.rb +2 -2
- data/lib/aasm/core/transition.rb +2 -2
- data/lib/aasm/persistence/active_record_persistence.rb +18 -7
- data/lib/aasm/version.rb +1 -1
- data/spec/models/callbacks/basic.rb +29 -22
- data/spec/models/foo.rb +27 -13
- data/spec/models/state_machine_with_failed_event.rb +8 -0
- data/spec/models/validator.rb +36 -0
- data/spec/unit/callbacks_spec.rb +176 -31
- data/spec/unit/event_naming_spec.rb +6 -1
- data/spec/unit/persistence/active_record_persistence_spec.rb +65 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f758ead4b090ac11ffd00fd7b5a85b5a24dfa626
|
4
|
+
data.tar.gz: 7e2192786e067726f08fdae1f70341922edf63bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3df7b62a3328455e73356824ceca28663ea789963518730e731078f8d419dbcda1fc20dafe7697563e5e8da5a97fe1409ddda0366b48258c14c14e671b4b855
|
7
|
+
data.tar.gz: 2a001ca4ce4e3b8452396546201e2506fc467eb5b12ad24dfbd21ed420f20112df5b854e45e2c02211a463f3476ec4e0ce9a587936c082c0200ed4e5bebf6127
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 4.7.0
|
4
|
+
|
5
|
+
* fix: allow :send as event name (see [issue #257](https://github.com/aasm/aasm/issues/257) for details)
|
6
|
+
* add new callbacks: transactions, all events, ensure (see [issue #282](https://github.com/aasm/aasm/issues/282) for details, thanks to [@HoyaBoya](https://github.com/HoyaBoya))
|
7
|
+
|
3
8
|
## 4.6.0
|
4
9
|
|
5
10
|
* fix: make sure the column is actually present for _ActiveRecord_ enums (see [issue #265](https://github.com/aasm/aasm/issues/265) and [issue #152](https://github.com/aasm/aasm/issues/152) for details, thanks to [@anilmaurya](https://github.com/anilmaurya))
|
6
|
-
* add generators to configure active_record and mongoid after install (see [issue #261](https://github.com/aasm/aasm/issues/261) for details, thanks to [@anilmaurya](https://github.com/anilmaurya))
|
11
|
+
* add generators to configure active_record and mongoid after install (see [issue #261](https://github.com/aasm/aasm/issues/261) for details, thanks to [@anilmaurya](https://github.com/anilmaurya))
|
7
12
|
|
8
13
|
## 4.5.2
|
9
14
|
|
data/README.md
CHANGED
@@ -146,6 +146,7 @@ Here you can see a list of all possible callbacks, together with their order of
|
|
146
146
|
|
147
147
|
```ruby
|
148
148
|
begin
|
149
|
+
event before_all_events
|
149
150
|
event before
|
150
151
|
event guards
|
151
152
|
transition guards
|
@@ -160,8 +161,13 @@ begin
|
|
160
161
|
old_state after_exit
|
161
162
|
new_state after_enter
|
162
163
|
event after
|
164
|
+
event after_all_events
|
163
165
|
rescue
|
164
166
|
event error
|
167
|
+
event error_on_all_events
|
168
|
+
ensure
|
169
|
+
event ensure
|
170
|
+
event ensure_on_all_events
|
165
171
|
end
|
166
172
|
```
|
167
173
|
|
@@ -614,6 +620,17 @@ Since version *3.0.13* AASM supports ActiveRecord transactions. So whenever a tr
|
|
614
620
|
callback or the state update fails, all changes to any database record are rolled back.
|
615
621
|
Mongodb does not support transactions.
|
616
622
|
|
623
|
+
There are currently 3 transactional callbacks that can be handled on the event, and 2 transactional callbacks for all events.
|
624
|
+
|
625
|
+
```ruby
|
626
|
+
event before_all_transactions
|
627
|
+
event before_transaction
|
628
|
+
event aasm_fire_event (within transaction)
|
629
|
+
event after_commit (if event successful)
|
630
|
+
event after_transaction
|
631
|
+
event after_all_transactions
|
632
|
+
```
|
633
|
+
|
617
634
|
If you want to make sure a depending action happens only after the transaction is committed,
|
618
635
|
use the `after_commit` callback along with the auto-save (bang) methods, like this:
|
619
636
|
|
data/lib/aasm/aasm.rb
CHANGED
@@ -88,6 +88,12 @@ private
|
|
88
88
|
begin
|
89
89
|
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
90
90
|
|
91
|
+
event.fire_global_callbacks(
|
92
|
+
:before_all_events,
|
93
|
+
self,
|
94
|
+
*process_args(event, aasm(state_machine_name).current_state, *args)
|
95
|
+
)
|
96
|
+
|
91
97
|
# new event before callback
|
92
98
|
event.fire_callbacks(
|
93
99
|
:before,
|
@@ -110,7 +116,12 @@ private
|
|
110
116
|
aasm_failed(state_machine_name, event_name, old_state)
|
111
117
|
end
|
112
118
|
rescue StandardError => e
|
113
|
-
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
119
|
+
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
120
|
+
event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
121
|
+
raise(e)
|
122
|
+
ensure
|
123
|
+
event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
124
|
+
event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
114
125
|
end
|
115
126
|
end
|
116
127
|
|
@@ -147,6 +158,11 @@ private
|
|
147
158
|
self,
|
148
159
|
*process_args(event, old_state.name, *args)
|
149
160
|
)
|
161
|
+
event.fire_global_callbacks(
|
162
|
+
:after_all_events,
|
163
|
+
self,
|
164
|
+
*process_args(event, old_state.name, *args)
|
165
|
+
)
|
150
166
|
|
151
167
|
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
|
152
168
|
else
|
data/lib/aasm/base.rb
CHANGED
@@ -113,6 +113,30 @@ module AASM
|
|
113
113
|
@state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block)
|
114
114
|
end
|
115
115
|
|
116
|
+
def after_all_transactions(*callbacks, &block)
|
117
|
+
@state_machine.add_global_callbacks(:after_all_transactions, *callbacks, &block)
|
118
|
+
end
|
119
|
+
|
120
|
+
def before_all_transactions(*callbacks, &block)
|
121
|
+
@state_machine.add_global_callbacks(:before_all_transactions, *callbacks, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
def before_all_events(*callbacks, &block)
|
125
|
+
@state_machine.add_global_callbacks(:before_all_events, *callbacks, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def after_all_events(*callbacks, &block)
|
129
|
+
@state_machine.add_global_callbacks(:after_all_events, *callbacks, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
def error_on_all_events(*callbacks, &block)
|
133
|
+
@state_machine.add_global_callbacks(:error_on_all_events, *callbacks, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def ensure_on_all_events(*callbacks, &block)
|
137
|
+
@state_machine.add_global_callbacks(:ensure_on_all_events, *callbacks, &block)
|
138
|
+
end
|
139
|
+
|
116
140
|
def states
|
117
141
|
@state_machine.states
|
118
142
|
end
|
data/lib/aasm/core/event.rb
CHANGED
@@ -13,7 +13,16 @@ module AASM::Core
|
|
13
13
|
|
14
14
|
# from aasm4
|
15
15
|
@options = options # QUESTION: .dup ?
|
16
|
-
add_options_from_dsl(@options, [
|
16
|
+
add_options_from_dsl(@options, [
|
17
|
+
:after,
|
18
|
+
:after_commit,
|
19
|
+
:after_transaction,
|
20
|
+
:before,
|
21
|
+
:before_transaction,
|
22
|
+
:ensure,
|
23
|
+
:error,
|
24
|
+
:success,
|
25
|
+
], &block) if block
|
17
26
|
end
|
18
27
|
|
19
28
|
# a neutered version of fire - it doesn't actually fire the event, it just
|
@@ -43,6 +52,10 @@ module AASM::Core
|
|
43
52
|
@transitions.select { |t| t.to == state }
|
44
53
|
end
|
45
54
|
|
55
|
+
def fire_global_callbacks(callback_name, record, *args)
|
56
|
+
invoke_callbacks(state_machine.global_callbacks[callback_name], record, args)
|
57
|
+
end
|
58
|
+
|
46
59
|
def fire_callbacks(callback_name, record, *args)
|
47
60
|
# strip out the first element in args if it's a valid to_state
|
48
61
|
# #given where we're coming from, this condition implies args not empty
|
@@ -128,8 +141,8 @@ module AASM::Core
|
|
128
141
|
unless record.respond_to?(code, true)
|
129
142
|
raise NoMethodError.new("NoMethodError: undefined method `#{code}' for #{record.inspect}:#{record.class}")
|
130
143
|
end
|
131
|
-
arity = record.
|
132
|
-
record.
|
144
|
+
arity = record.__send__(:method, code.to_sym).arity
|
145
|
+
record.__send__(code, *(arity < 0 ? args : args[0...arity]))
|
133
146
|
true
|
134
147
|
|
135
148
|
when Proc
|
@@ -145,6 +158,5 @@ module AASM::Core
|
|
145
158
|
false
|
146
159
|
end
|
147
160
|
end
|
148
|
-
|
149
161
|
end
|
150
162
|
end # AASM
|
data/lib/aasm/core/state.rb
CHANGED
@@ -70,8 +70,8 @@ module AASM::Core
|
|
70
70
|
def _fire_callbacks(action, record, args)
|
71
71
|
case action
|
72
72
|
when Symbol, String
|
73
|
-
arity = record.
|
74
|
-
record.
|
73
|
+
arity = record.__send__(:method, action.to_sym).arity
|
74
|
+
record.__send__(action, *(arity < 0 ? args : args[0...arity]))
|
75
75
|
when Proc
|
76
76
|
arity = action.arity
|
77
77
|
action.call(record, *(arity < 0 ? args : args[0...arity]))
|
data/lib/aasm/core/transition.rb
CHANGED
@@ -52,8 +52,8 @@ module AASM::Core
|
|
52
52
|
|
53
53
|
case code
|
54
54
|
when Symbol, String
|
55
|
-
arity = record.
|
56
|
-
arity == 0 ? record.
|
55
|
+
arity = record.__send__(:method, code.to_sym).arity
|
56
|
+
arity == 0 ? record.__send__(code) : record.__send__(code, *args)
|
57
57
|
when Proc
|
58
58
|
code.parameters.size == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
|
59
59
|
when Array
|
@@ -129,11 +129,9 @@ module AASM
|
|
129
129
|
end
|
130
130
|
|
131
131
|
# Ensures that if the aasm_state column is nil and the record is new
|
132
|
-
# then the initial state gets populated
|
132
|
+
# then the initial state gets populated after initialization
|
133
133
|
#
|
134
134
|
# foo = Foo.new
|
135
|
-
# foo.aasm_state # => nil
|
136
|
-
# foo.valid?
|
137
135
|
# foo.aasm_state # => "open" (where :open is the initial state)
|
138
136
|
#
|
139
137
|
#
|
@@ -159,11 +157,24 @@ module AASM
|
|
159
157
|
end
|
160
158
|
|
161
159
|
def aasm_fire_event(state_machine_name, name, options, *args, &block)
|
162
|
-
|
160
|
+
event = self.class.aasm(state_machine_name).state_machine.events[name]
|
161
|
+
|
162
|
+
if options[:persist]
|
163
|
+
event.fire_callbacks(:before_transaction, self, *args)
|
164
|
+
event.fire_global_callbacks(:before_all_transactions, self, *args)
|
165
|
+
end
|
163
166
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
begin
|
168
|
+
success = options[:persist] ? self.class.transaction(:requires_new => requires_new?(state_machine_name)) { super } : super
|
169
|
+
if options[:persist] && success
|
170
|
+
event.fire_callbacks(:after_commit, self, *args)
|
171
|
+
event.fire_global_callbacks(:after_all_commits, self, *args)
|
172
|
+
end
|
173
|
+
ensure
|
174
|
+
if options[:persist]
|
175
|
+
event.fire_callbacks(:after_transaction, self, *args)
|
176
|
+
event.fire_global_callbacks(:after_all_transactions, self, *args)
|
177
|
+
end
|
167
178
|
end
|
168
179
|
|
169
180
|
success
|
data/lib/aasm/version.rb
CHANGED
@@ -18,7 +18,10 @@ module Callbacks
|
|
18
18
|
end
|
19
19
|
|
20
20
|
aasm do
|
21
|
-
|
21
|
+
before_all_events :before_all_events
|
22
|
+
after_all_events :after_all_events
|
23
|
+
ensure_on_all_events :ensure_on_all_events
|
24
|
+
after_all_transitions :after_all_transitions
|
22
25
|
|
23
26
|
state :open, :initial => true,
|
24
27
|
:before_enter => :before_enter_open,
|
@@ -36,11 +39,11 @@ module Callbacks
|
|
36
39
|
:exit => :exit_closed,
|
37
40
|
:after_exit => :after_exit_closed
|
38
41
|
|
39
|
-
event :close, :before => :before_event, :after => :after_event, :guard => :event_guard do
|
42
|
+
event :close, :before => :before_event, :after => :after_event, :guard => :event_guard, :ensure => :ensure_event do
|
40
43
|
transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :after_transition
|
41
44
|
end
|
42
45
|
|
43
|
-
event :open, :before => :before_event, :after => :after_event
|
46
|
+
event :open, :before => :before_event, :after => :after_event do
|
44
47
|
transitions :to => :open, :from => :closed
|
45
48
|
end
|
46
49
|
end
|
@@ -50,29 +53,33 @@ module Callbacks
|
|
50
53
|
puts text if @log
|
51
54
|
end
|
52
55
|
|
53
|
-
def aasm_write_state(*args);
|
56
|
+
def aasm_write_state(*args); log('aasm_write_state'); true; end
|
57
|
+
def before_enter_open; log('before_enter_open'); end
|
58
|
+
def enter_open; log('enter_open'); end
|
59
|
+
def before_exit_open; log('before_exit_open'); end
|
60
|
+
def after_enter_open; log('after_enter_open'); end
|
61
|
+
def exit_open; log('exit_open'); end
|
62
|
+
def after_exit_open; log('after_exit_open'); end
|
54
63
|
|
55
|
-
def
|
56
|
-
def
|
57
|
-
def
|
58
|
-
def
|
59
|
-
def
|
60
|
-
def
|
64
|
+
def before_enter_closed; log('before_enter_closed'); end
|
65
|
+
def enter_closed; log('enter_closed'); end
|
66
|
+
def before_exit_closed; log('before_exit_closed'); end
|
67
|
+
def exit_closed; log('exit_closed'); end
|
68
|
+
def after_enter_closed; log('after_enter_closed'); end
|
69
|
+
def after_exit_closed; log('after_exit_closed'); end
|
61
70
|
|
62
|
-
def
|
63
|
-
def
|
64
|
-
def before_exit_closed; log('before_exit_closed'); end
|
65
|
-
def exit_closed; log('exit_closed'); end
|
66
|
-
def after_enter_closed; log('after_enter_closed'); end
|
67
|
-
def after_exit_closed; log('after_exit_closed'); end
|
71
|
+
def event_guard; log('event_guard'); !@fail_event_guard; end
|
72
|
+
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
|
68
73
|
|
69
|
-
def
|
70
|
-
def
|
74
|
+
def after_transition; log('after_transition'); end
|
75
|
+
def after_all_transitions; log('after_all_transitions'); end
|
71
76
|
|
72
|
-
def
|
73
|
-
def
|
77
|
+
def before_all_events; log('before_all_events') end
|
78
|
+
def before_event; log('before_event'); end
|
79
|
+
def after_event; log('after_event'); end
|
80
|
+
def after_all_events; log('after_all_events'); end
|
74
81
|
|
75
|
-
def
|
76
|
-
def
|
82
|
+
def ensure_event; log('ensure'); end
|
83
|
+
def ensure_on_all_events; log('ensure'); end
|
77
84
|
end
|
78
85
|
end
|
data/spec/models/foo.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
module Fooable
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval do
|
4
|
+
aasm do
|
5
|
+
state :open, :initial => true, :before_exit => :before_exit
|
6
|
+
state :closed, :before_enter => :before_enter
|
7
|
+
state :final
|
8
|
+
|
9
|
+
event :close, :success => :success_callback do
|
10
|
+
transitions :from => [:open], :to => [:closed]
|
11
|
+
end
|
12
|
+
|
13
|
+
event :null do
|
14
|
+
transitions :from => [:open], :to => [:closed, :final], :guard => :always_false
|
15
|
+
end
|
16
|
+
end
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
@@ -23,10 +26,21 @@ class Foo
|
|
23
26
|
|
24
27
|
def before_enter
|
25
28
|
end
|
29
|
+
|
26
30
|
def before_exit
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
34
|
+
class Foo
|
35
|
+
include AASM
|
36
|
+
include Fooable
|
37
|
+
end
|
38
|
+
|
39
|
+
class FooGlobal
|
40
|
+
include AASM
|
41
|
+
include Fooable
|
42
|
+
end
|
43
|
+
|
30
44
|
class FooTwo < Foo
|
31
45
|
include AASM
|
32
46
|
aasm do
|
@@ -4,9 +4,17 @@ class StateMachineWithFailedEvent
|
|
4
4
|
aasm do
|
5
5
|
state :init, :initial => true
|
6
6
|
state :failed
|
7
|
+
state :sent
|
7
8
|
|
8
9
|
event :failed do
|
9
10
|
transitions :from => :init, :to => :failed
|
10
11
|
end
|
12
|
+
event :send, :before => :callback do
|
13
|
+
transitions :from => :init, :to => :sent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def callback
|
18
|
+
true
|
11
19
|
end
|
12
20
|
end
|
data/spec/models/validator.rb
CHANGED
@@ -1,23 +1,51 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
3
|
class Validator < ActiveRecord::Base
|
4
|
+
attr_accessor :after_all_transactions_performed,
|
5
|
+
:after_transaction_performed_on_fail,
|
6
|
+
:after_transaction_performed_on_run,
|
7
|
+
:before_all_transactions_performed,
|
8
|
+
:before_transaction_performed_on_fail,
|
9
|
+
:before_transaction_performed_on_run
|
4
10
|
|
5
11
|
include AASM
|
12
|
+
|
6
13
|
aasm :column => :status do
|
14
|
+
before_all_transactions :before_all_transactions
|
15
|
+
after_all_transactions :after_all_transactions
|
16
|
+
|
7
17
|
state :sleeping, :initial => true
|
8
18
|
state :running
|
9
19
|
state :failed, :after_enter => :fail
|
10
20
|
|
11
21
|
event :run, :after_commit => :change_name! do
|
22
|
+
after_transaction do
|
23
|
+
@after_transaction_performed_on_run = true
|
24
|
+
end
|
25
|
+
|
26
|
+
before_transaction do
|
27
|
+
@before_transaction_performed_on_run = true
|
28
|
+
end
|
29
|
+
|
12
30
|
transitions :to => :running, :from => :sleeping
|
13
31
|
end
|
32
|
+
|
14
33
|
event :sleep do
|
15
34
|
after_commit do |name|
|
16
35
|
change_name_on_sleep name
|
17
36
|
end
|
18
37
|
transitions :to => :sleeping, :from => :running
|
19
38
|
end
|
39
|
+
|
20
40
|
event :fail do
|
41
|
+
after_transaction do
|
42
|
+
@after_transaction_performed_on_fail = true
|
43
|
+
end
|
44
|
+
|
45
|
+
before_transaction do
|
46
|
+
@before_transaction_performed_on_fail = true
|
47
|
+
end
|
48
|
+
|
21
49
|
transitions :to => :failed, :from => [:sleeping, :running]
|
22
50
|
end
|
23
51
|
end
|
@@ -37,6 +65,14 @@ class Validator < ActiveRecord::Base
|
|
37
65
|
def fail
|
38
66
|
raise StandardError.new('failed on purpose')
|
39
67
|
end
|
68
|
+
|
69
|
+
def after_all_transactions
|
70
|
+
@after_all_transactions_performed = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def before_all_transactions
|
74
|
+
@before_all_transactions_performed = true
|
75
|
+
end
|
40
76
|
end
|
41
77
|
|
42
78
|
class MultipleValidator < ActiveRecord::Base
|
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -1,6 +1,65 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
Dir[File.dirname(__FILE__) + "/../models/callbacks/*.rb"].sort.each { |f| require File.expand_path(f) }
|
3
3
|
|
4
|
+
shared_examples 'an implemented callback that accepts error' do
|
5
|
+
context 'with callback defined' do
|
6
|
+
it "should run error_callback if an exception is raised" do
|
7
|
+
aasm_model.class.send(:define_method, callback_name) do |e|
|
8
|
+
@data = [e]
|
9
|
+
end
|
10
|
+
|
11
|
+
allow(aasm_model).to receive(:before_enter).and_raise(e = StandardError.new)
|
12
|
+
|
13
|
+
aasm_model.safe_close!
|
14
|
+
expect(aasm_model.data).to eql [e]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should run error_callback without parameters if callback does not support any" do
|
18
|
+
aasm_model.class.send(:define_method, callback_name) do |e|
|
19
|
+
@data = []
|
20
|
+
end
|
21
|
+
|
22
|
+
allow(aasm_model).to receive(:before_enter).and_raise(e = StandardError.new)
|
23
|
+
|
24
|
+
aasm_model.safe_close!('arg1', 'arg2')
|
25
|
+
expect(aasm_model.data).to eql []
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should run error_callback with parameters if callback supports them" do
|
29
|
+
aasm_model.class.send(:define_method, callback_name) do |e, arg1, arg2|
|
30
|
+
@data = [arg1, arg2]
|
31
|
+
end
|
32
|
+
|
33
|
+
allow(aasm_model).to receive(:before_enter).and_raise(e = StandardError.new)
|
34
|
+
|
35
|
+
aasm_model.safe_close!('arg1', 'arg2')
|
36
|
+
expect(aasm_model.data).to eql ['arg1', 'arg2']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
shared_examples 'an implemented callback' do
|
42
|
+
context 'with callback defined' do
|
43
|
+
it 'should run callback without parameters if callback does not support any' do
|
44
|
+
aasm_model.class.send(:define_method, callback_name) do
|
45
|
+
@data = ['callback-was-called']
|
46
|
+
end
|
47
|
+
|
48
|
+
aasm_model.safe_close!
|
49
|
+
expect(aasm_model.data).to eql ['callback-was-called']
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should run callback with parameters if callback supports them' do
|
53
|
+
aasm_model.class.send(:define_method, callback_name) do |arg1, arg2|
|
54
|
+
@data = [arg1, arg2]
|
55
|
+
end
|
56
|
+
|
57
|
+
aasm_model.safe_close!('arg1', 'arg2')
|
58
|
+
expect(aasm_model.data).to eql ['arg1', 'arg2']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
4
63
|
describe 'callbacks for the new DSL' do
|
5
64
|
|
6
65
|
it "be called in order" do
|
@@ -10,6 +69,7 @@ describe 'callbacks for the new DSL' do
|
|
10
69
|
callback.aasm.current_state
|
11
70
|
|
12
71
|
unless show_debug_log
|
72
|
+
expect(callback).to receive(:before_all_events).once.ordered
|
13
73
|
expect(callback).to receive(:before_event).once.ordered
|
14
74
|
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
15
75
|
expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
|
@@ -25,6 +85,9 @@ describe 'callbacks for the new DSL' do
|
|
25
85
|
expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
|
26
86
|
expect(callback).to receive(:after_enter_closed).once.ordered
|
27
87
|
expect(callback).to receive(:after_event).once.ordered
|
88
|
+
expect(callback).to receive(:after_all_events).once.ordered
|
89
|
+
expect(callback).to receive(:ensure_event).once.ordered
|
90
|
+
expect(callback).to receive(:ensure_on_all_events).once.ordered
|
28
91
|
end
|
29
92
|
|
30
93
|
# puts "------- close!"
|
@@ -35,11 +98,13 @@ describe 'callbacks for the new DSL' do
|
|
35
98
|
callback = Callbacks::Basic.new(:log => false)
|
36
99
|
callback.aasm.current_state
|
37
100
|
|
101
|
+
expect(callback).to receive(:before_all_events).once.ordered
|
38
102
|
expect(callback).to receive(:before_event).once.ordered
|
39
103
|
expect(callback).to receive(:event_guard).once.ordered.and_return(false)
|
40
104
|
expect(callback).to_not receive(:transition_guard)
|
41
105
|
expect(callback).to_not receive(:before_exit_open)
|
42
106
|
expect(callback).to_not receive(:exit_open)
|
107
|
+
expect(callback).to_not receive(:after_all_transitions)
|
43
108
|
expect(callback).to_not receive(:after_transition)
|
44
109
|
expect(callback).to_not receive(:before_enter_closed)
|
45
110
|
expect(callback).to_not receive(:enter_closed)
|
@@ -47,6 +112,9 @@ describe 'callbacks for the new DSL' do
|
|
47
112
|
expect(callback).to_not receive(:after_exit_open)
|
48
113
|
expect(callback).to_not receive(:after_enter_closed)
|
49
114
|
expect(callback).to_not receive(:after_event)
|
115
|
+
expect(callback).to_not receive(:after_all_events)
|
116
|
+
expect(callback).to receive(:ensure_event).once.ordered
|
117
|
+
expect(callback).to receive(:ensure_on_all_events).once.ordered
|
50
118
|
|
51
119
|
expect {
|
52
120
|
callback.close!
|
@@ -72,11 +140,13 @@ describe 'callbacks for the new DSL' do
|
|
72
140
|
callback.aasm.current_state
|
73
141
|
|
74
142
|
unless show_debug_log
|
143
|
+
expect(callback).to receive(:before_all_events).once.ordered
|
75
144
|
expect(callback).to receive(:before_event).once.ordered
|
76
145
|
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
77
146
|
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
|
78
147
|
expect(callback).to_not receive(:before_exit_open)
|
79
148
|
expect(callback).to_not receive(:exit_open)
|
149
|
+
expect(callback).to_not receive(:after_all_transitions)
|
80
150
|
expect(callback).to_not receive(:after_transition)
|
81
151
|
expect(callback).to_not receive(:before_enter_closed)
|
82
152
|
expect(callback).to_not receive(:enter_closed)
|
@@ -84,6 +154,9 @@ describe 'callbacks for the new DSL' do
|
|
84
154
|
expect(callback).to_not receive(:after_exit_open)
|
85
155
|
expect(callback).to_not receive(:after_enter_closed)
|
86
156
|
expect(callback).to_not receive(:after_event)
|
157
|
+
expect(callback).to_not receive(:after_all_events)
|
158
|
+
expect(callback).to receive(:ensure_event).once.ordered
|
159
|
+
expect(callback).to receive(:ensure_on_all_events).once.ordered
|
87
160
|
end
|
88
161
|
|
89
162
|
expect {
|
@@ -201,49 +274,50 @@ describe 'event callbacks' do
|
|
201
274
|
@foo = Foo.new
|
202
275
|
end
|
203
276
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
|
211
|
-
|
212
|
-
@foo.safe_close!
|
213
|
-
expect(@foo.data).to eql [e]
|
214
|
-
end
|
277
|
+
it_behaves_like 'an implemented callback that accepts error' do
|
278
|
+
let(:aasm_model) { @foo }
|
279
|
+
let(:callback_name) { :error_callback }
|
280
|
+
end
|
215
281
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
282
|
+
it "should raise NoMethodError if exception is raised and error_callback is declared but not defined" do
|
283
|
+
allow(@foo).to receive(:before_enter).and_raise(StandardError)
|
284
|
+
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
285
|
+
end
|
220
286
|
|
221
|
-
|
287
|
+
it "should propagate an error if no error callback is declared" do
|
288
|
+
allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
|
289
|
+
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
|
290
|
+
end
|
291
|
+
end
|
222
292
|
|
223
|
-
|
224
|
-
|
225
|
-
|
293
|
+
describe 'with an ensure callback defined' do
|
294
|
+
before do
|
295
|
+
class Foo
|
296
|
+
# this hack is needed to allow testing of parameters, since RSpec
|
297
|
+
# destroys a method's arity when mocked
|
298
|
+
attr_accessor :data
|
226
299
|
|
227
|
-
|
228
|
-
|
229
|
-
|
300
|
+
aasm do
|
301
|
+
event :safe_close, :success => :success_callback, :ensure => :ensure_callback do
|
302
|
+
transitions :to => :closed, :from => [:open]
|
303
|
+
end
|
230
304
|
end
|
305
|
+
end
|
231
306
|
|
232
|
-
|
307
|
+
@foo = Foo.new
|
308
|
+
end
|
233
309
|
|
234
|
-
|
235
|
-
|
236
|
-
|
310
|
+
it_behaves_like 'an implemented callback' do
|
311
|
+
let(:aasm_model) { @foo }
|
312
|
+
let(:callback_name) { :ensure_callback }
|
237
313
|
end
|
238
314
|
|
239
|
-
it "should raise NoMethodError if
|
240
|
-
allow(@foo).to receive(:before_enter).and_raise(StandardError)
|
315
|
+
it "should raise NoMethodError if ensure_callback is declared but not defined" do
|
241
316
|
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
242
317
|
end
|
243
318
|
|
244
|
-
it "should
|
245
|
-
|
246
|
-
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
|
319
|
+
it "should not raise any error if no ensure_callback is declared" do
|
320
|
+
expect{@foo.close!}.to_not raise_error
|
247
321
|
end
|
248
322
|
end
|
249
323
|
|
@@ -292,5 +366,76 @@ describe 'event callbacks' do
|
|
292
366
|
@foo.close!
|
293
367
|
end
|
294
368
|
end
|
369
|
+
end
|
370
|
+
|
371
|
+
describe 'global error_on_all_events_callback callbacks' do
|
372
|
+
describe "with an error_on_all_events" do
|
373
|
+
before do
|
374
|
+
class FooGlobal
|
375
|
+
# this hack is needed to allow testing of parameters, since RSpec
|
376
|
+
# destroys a method's arity when mocked
|
377
|
+
attr_accessor :data
|
378
|
+
|
379
|
+
aasm do
|
380
|
+
error_on_all_events :error_on_all_events_callback
|
381
|
+
|
382
|
+
event :safe_close do
|
383
|
+
transitions :to => :closed, :from => [:open]
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
@foo = FooGlobal.new
|
389
|
+
end
|
390
|
+
|
391
|
+
it_behaves_like 'an implemented callback that accepts error' do
|
392
|
+
let(:aasm_model) { @foo }
|
393
|
+
let(:callback_name) { :error_on_all_events_callback }
|
394
|
+
end
|
395
|
+
|
396
|
+
it "should raise NoMethodError if exception is raised and error_callback is declared but not defined" do
|
397
|
+
allow(@foo).to receive(:before_enter).and_raise(StandardError)
|
398
|
+
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
399
|
+
end
|
295
400
|
|
401
|
+
it "should raise NoMethodError if no error callback is declared" do
|
402
|
+
allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
|
403
|
+
expect{@foo.close!}.to raise_error(NoMethodError)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'global ensure_on_all_events_callback callbacks' do
|
409
|
+
describe "with an ensure_on_all_events" do
|
410
|
+
before do
|
411
|
+
class FooGlobal
|
412
|
+
# this hack is needed to allow testing of parameters, since RSpec
|
413
|
+
# destroys a method's arity when mocked
|
414
|
+
attr_accessor :data
|
415
|
+
|
416
|
+
aasm do
|
417
|
+
ensure_on_all_events :ensure_on_all_events_callback
|
418
|
+
|
419
|
+
event :safe_close do
|
420
|
+
transitions :to => :closed, :from => [:open]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
@foo = FooGlobal.new
|
426
|
+
end
|
427
|
+
|
428
|
+
it_behaves_like 'an implemented callback' do
|
429
|
+
let(:aasm_model) { @foo }
|
430
|
+
let(:callback_name) { :ensure_on_all_events_callback }
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should raise NoMethodError if ensure_on_all_events callback is declared but not defined" do
|
434
|
+
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should raise NoMethodError if no ensure_on_all_events callback is declared" do
|
438
|
+
expect{@foo.close!}.to raise_error(NoMethodError)
|
439
|
+
end
|
440
|
+
end
|
296
441
|
end
|
@@ -5,7 +5,12 @@ describe "event naming" do
|
|
5
5
|
|
6
6
|
it "allows an event of failed without blowing the stack aka stack level too deep" do
|
7
7
|
state_machine.failed
|
8
|
-
|
9
8
|
expect { state_machine.failed }.to raise_error(AASM::InvalidTransition)
|
10
9
|
end
|
10
|
+
|
11
|
+
it "allows send as event name" do
|
12
|
+
expect(state_machine.aasm.current_state).to eq :init
|
13
|
+
state_machine.send
|
14
|
+
expect(state_machine.aasm.current_state).to eq :sent
|
15
|
+
end
|
11
16
|
end
|
@@ -494,7 +494,72 @@ describe 'transitions with persistence' do
|
|
494
494
|
expect(validator).to be_running
|
495
495
|
expect(validator.name).to eq("name")
|
496
496
|
end
|
497
|
+
end
|
497
498
|
|
499
|
+
describe 'before and after transaction callbacks' do
|
500
|
+
[:after, :before].each do |event_type|
|
501
|
+
describe "#{event_type}_transaction callback" do
|
502
|
+
it "should fire :#{event_type}_transaction if transaction was successful" do
|
503
|
+
validator = Validator.create(:name => 'name')
|
504
|
+
expect(validator).to be_sleeping
|
505
|
+
|
506
|
+
expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
|
507
|
+
expect(validator).to be_running
|
508
|
+
end
|
509
|
+
|
510
|
+
it "should fire :#{event_type}_transaction if transaction failed" do
|
511
|
+
validator = Validator.create(:name => 'name')
|
512
|
+
expect do
|
513
|
+
begin
|
514
|
+
validator.fail!
|
515
|
+
rescue => ignored
|
516
|
+
end
|
517
|
+
end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
|
518
|
+
expect(validator).to_not be_running
|
519
|
+
end
|
520
|
+
|
521
|
+
it "should not fire :#{event_type}_transaction if not saving" do
|
522
|
+
validator = Validator.create(:name => 'name')
|
523
|
+
expect(validator).to be_sleeping
|
524
|
+
expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
|
525
|
+
expect(validator).to be_running
|
526
|
+
expect(validator.name).to eq("name")
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
describe 'before and after all transactions callbacks' do
|
533
|
+
[:after, :before].each do |event_type|
|
534
|
+
describe "#{event_type}_all_transactions callback" do
|
535
|
+
it "should fire :#{event_type}_all_transactions if transaction was successful" do
|
536
|
+
validator = Validator.create(:name => 'name')
|
537
|
+
expect(validator).to be_sleeping
|
538
|
+
|
539
|
+
expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
|
540
|
+
expect(validator).to be_running
|
541
|
+
end
|
542
|
+
|
543
|
+
it "should fire :#{event_type}_all_transactions if transaction failed" do
|
544
|
+
validator = Validator.create(:name => 'name')
|
545
|
+
expect do
|
546
|
+
begin
|
547
|
+
validator.fail!
|
548
|
+
rescue => ignored
|
549
|
+
end
|
550
|
+
end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
|
551
|
+
expect(validator).to_not be_running
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should not fire :#{event_type}_all_transactions if not saving" do
|
555
|
+
validator = Validator.create(:name => 'name')
|
556
|
+
expect(validator).to be_sleeping
|
557
|
+
expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
|
558
|
+
expect(validator).to be_running
|
559
|
+
expect(validator.name).to eq("name")
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
498
563
|
end
|
499
564
|
|
500
565
|
context "when not persisting" do
|