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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b8d8a7ce2210a49761c9f1c622393a1bc453452
4
- data.tar.gz: 1239b8468c521eb11bdbfef04d58540a6d1e5b72
3
+ metadata.gz: f758ead4b090ac11ffd00fd7b5a85b5a24dfa626
4
+ data.tar.gz: 7e2192786e067726f08fdae1f70341922edf63bb
5
5
  SHA512:
6
- metadata.gz: 7b8f2affa363cce45636f684c24e2f72d71cb0a5af4fce0303894a77eb9db7fb37f08442c4b4b1955d7aaae042225ec4b3e52b27ffafe5cbb0c765c15498cf65
7
- data.tar.gz: d9769d7cfd98b28e98f8a79c9b0b823cabec8b721bf5bdfaaae3dac37ca2b6127c38b42d427ed2fd451a67987c2371e36f4b46df09e0b4042fb1abb2c44320cd
6
+ metadata.gz: f3df7b62a3328455e73356824ceca28663ea789963518730e731078f8d419dbcda1fc20dafe7697563e5e8da5a97fe1409ddda0366b48258c14c14e671b4b855
7
+ data.tar.gz: 2a001ca4ce4e3b8452396546201e2506fc467eb5b12ad24dfbd21ed420f20112df5b854e45e2c02211a463f3476ec4e0ce9a587936c082c0200ed4e5bebf6127
@@ -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
 
@@ -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)) || raise(e)
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
@@ -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
@@ -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, [:after, :before, :error, :success, :after_commit], &block) if block
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.send(:method, code.to_sym).arity
132
- record.send(code, *(arity < 0 ? args : args[0...arity]))
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
@@ -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.send(:method, action.to_sym).arity
74
- record.send(action, *(arity < 0 ? args : args[0...arity]))
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]))
@@ -52,8 +52,8 @@ module AASM::Core
52
52
 
53
53
  case code
54
54
  when Symbol, String
55
- arity = record.send(:method, code.to_sym).arity
56
- arity == 0 ? record.send(code) : record.send(code, *args)
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 before validation on create
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
- success = options[:persist] ? self.class.transaction(:requires_new => requires_new?(state_machine_name)) { super } : super
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
- if success && options[:persist]
165
- event = self.class.aasm(state_machine_name).state_machine.events[name]
166
- event.fire_callbacks(:after_commit, self, *args)
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
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "4.6.0"
2
+ VERSION = "4.7.0"
3
3
  end
@@ -18,7 +18,10 @@ module Callbacks
18
18
  end
19
19
 
20
20
  aasm do
21
- after_all_transitions :after_all_transitions
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 do
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); log('aasm_write_state'); true; end
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 before_enter_open; log('before_enter_open'); end
56
- def enter_open; log('enter_open'); end
57
- def before_exit_open; log('before_exit_open'); end
58
- def after_enter_open; log('after_enter_open'); end
59
- def exit_open; log('exit_open'); end
60
- def after_exit_open; log('after_exit_open'); end
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 before_enter_closed; log('before_enter_closed'); end
63
- def enter_closed; log('enter_closed'); end
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 event_guard; log('event_guard'); !@fail_event_guard; end
70
- def transition_guard; log('transition_guard'); !@fail_transition_guard; end
74
+ def after_transition; log('after_transition'); end
75
+ def after_all_transitions; log('after_all_transitions'); end
71
76
 
72
- def after_transition; log('after_transition'); end
73
- def after_all_transitions;log('after_all_transitions');end
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 before_event; log('before_event'); end
76
- def after_event; log('after_event'); end
82
+ def ensure_event; log('ensure'); end
83
+ def ensure_on_all_events; log('ensure'); end
77
84
  end
78
85
  end
@@ -1,16 +1,19 @@
1
- class Foo
2
- include AASM
3
- aasm do
4
- state :open, :initial => true, :before_exit => :before_exit
5
- state :closed, :before_enter => :before_enter
6
- state :final
7
-
8
- event :close, :success => :success_callback do
9
- transitions :from => [:open], :to => [:closed]
10
- end
11
-
12
- event :null do
13
- transitions :from => [:open], :to => [:closed, :final], :guard => :always_false
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
@@ -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
@@ -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
- context "error_callback defined" do
205
- it "should run error_callback if an exception is raised" do
206
- def @foo.error_callback(e)
207
- @data = [e]
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
- it "should run error_callback without parameters if callback does not support any" do
217
- def @foo.error_callback(e)
218
- @data = []
219
- end
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
- allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
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
- @foo.safe_close!('arg1', 'arg2')
224
- expect(@foo.data).to eql []
225
- end
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
- it "should run error_callback with parameters if callback supports them" do
228
- def @foo.error_callback(e, arg1, arg2)
229
- @data = [arg1, arg2]
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
- allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
307
+ @foo = Foo.new
308
+ end
233
309
 
234
- @foo.safe_close!('arg1', 'arg2')
235
- expect(@foo.data).to eql ['arg1', 'arg2']
236
- end
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 exception is raised and error_callback is declared but not defined" do
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 propagate an error if no error callback is declared" do
245
- allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.0
4
+ version: 4.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Barron