Skip to content

Commit

Permalink
Merge pull request #270 from aasm/support_for_global_callbacks
Browse files Browse the repository at this point in the history
Support for global transition callbacks
  • Loading branch information
alto committed Oct 23, 2015
2 parents 5e6a8f0 + d741086 commit 7586648
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 22 deletions.
4 changes: 4 additions & 0 deletions 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)
Expand Down
19 changes: 0 additions & 19 deletions PLANNED_CHANGES.md
Expand Up @@ -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
12 changes: 10 additions & 2 deletions README.md
Expand Up @@ -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')
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions lib/aasm/base.rb
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/aasm/core/transition.rb
Expand Up @@ -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

Expand Down
11 changes: 10 additions & 1 deletion lib/aasm/state_machine.rb
Expand Up @@ -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
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions spec/models/callbacks/basic.rb
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions spec/unit/callbacks_spec.rb
Expand Up @@ -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
Expand Down

0 comments on commit 7586648

Please sign in to comment.