aasm 4.6.0 → 4.7.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/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
|