aasm-coder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AASM Coder

AASM 状态机实现

State machine patterns for managing workflow states in Rails.
在Rails中管理工作流状态的状态机模式。

Setup

环境搭建

ruby
undefined
ruby
undefined

Gemfile

Gemfile

gem "aasm", "~> 5.5"
undefined
gem "aasm", "~> 5.5"
undefined

Basic State Machine

基础状态机

ruby
class Order < ApplicationRecord
  include AASM

  aasm column: :status do
    state :pending, initial: true
    state :paid
    state :processing
    state :shipped
    state :cancelled

    event :pay do
      transitions from: :pending, to: :paid

      after do
        OrderMailer.payment_received(self).deliver_later
      end
    end

    event :process do
      transitions from: :paid, to: :processing
    end

    event :ship do
      transitions from: :processing, to: :shipped
    end

    event :cancel do
      transitions from: [:pending, :paid], to: :cancelled

      before do
        refund_payment if paid?
      end
    end
  end
end
ruby
class Order < ApplicationRecord
  include AASM

  aasm column: :status do
    state :pending, initial: true
    state :paid
    state :processing
    state :shipped
    state :cancelled

    event :pay do
      transitions from: :pending, to: :paid

      after do
        OrderMailer.payment_received(self).deliver_later
      end
    end

    event :process do
      transitions from: :paid, to: :processing
    end

    event :ship do
      transitions from: :processing, to: :shipped
    end

    event :cancel do
      transitions from: [:pending, :paid], to: :cancelled

      before do
        refund_payment if paid?
      end
    end
  end
end

Usage

使用方法

ruby
order = Order.create!
order.pending?      # => true
order.may_pay?      # => true
order.pay!          # Transition + callbacks
order.paid?         # => true

order.may_ship?     # => false (must process first)
order.aasm.events   # => [:process, :cancel]
ruby
order = Order.create!
order.pending?      # => true
order.may_pay?      # => true
order.pay!          # Transition + callbacks
order.paid?         # => true

order.may_ship?     # => false (must process first)
order.aasm.events   # => [:process, :cancel]

Scopes created automatically

Scopes created automatically

Order.pending Order.paid.where(user: current_user)
undefined
Order.pending Order.paid.where(user: current_user)
undefined

Guards

守卫机制

ruby
event :pay do
  transitions from: :pending, to: :paid, guard: :payment_valid?
end

def payment_valid?
  payment_method.present? && total > 0
end
ruby
event :pay do
  transitions from: :pending, to: :paid, guard: :payment_valid?
end

def payment_valid?
  payment_method.present? && total > 0
end

Usage

Usage

order.pay! # Raises AASM::InvalidTransition if guard fails order.pay # Returns false (no exception)
undefined
order.pay! # Raises AASM::InvalidTransition if guard fails order.pay # Returns false (no exception)
undefined

Callbacks

回调函数

ruby
aasm do
  # State callbacks
  state :paid, before_enter: :validate_payment,
               after_enter: :send_receipt

  # Event callbacks
  event :ship do
    before do
      generate_tracking_number
    end

    after do
      notify_customer
    end

    transitions from: :processing, to: :shipped
  end
end
Callback order:
  1. before
    (event)
  2. before_exit
    (old state)
  3. before_enter
    (new state)
  4. State change persisted
  5. after_exit
    (old state)
  6. after_enter
    (new state)
  7. after
    (event)
ruby
aasm do
  # State callbacks
  state :paid, before_enter: :validate_payment,
               after_enter: :send_receipt

  # Event callbacks
  event :ship do
    before do
      generate_tracking_number
    end

    after do
      notify_customer
    end

    transitions from: :processing, to: :shipped
  end
end
回调执行顺序:
  1. before
    (事件级)
  2. before_exit
    (旧状态)
  3. before_enter
    (新状态)
  4. 持久化状态变更
  5. after_exit
    (旧状态)
  6. after_enter
    (新状态)
  7. after
    (事件级)

Multiple Transitions

多分支转换

ruby
event :approve do
  transitions from: :pending, to: :approved, guard: :auto_approvable?
  transitions from: :pending, to: :review, guard: :needs_review?
  transitions from: :pending, to: :rejected  # fallback
end
First matching guard wins.
ruby
event :approve do
  transitions from: :pending, to: :approved, guard: :auto_approvable?
  transitions from: :pending, to: :review, guard: :needs_review?
  transitions from: :pending, to: :rejected  # fallback
end
第一个匹配的守卫规则生效。

Error Handling

错误处理

ruby
undefined
ruby
undefined

Safe (returns false on failure)

Safe (returns false on failure)

order.pay # => false if invalid
order.pay # => false if invalid

Raises exception

Raises exception

begin order.pay! rescue AASM::InvalidTransition => e Rails.logger.error("Invalid transition: #{e.message}") end
begin order.pay! rescue AASM::InvalidTransition => e Rails.logger.error("Invalid transition: #{e.message}") end

Check before transition

Check before transition

if order.may_pay? order.pay! end
undefined
if order.may_pay? order.pay! end
undefined

Testing State Machines

状态机测试

ruby
RSpec.describe Order do
  let(:order) { create(:order) }

  it "starts in pending state" do
    expect(order).to be_pending
  end

  describe "pay event" do
    it "transitions to paid" do
      expect { order.pay! }
        .to change(order, :status).from("pending").to("paid")
    end

    it "sends payment received email" do
      expect(OrderMailer).to receive_message_chain(:payment_received, :deliver_later)
      order.pay!
    end
  end

  describe "ship event" do
    context "when pending" do
      it "raises error" do
        expect { order.ship! }.to raise_error(AASM::InvalidTransition)
      end
    end

    context "when processing" do
      before { order.update!(status: :processing) }

      it "transitions to shipped" do
        expect { order.ship! }
          .to change(order, :status).to("shipped")
      end
    end
  end
end
ruby
RSpec.describe Order do
  let(:order) { create(:order) }

  it "starts in pending state" do
    expect(order).to be_pending
  end

  describe "pay event" do
    it "transitions to paid" do
      expect { order.pay! }
        .to change(order, :status).from("pending").to("paid")
    end

    it "sends payment received email" do
      expect(OrderMailer).to receive_message_chain(:payment_received, :deliver_later)
      order.pay!
    end
  end

  describe "ship event" do
    context "when pending" do
      it "raises error" do
        expect { order.ship! }.to raise_error(AASM::InvalidTransition)
      end
    end

    context "when processing" do
      before { order.update!(status: :processing) }

      it "transitions to shipped" do
        expect { order.ship! }
          .to change(order, :status).to("shipped")
      end
    end
  end
end

Advanced Patterns

进阶模式

For multiple state machines, persistence options, and history tracking see:
  • references/aasm-patterns.md
关于多状态机、持久化选项以及历史追踪的内容,请查看:
  • references/aasm-patterns.md

Related Skills

相关技能

  • event-sourcing-coder
    - For recording domain events when state transitions should trigger notifications, webhooks, or audit trails.
  • event-sourcing-coder
    - 用于在状态转换需要触发通知、Webhook或审计追踪时记录领域事件。