Skip to content

Add bang method variants for flow actions#63

Merged
saturnflyer merged 1 commit intomainfrom
bang-method-variants
Feb 17, 2026
Merged

Add bang method variants for flow actions#63
saturnflyer merged 1 commit intomainfrom
bang-method-variants

Conversation

@saturnflyer
Copy link
Member

Summary

  • Add Circulator::InvalidTransition base exception and auto-define HostClass::InvalidTransition subclass when a flow is declared, so host apps can rescue their own namespaced exception without coupling to Circulator
  • Generate bang methods (e.g. status_approve!) that raise InvalidTransition with distinct messages for missing transitions vs guard rejections
  • Add flow! instance method and variant: keyword to flow for extensible dispatch (e.g. future flow? / variant: :check)
  • Unknown variant: values raise ArgumentError

Examples

class Widget
  extend Circulator

  flow :status do
    state :pending do
      action :approve, to: :approved, allow_if: :admin?
    end
  end
end

widget.status_approve   # => nil (guard rejected, no raise)
widget.status_approve!  # => raises Widget::InvalidTransition

# Rescue by host namespace or gem namespace
rescue Widget::InvalidTransition    # works
rescue Circulator::InvalidTransition # also works

# Via flow instance method
widget.flow!(:approve, :status)
widget.flow(:approve, :status, variant: :bang)

Test plan

  • Successful transitions with bang methods
  • Raises on missing transition with descriptive message
  • Raises on guard rejection with distinct message
  • Exception hierarchy (host < Circulator < StandardError)
  • Self-transitions do not raise
  • Around blocks (success, failure, intentional skip)
  • Callable to: lambdas
  • Arguments passthrough
  • Model-based flow_target: flows
  • flow! and variant: :bang
  • Invalid variant: raises ArgumentError
  • Non-bang methods unchanged
  • Extension system generates bang methods
  • Full suite: 358 runs, 1055 assertions, 0 failures
  • Standardrb clean

Define Circulator::InvalidTransition and auto-define a host-namespaced
subclass (e.g. Widget::InvalidTransition) when a flow is declared. Bang
methods (status_approve!) raise InvalidTransition when no transition
exists or a guard rejects, with distinct messages for each case.
Non-bang methods remain unchanged.

Add flow! instance method and variant: keyword to flow so that callers
can select method variants (e.g. flow(:approve, :status, variant: :bang)).
Unknown variants raise ArgumentError.

FLOW_VARIANTS drives method definition: each key maps to a
define_<variant>_flow_method, so adding a variant without implementing
its define method raises NoMethodError at class load time.

Added: bang method variants for flow actions
Added: Circulator::InvalidTransition exception class
Added: host-namespaced InvalidTransition subclass per class with flows
Added: flow! instance method and variant: keyword on flow
Added: FLOW_VARIANTS registry that drives method definition
@saturnflyer saturnflyer marked this pull request as ready for review February 17, 2026 14:54
@saturnflyer saturnflyer requested a review from a team as a code owner February 17, 2026 14:54
Copy link

@jeffdlange jeffdlange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good explanation and example in the main description. Thank you!

@saturnflyer saturnflyer merged commit 9a8f850 into main Feb 17, 2026
4 checks passed
@saturnflyer saturnflyer deleted the bang-method-variants branch February 17, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants