PJCHENder 未整理筆記

[Gem] AASM 狀態機

2017-10-11

[Gem] AASM 狀態機

@(Ruby on Rails)[rails, gem]

1
2
3
4
# Gemfile

# AASM is a continuation of the acts-as-state-machine rails plugin, built for plain Ruby objects.
gem 'aasm', '~> 4.12', '>= 4.12.0'

instance method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
##
# check status
##

job.running? # => true
job.may_run? # => false

job.aasm.current_state # stage3
job.aasm_state # => 'sleeping'
job.aasm.human_state # => 'sleeping'
job.aasm.states(:permitted => true).map(&:name) # show all permitted states (from initial state)
job.aasm.states(:permitted => false).map(&:name) # show all non permitted states
job.aasm.events.map(&:name) # show all possible (triggerable) events from the current state

##
# change status 的方法要根據在 AASM 中 event 所定義的名稱
##
job.sleep # => triggered :sleep
job.sleep! # => triggered :sleep!

class method

1
Job.aasm.states_for_select  # 產生可以直接套用到 select 的 options

erb 中帶入 select options

1
<%= select_tag :transaction_state, options_for_select(Transaction.aasm.states_for_select), class: 'form-control' %>

目錄

[TOC]

Automatic Scope

在 AASM 中內建了 Model Scope 讓我們使用:

1
2
# 假設 Order 有 paid 的狀態,那麼可以直接使用
Order.paid

i18n

AASM 支援在 i18n 定義:

1
2
3
4
5
6
7
8
9
10
zh-TW:
activerecord:
attributes:
order:
status/waiting_for_payment: 等待付款
status/paid: 已付款
status/failed: 已出貨
status/canceled: 已逾期
status/expired: 已退款
status/refunded: 已退款

使用範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# ./OnePageShop/app/models/order.rb
class Order < ApplicationRecord
# Concerns macros
include AASM

# Attributes related macros
aasm column: 'status', no_direct_assignment: true, requires_lock: true do
# 等待付款
state :waiting_for_payment, initial: true
# 已付款
state :paid
# 已出貨
state :shipped
# 訂單逾期
state :expired
# 已退款
state :refunded

after_all_transitions :generate_status_transition_log!

event :mark_as_paid do
transitions from: %i(placed waiting_for_payment), to: :paid
end

event :mark_as_shipped do
transitions from: :paid, to: :shipped
end

event :mark_as_expired do
transitions from: :waiting_for_payment, to: :expired
end

event :mark_as_refunded do
transitions from: :paid, to: :refunded
end
end

# ...

private
# callback methods
def can_not_be_destroyed
throw :abort
end

def generate_status_transition_log!
status_logs.create!(status: aasm.to_state)
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# ./OnePageShop/app/models/transaction.rb
class Transaction < ApplicationRecord
# Concerns macros
include AASM

# Attributes related macros
aasm column: 'status', no_direct_assignment: true, requires_lock: true do
# 等待繳款
state :waiting_for_payment, initial: true
# 已付款
state :paid
# 交易失敗
state :failed
# 取消
state :canceled
# 逾期
state :expired
# 已退款
state :refunded

after_all_transitions :generate_status_transition_log!

event :mark_as_paid do
transitions from: :waiting_for_payment, to: :paid
end

event :mark_as_paid do
transitions from: :waiting_for_payment, to: :failed
end

event :mark_as_canceled do
transitions from: :waiting_for_payment, to: :canceled
end

event :mark_as_expired do
transitions from: :waiting_for_payment, to: :expired
end

event :mark_as_refunded, after: :check_all_transactions_refunded! do
transitions from: :paid, to: :refunded
end
end

# callbacks
after_initialize :generate_trade_number
after_create :generate_status_change_log!
after_update :update_order_status!

private
# callback methods
def generate_status_change_log!
status_logs.create!(status: aasm.current_state)
end

def generate_status_transition_log!
status_logs.create!(status: aasm.to_state)
end

def update_order_status!
return if payment_info.nil?

if self.paid?
# set all waiting for payment transaction as canceled
self.order.transactions.waiting_for_payment.each do |transaction|
transaction.mark_as_canceled! if transaction.may_mark_as_canceled?
end

self.order.mark_as_paid! if self.order.may_mark_as_paid?
end
end

def check_all_transactions_refunded!
# 若沒有其餘等待付款或已付款的交易,則將訂單狀態改為已退款
if !Transaction.where(order_id: self.order_id, status: %i(waiting_for_payment paid)).exists?
self.order.mark_as_refunded!
end
end
end

參考

掃描二維條碼,分享此文章