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 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