AASM allows before: / after: on an event, but not on a single transition inside that event when the event has multiple possible destinations. That becomes a problem when one event fans out to several states and only one path should run certain side effects.
Example:
event :approve do
transitions from: :pending, to: :active, if: :prepaid?
transitions from: :pending, to: :awaiting_payment
end
We want finalize_activation to run only when going to :active.
If we do:
event :approve, before: :finalize_activation do
# ...
end
finalize_activation also runs when the record goes to :awaiting_payment, which is wrong.
Event-level before: is fine when every transition shares the same kind of outcome (e.g. pay always ends in :active). It doesn’t work cleanly for branching events.
Describe the solution you'd like
Allow callbacks on the transition itself:
event :approve do
transitions from: :pending, to: :active,
if: :prepaid?,
before: :finalize_activation,
after: :notify_activated
transitions from: :pending, to: :awaiting_payment
end
Expected behavior:
- Transition callbacks run only for the transition that was selected.
- Document how they compose with event-level callbacks (e.g. event before → transition before → change state → transition after → event after).
Describe alternatives you've considered
-
Event-level before + condition inside the callback
e.g. finalize_activation if prepaid?: duplicates guards and breaks easily if transition order changes.
-
Use transition after: as a workaround
Often works, but it’s not a real before, (runs after the state change).
-
Split into separate events
e.g. approve_prepaid vs approve: This avoids the issue but duplicates events and call sites.
Additional context
This comes up whenever an event is a single user/system action (“approve”, “submit”, “complete”) but business rules send the record down different paths with different side effects. Transition-scoped callbacks would make that model natural without splitting events or overloading event-level hooks.
AASM allows before: / after: on an event, but not on a single transition inside that event when the event has multiple possible destinations. That becomes a problem when one event fans out to several states and only one path should run certain side effects.
Example:
We want finalize_activation to run only when going to :active.
If we do:
finalize_activation also runs when the record goes to :awaiting_payment, which is wrong.
Event-level before: is fine when every transition shares the same kind of outcome (e.g. pay always ends in :active). It doesn’t work cleanly for branching events.
Describe the solution you'd like
Allow callbacks on the transition itself:
Expected behavior:
Describe alternatives you've considered
Event-level before + condition inside the callback
e.g. finalize_activation if prepaid?: duplicates guards and breaks easily if transition order changes.
Use transition after: as a workaround
Often works, but it’s not a real
before, (runs after the state change).Split into separate events
e.g. approve_prepaid vs approve: This avoids the issue but duplicates events and call sites.
Additional context
This comes up whenever an event is a single user/system action (“approve”, “submit”, “complete”) but business rules send the record down different paths with different side effects. Transition-scoped callbacks would make that model natural without splitting events or overloading event-level hooks.