From 6396847041f78bcbf44178047a2d5f9ad662fb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20B=C3=B6ttger?= Date: Mon, 28 Sep 2015 21:19:02 +1300 Subject: [PATCH 1/2] provide after_all_transitions callback --- lib/aasm/base.rb | 4 ++++ lib/aasm/core/transition.rb | 1 + lib/aasm/state_machine.rb | 11 ++++++++++- spec/models/callbacks/basic.rb | 3 +++ spec/unit/callbacks_spec.rb | 1 + 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/aasm/base.rb b/lib/aasm/base.rb index d1fa2b7c..a2a9e11d 100644 --- a/lib/aasm/base.rb +++ b/lib/aasm/base.rb @@ -109,6 +109,10 @@ def #{name}(*args, &block) EORUBY end + def after_all_transitions(*callbacks, &block) + @state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block) + end + def states @state_machine.states end diff --git a/lib/aasm/core/transition.rb b/lib/aasm/core/transition.rb index d13fa9ab..779bb894 100644 --- a/lib/aasm/core/transition.rb +++ b/lib/aasm/core/transition.rb @@ -30,6 +30,7 @@ def allowed?(obj, *args) end def execute(obj, *args) + invoke_callbacks_compatible_with_guard(event.state_machine.global_callbacks[:after_all_transitions], obj, args) invoke_callbacks_compatible_with_guard(@after, obj, args) end diff --git a/lib/aasm/state_machine.rb b/lib/aasm/state_machine.rb index 001387a8..e511dfcc 100644 --- a/lib/aasm/state_machine.rb +++ b/lib/aasm/state_machine.rb @@ -10,12 +10,13 @@ def self.[]=(klass, machine) (@machines ||= {})[klass.to_s] = machine end - attr_accessor :states, :events, :initial_state, :config, :name + attr_accessor :states, :events, :initial_state, :config, :name, :global_callbacks def initialize(name) @initial_state = nil @states = [] @events = {} + @global_callbacks = {} @config = AASM::Configuration.new @name = name end @@ -40,6 +41,14 @@ def add_event(name, options, &block) @events[name] = AASM::Core::Event.new(name, self, options, &block) end + def add_global_callbacks(name, *callbacks, &block) + @global_callbacks[name] ||= [] + callbacks.each do |callback| + @global_callbacks[name] << callback + end + @global_callbacks[name] << block if block + end + private def set_initial_state(name, options) diff --git a/spec/models/callbacks/basic.rb b/spec/models/callbacks/basic.rb index 2cadac19..3b4e5142 100644 --- a/spec/models/callbacks/basic.rb +++ b/spec/models/callbacks/basic.rb @@ -18,6 +18,8 @@ def data end aasm do + after_all_transitions :after_all_transitions + state :open, :initial => true, :before_enter => :before_enter_open, :enter => :enter_open, @@ -68,6 +70,7 @@ def event_guard; log('event_guard'); !@fail_event_guard; e def transition_guard; log('transition_guard'); !@fail_transition_guard; end def after_transition; log('after_transition'); end + def after_all_transitions;log('after_all_transitions');end def before_event; log('before_event'); end def after_event; log('after_event'); end diff --git a/spec/unit/callbacks_spec.rb b/spec/unit/callbacks_spec.rb index 1cee0a67..bef7f2bd 100644 --- a/spec/unit/callbacks_spec.rb +++ b/spec/unit/callbacks_spec.rb @@ -17,6 +17,7 @@ expect(callback).to receive(:exit_open).once.ordered # expect(callback).to receive(:event_guard).once.ordered.and_return(true) # expect(callback).to receive(:transition_guard).once.ordered.and_return(true) + expect(callback).to receive(:after_all_transitions).once.ordered expect(callback).to receive(:after_transition).once.ordered expect(callback).to receive(:before_enter_closed).once.ordered expect(callback).to receive(:enter_closed).once.ordered From d741086b200b304c17b0d060e43bb7b2052b98e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20B=C3=B6ttger?= Date: Sat, 17 Oct 2015 12:17:57 +1300 Subject: [PATCH 2/2] add support for global transition callbacks --- CHANGELOG.md | 4 ++++ PLANNED_CHANGES.md | 19 ------------------- README.md | 12 ++++++++++-- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78305da3..65c3f220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 4.4.0 (not yet released) + + * add support global transation callbacks (see [issue #221](https://github.com/aasm/aasm/issues/221) and [issue #253](https://github.com/aasm/aasm/issues/253) for details) + ## 4.3.0 * add support for multiple state machines per class (see [issue #158](https://github.com/aasm/aasm/issues/158) and [issue #240](https://github.com/aasm/aasm/issues/240) for details) diff --git a/PLANNED_CHANGES.md b/PLANNED_CHANGES.md index 4dd23eaa..987885c6 100644 --- a/PLANNED_CHANGES.md +++ b/PLANNED_CHANGES.md @@ -7,24 +7,5 @@ # Currently working on - * support for global callbacks (see #221 and #253) - # Changes so far - -## version 4.3 - - * add support for multiple state machines per class - * class- and instance-level `aasm` methods accept a state machine selector - (aka the state machine _name_) - * if no selector/name is provided, `:default` will be used - * duplicate definitions of states and events will issue warnings - * check all tests - * _ActiveRecord_ - * _Mongoid_ - * _MongoMapper_ - * _Sequel_ - * what happen's if someone accesses `aasm`, but has defined a - state machine for `aasm(:my_name)`? - * documentation - * drop support for find_in_state, count_in_state, calculate_in_state, with_state_scope diff --git a/README.md b/README.md index b91827b8..732129ed 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ class Job state :sleeping, :initial => true, :before_enter => :do_something state :running + after_all_transitions :log_status_change + event :run, :after => :notify_somebody do before do log('Preparing to run') @@ -117,6 +119,10 @@ class Job end end + def log_status_change + puts "changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})" + end + def set_process(name) ... end @@ -145,6 +151,7 @@ begin transition guards old_state before_exit old_state exit + after_all_transitions transition after new_state before_enter new_state enter @@ -172,8 +179,9 @@ Note that when passing arguments to a state transition, the first argument must In case of an error during the event processing the error is rescued and passed to `:error` callback, which can handle it or re-raise it for further propagation. -During the transition's `:after` callback (and reliably only then) you can access the -originating state (the from-state) and the target state (the to state), like this: +During the transition's `:after` callback (and reliably only then, or in the global +`after_all_transitions` callback) you can access the originating state (the from-state) +and the target state (the to state), like this: ```ruby def set_process(name)