แพ็คเกจนี้มี AASM ซึ่งเป็นไลบรารีสำหรับการเพิ่มเครื่องจักรสถานะ จำกัด ในคลาสทับทิม
AASM เริ่มต้นเป็นปลั๊กอิน ACTS_AS_STATE_MACHINE แต่ได้พัฒนาเป็นไลบรารีทั่วไปมากกว่าซึ่งไม่ได้กำหนดเป้าหมายแบบจำลอง Activerecord อีกต่อไป ปัจจุบันมีอะแดปเตอร์สำหรับ ORM หลายตัว แต่สามารถใช้สำหรับคลาสทับทิมใด ๆ ไม่ว่าจะเป็นคลาสแม่ (ถ้ามี)
ลองดูที่ readme_from_version_3_to_4 สำหรับรายละเอียดวิธีเปลี่ยนจากเวอร์ชัน 3.x เป็น 4.0 ของ AASM
การเพิ่มเครื่องจักรของรัฐนั้นง่ายพอ ๆ กับโมดูล AASM และเริ่มกำหนด สถานะ และ เหตุการณ์ ร่วมกับ ช่วงการเปลี่ยนภาพ ของพวกเขา:
class Job
include AASM
aasm do
state :sleeping , initial : true
state :running , :cleaning
event :run do
transitions from : :sleeping , to : :running
end
event :clean do
transitions from : :running , to : :cleaning
end
event :sleep do
transitions from : [ :running , :cleaning ] , to : :sleeping
end
end
end สิ่งนี้ให้วิธีการสาธารณะสองสามวิธีสำหรับอินสแตนซ์ของ Job ในชั้นเรียน:
job = Job . new
job . sleeping? # => true
job . may_run? # => true
job . run
job . running? # => true
job . sleeping? # => false
job . may_run? # => false
job . run # => raises AASM::InvalidTransition หากคุณไม่ชอบข้อยกเว้นและชอบการตอบ สนอง true หรือ false ง่าย
class Job
...
aasm whiny_transitions : false do
...
end
end
job . running? # => true
job . may_run? # => false
job . run # => falseเมื่อทำการยิงเหตุการณ์คุณสามารถส่งผ่านบล็อกไปยังวิธีนี้จะเรียกได้ก็ต่อเมื่อการเปลี่ยนแปลงสำเร็จ:
job . run do
job . user . notify_job_ran # Will be called if job.may_run? is true
endคุณสามารถกำหนดจำนวนของการโทรกลับสำหรับกิจกรรมการเปลี่ยนผ่านและสถานะของคุณ วิธีการเหล่านี้ procs หรือคลาสจะถูกเรียกเมื่อเป็นไปตามเกณฑ์บางอย่างเช่นการเข้าสู่สถานะเฉพาะ:
class Job
include AASM
aasm do
state :sleeping , initial : true , before_enter : :do_something
state :running , before_enter : Proc . new { do_something && notify_somebody }
state :finished
after_all_transitions :log_status_change
event :run , after : :notify_somebody do
before do
log ( 'Preparing to run' )
end
transitions from : :sleeping , to : :running , after : Proc . new { |* args | set_process ( * args ) }
transitions from : :running , to : :finished , after : LogRunTime
end
event :sleep do
after do
...
end
error do | e |
...
end
transitions from : :running , to : :sleeping
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
def do_something
...
end
def notify_somebody
...
end
end
class LogRunTime
def call
log "Job was running for X seconds"
end
end ในกรณีนี้ do_something ถูกเรียกก่อนเข้าสู่ sleeping ของรัฐจริง ๆ ในขณะที่ notify_somebody ถูกเรียกหลังจาก run เปลี่ยนแปลง (จาก sleeping จนถึง running ) เสร็จสิ้น
AASM จะเริ่มต้น LogRunTime และเรียกใช้วิธี call สำหรับคุณหลังจากการเปลี่ยนจาก running เป็น finished ในตัวอย่างด้านบน คุณสามารถส่งอาร์กิวเมนต์ไปยังชั้นเรียนโดยกำหนดวิธีการเริ่มต้นในนั้นเช่นนี้:
โปรดทราบว่า Procs ถูกดำเนินการในบริบทของบันทึกหมายความว่าคุณไม่จำเป็นต้องคาดหวังว่าบันทึกเป็นอาร์กิวเมนต์เพียงเรียกวิธีการที่คุณต้องการ
class LogRunTime
# optional args parameter can be omitted, but if you define initialize
# you must accept the model instance as the first parameter to it.
def initialize ( job , args = { } )
@job = job
end
def call
log "Job was running for #{ @job . run_time } seconds"
end
end คุณสามารถส่งพารามิเตอร์ไปยังกิจกรรม:
job = Job . new
job . run ( :defragmentation ) ยามทั้งหมดและหลังจากการเรียกกลับจะได้รับพารามิเตอร์เหล่านี้ ในกรณีนี้ set_process จะถูกเรียกด้วย :defragmentation
หากอาร์กิวเมนต์แรกของเหตุการณ์คือสถานะ (เช่น :running หรือ :finished ) อาร์กิวเมนต์แรกจะถูกใช้และเครื่องรัฐจะพยายามเปลี่ยนไปสู่สถานะนั้น เพิ่มพารามิเตอร์คั่นด้วยเครื่องหมายจุลภาคสำหรับยามและการโทรกลับ
job = Job . new
job . run ( :running , :defragmentation ) ในกรณีนี้ set_process จะไม่ถูกเรียกงานจะเปลี่ยนไปใช้สถานะการเรียกใช้และการโทรกลับจะได้รับ :defragmentation เป็นพารามิเตอร์
ในกรณีที่เกิดข้อผิดพลาดในระหว่างการประมวลผลเหตุการณ์ข้อผิดพลาดจะได้รับการช่วยเหลือและส่งผ่านไปยัง :error การโทรกลับซึ่งสามารถจัดการหรือปรับใหม่เพื่อการแพร่กระจายเพิ่มเติม
นอกจากนี้คุณสามารถกำหนดวิธีการที่จะเรียกได้หากเหตุการณ์ใด ๆ ล้มเหลว:
def aasm_event_failed ( event_name , old_state_name )
# use custom exception/messages, report metrics, etc
end ในระหว่างการเปลี่ยนแปลง :after การโทรกลับ (และอย่างน่าเชื่อถือเท่านั้นหรือในการเรียกกลับไปที่ Global after_all_transitions ) คุณสามารถเข้าถึงสถานะต้นกำเนิด (จากสถานะ) และสถานะเป้าหมาย (รัฐถึง) เช่นนี้:
def set_process ( name )
logger . info "from #{ aasm . from_state } to #{ aasm . to_state } "
end ที่นี่คุณสามารถดูรายการการโทรกลับที่เป็นไปได้ทั้งหมดพร้อมกับคำสั่งโทรของพวกเขา:
begin
event before_all_events
event before
event guards
transition guards
old_state before_exit
old_state exit
after_all_transitions
transition after
new_state before_enter
new_state enter
... update state ...
event before_success # if persist successful
transition success # if persist successful, database update not guaranteed
event success # if persist successful, database update not guaranteed
old_state after_exit
new_state after_enter
event after
event after_all_events
rescue
event error
event error_on_all_events
ensure
event ensure
event ensure_on_all_events
end ใช้การโทรกลับของเหตุการณ์ after_commit หากควรถูกไล่ออกหลังจากอัปเดตฐานข้อมูล
ในขณะที่เรียกใช้การโทรกลับคุณสามารถดึงชื่อของเหตุการณ์ที่เกิดขึ้นได้อย่างง่ายดายโดยใช้ aasm.current_event :
# taken the example callback from above
def do_something
puts "triggered #{ aasm . current_event } "
endแล้วก็
job = Job . new
# without bang
job . sleep # => triggered :sleep
# with bang
job . sleep! # => triggered :sleep! สมมติว่าคุณต้องการอนุญาตให้มีการเปลี่ยนแปลงโดยเฉพาะเฉพาะในกรณีที่กำหนดเงื่อนไขที่กำหนดไว้ สำหรับสิ่งนี้คุณสามารถตั้งค่ายามต่อการเปลี่ยนแปลงซึ่งจะทำงานก่อนที่จะรันการเปลี่ยนแปลงจริง หากผู้พิทักษ์ส่งคืน false การเปลี่ยนแปลงจะถูกปฏิเสธ (เพิ่ม AASM::InvalidTransition ):
class Cleaner
include AASM
aasm do
state :idle , initial : true
state :cleaning
event :clean do
transitions from : :idle , to : :cleaning , guard : :cleaning_needed?
end
event :clean_if_needed do
transitions from : :idle , to : :cleaning do
guard do
cleaning_needed?
end
end
transitions from : :idle , to : :idle
end
event :clean_if_dirty do
transitions from : :idle , to : :cleaning , guard : :if_dirty?
end
end
def cleaning_needed?
false
end
def if_dirty? ( status )
status == :dirty
end
end
job = Cleaner . new
job . may_clean? # => false
job . clean # => raises AASM::InvalidTransition
job . may_clean_if_needed? # => true
job . clean_if_needed! # idle
job . clean_if_dirty ( :clean ) # => raises AASM::InvalidTransition
job . clean_if_dirty ( :dirty ) # => trueคุณยังสามารถจัดหาเจ้าหน้าที่รักษาความปลอดภัยจำนวนหนึ่งซึ่งทุกคนต้องประสบความสำเร็จในการดำเนินการต่อไป
def walked_the_dog? ; ... ; end
event :sleep do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? , :walked_the_dog? ]
endหากคุณต้องการจัดหายามสำหรับการเปลี่ยนแปลงทั้งหมดภายในเหตุการณ์คุณสามารถใช้เจ้าหน้าที่จัดงาน
event :sleep , guards : [ :walked_the_dog? ] do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? ]
transitions from : :cleaning , to : :sleeping
end หากคุณต้องการไวยากรณ์ของ Guard Ruby ที่คล้ายกันมากขึ้นคุณสามารถใช้ if และ unless ได้เช่นกัน:
event :clean do
transitions from : :running , to : :cleaning , if : :cleaning_needed?
end
event :sleep do
transitions from : :running , to : :sleeping , unless : :cleaning_needed?
end
end คุณสามารถเรียกใช้คลาสแทนวิธีการหากชั้นเรียนตอบกลับ call
event :sleep do
transitions from : :running , to : :sleeping , guards : Dog
end class Dog
def call
cleaning_needed? && walked?
end
...
endในกรณีที่มีการเปลี่ยนแปลงหลายครั้งสำหรับเหตุการณ์การเปลี่ยนแปลงครั้งแรกที่สำเร็จจะหยุดการเปลี่ยนแปลงอื่น ๆ ในเหตุการณ์เดียวกันจากการประมวลผล
require 'aasm'
class Job
include AASM
aasm do
state :stage1 , initial : true
state :stage2
state :stage3
state :completed
event :stage1_completed do
transitions from : :stage1 , to : :stage3 , guard : :stage2_completed?
transitions from : :stage1 , to : :stage2
end
end
def stage2_completed?
true
end
end
job = Job . new
job . stage1_completed
job . aasm . current_state # stage3 คุณสามารถกำหนดการเปลี่ยนแปลงจากสถานะที่กำหนดใด ๆ โดยละเว้น from :
event :abort do
transitions to : :aborted
endคุณสามารถกำหนดชื่อที่แสดงสำหรับสถานะโดยใช้: ตัวเลือกการแสดงผล
class Job
include AASM
aasm do
state :stage1 , initial : true , display : 'First Stage'
state :stage2
state :stage3
end
end
job = Job . new
job . aasm . human_state รองรับเครื่องจักรของรัฐหลายเครื่องต่อชั้นเรียน โปรดทราบว่า AASM ถูกสร้างขึ้นด้วยเครื่องหนึ่งรัฐต่อชั้นเรียนในใจ อย่างไรก็ตามนี่คือวิธีการทำ (ดูด้านล่าง) โปรดทราบว่าคุณจะต้องระบุคอลัมน์ฐานข้อมูลสำหรับตำแหน่งที่รัฐที่เกี่ยวข้องของคุณจะถูกจัดเก็บ - เราได้ระบุสองคอลัมน์ move_state และ work_state ในตัวอย่างด้านล่าง ดูส่วนชื่อคอลัมน์และการย้ายข้อมูลสำหรับข้อมูลเพิ่มเติม
class SimpleMultipleExample
include AASM
aasm ( :move , column : 'move_state' ) do
state :standing , initial : true
state :walking
state :running
event :walk do
transitions from : :standing , to : :walking
end
event :run do
transitions from : [ :standing , :walking ] , to : :running
end
event :hold do
transitions from : [ :walking , :running ] , to : :standing
end
end
aasm ( :work , column : 'work_state' ) do
state :sleeping , initial : true
state :processing
event :start do
transitions from : :sleeping , to : :processing
end
event :stop do
transitions from : :processing , to : :sleeping
end
end
end
simple = SimpleMultipleExample . new
simple . aasm ( :move ) . current_state
# => :standing
simple . aasm ( :work ) . current_state
# => :sleeping
simple . start
simple . aasm ( :move ) . current_state
# => :standing
simple . aasm ( :work ) . current_state
# => :processing AASM ไม่ได้ห้ามมิให้กำหนดเหตุการณ์เดียวกันในเครื่องมากกว่าหนึ่งเครื่อง หากไม่มีเนมสเปซให้คำจำกัดความล่าสุด "ชนะ" และแทนที่คำจำกัดความก่อนหน้า อย่างไรก็ตามมีการออกคำเตือน: SimpleMultipleExample: overriding method 'run'! -
หรือคุณสามารถจัดเตรียมเนมสเปซสำหรับแต่ละรัฐสถานะ:
class NamespacedMultipleExample
include AASM
aasm ( :status ) do
state :unapproved , initial : true
state :approved
event :approve do
transitions from : :unapproved , to : :approved
end
event :unapprove do
transitions from : :approved , to : :unapproved
end
end
aasm ( :review_status , namespace : :review ) do
state :unapproved , initial : true
state :approved
event :approve do
transitions from : :unapproved , to : :approved
end
event :unapprove do
transitions from : :approved , to : :unapproved
end
end
end
namespaced = NamespacedMultipleExample . new
namespaced . aasm ( :status ) . current_state
# => :unapproved
namespaced . aasm ( :review_status ) . current_state
# => :unapproved
namespaced . approve_review
namespaced . aasm ( :review_status ) . current_state
# => :approved วิธี aasm ระดับ AASM ทั้งหมดและระดับอินสแตนซ์ยอมรับตัวเลือกเครื่องจักรของรัฐ ตัวอย่างเช่นในการใช้การตรวจสอบในระดับคลาสคุณต้องใช้
SimpleMultipleExample . aasm ( :move ) . states . map ( & :name )
# => [:standing, :walking, :running]อนุญาตให้เหตุการณ์ถูกผูกไว้กับอีกเหตุการณ์หนึ่ง
class Example
include AASM
aasm ( :work ) do
state :sleeping , initial : true
state :processing
event :start do
transitions from : :sleeping , to : :processing
end
event :stop do
transitions from : :processing , to : :sleeping
end
end
aasm ( :question ) do
state :answered , initial : true
state :asked
event :ask , binding_event : :start do
transitions from : :answered , to : :asked
end
event :answer , binding_event : :stop do
transitions from : :asked , to : :answered
end
end
end
example = Example . new
example . aasm ( :work ) . current_state #=> :sleeping
example . aasm ( :question ) . current_state #=> :answered
example . ask
example . aasm ( :work ) . current_state #=> :processing
example . aasm ( :question ) . current_state #=> :askedAASM สร้างค่าคงที่สำหรับแต่ละสถานะโดยอัตโนมัติดังนั้นคุณไม่จำเป็นต้องกำหนดอย่างชัดเจน
class Foo
include AASM
aasm do
state :initialized
state :calculated
state :finalized
end
end
> Foo :: STATE_INITIALIZED
#=> :initialized
> Foo :: STATE_CALCULATED
#=> :calculated AASM ช่วยให้คุณสามารถขยาย AASM::Base สำหรับวัตถุประสงค์ในการใช้งานของคุณเองได้อย่างง่ายดาย
สมมติว่าเรามีตรรกะทั่วไปในหลายรุ่น AASM เราสามารถรวบรวมตรรกะนี้ในคลาสย่อยของ AASM::Base
class CustomAASMBase < AASM :: Base
# A custom transition that we want available across many AASM models.
def count_transitions!
klass . class_eval do
aasm with_klass : CustomAASMBase do
after_all_transitions :increment_transition_count
end
end
end
# A custom annotation that we want available across many AASM models.
def requires_guards!
klass . class_eval do
attr_reader :authorizable_called ,
:transition_count ,
:fillable_called
def authorizable?
@authorizable_called = true
end
def fillable?
@fillable_called = true
end
def increment_transition_count
@transition_count ||= 0
@transition_count += 1
end
end
end
end เมื่อเราประกาศแบบจำลองของเราที่มีเครื่อง AASM State เราเพียงแค่ประกาศบล็อก AASM ด้วย :with_klass Key ไปยังชั้นเรียนของเราเอง
class SimpleCustomExample
include AASM
# Let's build an AASM state machine with our custom class.
aasm with_klass : CustomAASMBase do
requires_guards!
count_transitions!
state :initialised , initial : true
state :filled_out
state :authorised
event :fill_out do
transitions from : :initialised , to : :filled_out , guard : :fillable?
end
event :authorise do
transitions from : :filled_out , to : :authorised , guard : :authorizable?
end
end
endAASM มาพร้อมกับการสนับสนุน Activerecord และอนุญาตให้คงอยู่โดยอัตโนมัติของสถานะของวัตถุในฐานข้อมูล
เพิ่ม gem 'after_commit_everywhere', '~> 1.0' ลงใน gemfile ของคุณ
class Job < ActiveRecord :: Base
include AASM
aasm do # default column: aasm_state
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
event :sleep do
transitions from : :running , to : :sleeping
end
end
endคุณสามารถบอก AASM ให้บันทึกวัตถุอัตโนมัติหรือปล่อยให้มันไม่ได้บันทึก
job = Job . new
job . run # not saved
job . run! # saved
# or
job . aasm . fire ( :run ) # not saved
job . aasm . fire! ( :run ) # saved การออมรวมถึงการเรียกใช้การตรวจสอบทั้งหมดในคลาส Job หากการตั้งค่าสถานะ whiny_persistence ถูกตั้งค่าเป็น true ข้อยกเว้นจะถูกยกขึ้นในกรณีที่เกิดความล้มเหลว หากการตั้งค่าสถานะ whiny_persistence ถูกตั้งค่าเป็น false วิธีการที่มีการส่งคืน BANG กลับ true หากการเปลี่ยนแปลงของรัฐนั้นสำเร็จหรือ false หากเกิดข้อผิดพลาด
หากคุณต้องการให้แน่ใจว่ารัฐได้รับการบันทึกโดยไม่ต้องใช้การตรวจสอบ (และอาจคงสถานะวัตถุที่ไม่ถูกต้อง) เพียงแค่บอก AASM เพื่อข้ามการตรวจสอบ โปรดทราบว่าเมื่อข้ามการตรวจสอบความถูกต้องจะมีเพียงคอลัมน์สถานะเท่านั้นที่จะได้รับการอัปเดตในฐานข้อมูล (เช่นเดียวกับ Activerecord update_column ที่ใช้งานได้)
class Job < ActiveRecord :: Base
include AASM
aasm skip_validation_on_save : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
event :sleep do
transitions from : :running , to : :sleeping
end
end
end นอกจากนี้คุณสามารถข้ามการตรวจสอบความถูกต้องที่ระดับอินสแตนซ์ด้วย some_event_name_without_validation! วิธี. ด้วยสิ่งนี้คุณมีความยืดหยุ่นในการตรวจสอบความถูกต้องสำหรับการเปลี่ยนแปลงทั้งหมดของคุณโดยค่าเริ่มต้นจากนั้นข้ามไปทุกที่ที่ต้องการ โปรดทราบว่าคอลัมน์สถานะเฉพาะจะได้รับการอัปเดตตามที่กล่าวไว้ในตัวอย่างข้างต้น
job . run_without_validation!หากคุณต้องการตรวจสอบให้แน่ใจว่าคอลัมน์ AASM สำหรับการจัดเก็บสถานะจะไม่ได้รับการกำหนดโดยตรงให้กำหนดค่า AASM เพื่อไม่อนุญาตให้มีการกำหนดโดยตรงเช่นนี้:
class Job < ActiveRecord :: Base
include AASM
aasm no_direct_assignment : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
end
endส่งผลให้สิ่งนี้:
job = Job . create
job . aasm_state # => 'sleeping'
job . aasm_state = :running # => raises AASM::NoDirectAssignmentError
job . aasm_state # => 'sleeping' คุณสามารถบอกให้ AASM พยายามเขียนการประทับเวลาเมื่อใดก็ตามที่มีการป้อนสถานะใหม่ หาก timestamps: true ถูกตั้งค่า AASM จะมองหาสนามที่มีชื่อเหมือนสถานะใหม่ Plus _at และพยายามเติมเต็ม:
class Job < ActiveRecord :: Base
include AASM
aasm timestamps : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
end
endส่งผลให้สิ่งนี้:
job = Job . create
job . running_at # => nil
job . run!
job . running_at # => 2020-02-20 20:00:00ฟิลด์การประทับเวลาที่หายไปจะถูกละเว้นอย่างเงียบ ๆ ดังนั้นจึงไม่จำเป็นต้องมีตัวตั้งค่า (เช่นคอลัมน์ Activerecord) สำหรับ ทุก รัฐเมื่อใช้ตัวเลือกนี้
คุณสามารถใช้การแจกแจงใน Rails 4.1+ สำหรับคอลัมน์สถานะของคุณ:
class Job < ActiveRecord :: Base
include AASM
enum state : {
sleeping : 5 ,
running : 99
}
aasm column : :state , enum : true do
state :sleeping , initial : true
state :running
end
end คุณสามารถส่งชื่อของวิธีการที่ให้การเข้าถึงการแมปแจงนับเป็นค่าของ enum หรือคุณสามารถตั้งค่าเป็น true ได้อย่างชัดเจน ในกรณีหลัง AASM จะพยายามใช้ชื่อคอลัมน์พหูพจน์เพื่อเข้าถึงสถานะ enum ที่เป็นไปได้
นอกจากนี้หากคอลัมน์ของคุณมีประเภทจำนวนเต็ม (ซึ่งโดยปกติจะเป็นกรณีเมื่อคุณทำงานกับ Rails enums) คุณสามารถละเว้น :enum --- AASM อัตโนมัติตรวจจับสถานการณ์นี้และเปิดใช้งานการสนับสนุน Enum หากมีอะไรผิดพลาดคุณสามารถปิดใช้งานฟังก์ชันการทำงานของ Enum และกลับไปใช้พฤติกรรมเริ่มต้นโดยการตั้งค่า :enum เป็น false
AASM ยังรองรับภาคต่อนอกเหนือจาก Activerecord และ Mongoid
class Job < Sequel :: Model
include AASM
aasm do # default column: aasm_state
...
end
endอย่างไรก็ตามมันยังไม่เป็นคุณสมบัติที่สมบูรณ์เหมือน Activerecord ตัวอย่างเช่นมีขอบเขตที่กำหนดไว้ ดูขอบเขตอัตโนมัติ
ตั้งแต่เวอร์ชัน 4.8.0 AASM ยังรองรับ Dynamoid เป็น ORM การคงอยู่
AASM ยังสนับสนุนการคงอยู่ของ MongoDB หากคุณใช้ mongoid ตรวจสอบให้แน่ใจว่าได้รวม Mongoid :: เอกสารก่อนที่คุณจะรวม AASM
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM ยังรองรับการคงอยู่ของ RethinkDB หากคุณใช้ nobrainer ตรวจสอบให้แน่ใจว่าได้รวมเอกสาร nobrainer :: ก่อนที่คุณจะรวม AASM
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM ยังสนับสนุนการคงอยู่ใน Redis ผ่าน Redis :: Objects ตรวจสอบให้แน่ใจว่าได้รวม Redis :: Objects ก่อนที่คุณจะรวม AASM โปรดทราบว่าเหตุการณ์ที่ไม่ใช่ BANG จะทำงานเป็นเหตุการณ์ที่เกิดขึ้นจากการเปลี่ยนแปลงทุกการโทร
class User
include Redis :: Objects
include AASM
aasm do
end
endAASM จะสร้างวิธีการขอบเขตโดยอัตโนมัติสำหรับแต่ละสถานะในโมเดล
class Job < ActiveRecord :: Base
include AASM
aasm do
state :sleeping , initial : true
state :running
state :cleaning
end
def self . sleeping
"This method name is already in use"
end
end class JobsController < ApplicationController
def index
@running_jobs = Job . running
@recent_cleaning_jobs = Job . cleaning . where ( 'created_at >= ?' , 3 . days . ago )
# @sleeping_jobs = Job.sleeping #=> "This method name is already in use"
end
end หากคุณไม่ต้องการขอบเขต (หรือไม่ต้องการ) ให้ปิดการสร้างของพวกเขาเมื่อกำหนดสถานะ AASM เช่นนี้:
class Job < ActiveRecord :: Base
include AASM
aasm create_scopes : false do
state :sleeping , initial : true
state :running
state :cleaning
end
endตั้งแต่เวอร์ชัน 3.0.13 AASM รองรับการทำธุรกรรม Activerecord ดังนั้นเมื่อใดก็ตามที่การโทรกลับหรือการอัปเดตของรัฐล้มเหลวการเปลี่ยนแปลงทั้งหมดในบันทึกฐานข้อมูลใด ๆ จะถูกย้อนกลับ MongoDB ไม่สนับสนุนการทำธุรกรรม
ขณะนี้มีการโทรกลับธุรกรรม 3 รายการที่สามารถจัดการได้ในเหตุการณ์และการโทรกลับ 2 รายการสำหรับเหตุการณ์ทั้งหมด
event before_all_transactions
event before_transaction
event aasm_fire_event ( within transaction )
event after_commit ( if event successful )
event after_transaction
event after_all_transactions หากคุณต้องการตรวจสอบให้แน่ใจว่าการกระทำที่ขึ้นอยู่กับการกระทำจะเกิดขึ้นหลังจากการทำธุรกรรมให้ใช้วิธีการโทรกลับ after_commit พร้อมกับวิธีการประหยัดอัตโนมัติ (bang) เช่นนี้:
class Job < ActiveRecord :: Base
include AASM
aasm do
state :sleeping , initial : true
state :running
event :run , after_commit : :notify_about_running_job do
transitions from : :sleeping , to : :running
end
end
def notify_about_running_job
...
end
end
job = Job . where ( state : 'sleeping' ) . first!
job . run! # Saves the model and triggers the after_commit callback โปรดทราบว่าสิ่งต่อไปนี้จะไม่เรียกใช้การเรียกกลับ after_commit เนื่องจากไม่ได้ใช้วิธีการแบบอัตโนมัติ:
job = Job . where ( state : 'sleeping' ) . first!
job . run
job . save! #notify_about_running_job is not run โปรดทราบว่า :after_commit ทำงานเกี่ยวกับการใช้รูปแบบการทำธุรกรรมแบบกำหนดเองมากกว่าการทำธุรกรรม DB ในชีวิตจริง ความจริงเรื่องนี้ยังคงเป็นสาเหตุของเงื่อนไขการแข่งขันและการโทรกลับซ้ำซ้อนภายในธุรกรรมที่ซ้อนกัน เพื่อที่จะแก้ไขว่าขอแนะนำให้เพิ่ม gem 'after_commit_everywhere', '~> 1.0' ลงใน Gemfile ของคุณ
หากคุณต้องการห่อหุ้มการเปลี่ยนแปลงของรัฐภายในการทำธุรกรรมของตัวเองพฤติกรรมของธุรกรรมที่ซ้อนกันนี้อาจทำให้เกิดความสับสน ดูการทำธุรกรรมที่ซ้อนกันของ Activerecord หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ อย่างไรก็ตาม AASM โดยค่าเริ่มต้นต้องมี transaction(requires_new: true) คุณสามารถแทนที่พฤติกรรมนี้ได้โดยการเปลี่ยนการกำหนดค่า
class Job < ActiveRecord :: Base
include AASM
aasm requires_new_transaction : false do
...
end
...
end ซึ่งนำไปสู่ transaction(requires_new: false) , ค่าเริ่มต้นของราง
นอกจากนี้หากคุณไม่ต้องการให้การกระทำใด ๆ ของการกระทำใด ๆ ของคุณถูกห่อหุ้มในการทำธุรกรรมคุณสามารถระบุธง use_transactions สิ่งนี้จะมีประโยชน์หากคุณต้องการคงอยู่กับฐานข้อมูลที่เกิดขึ้นเนื่องจากการทำธุรกรรมหรือการโทรกลับแม้ว่าจะเกิดข้อผิดพลาดบางอย่าง ค่าสถานะ use_transactions เป็นจริงโดยค่าเริ่มต้น
class Job < ActiveRecord :: Base
include AASM
aasm use_transactions : false do
...
end
...
end AASM รองรับการล็อคในแง่ร้ายของ Activerecord ผ่าน with_lock สำหรับเลเยอร์การคงอยู่ของฐานข้อมูล
| ตัวเลือก | วัตถุประสงค์ |
|---|---|
false (ค่าเริ่มต้น) | ไม่ได้ล็อค |
true | รับล็อคในแง่ร้ายบล็อกเช่น FOR UPDATE |
| สาย | รับล็อคตามสตริง SQL เช่น FOR UPDATE NOWAIT |
class Job < ActiveRecord :: Base
include AASM
aasm requires_lock : true do
...
end
...
end class Job < ActiveRecord :: Base
include AASM
aasm requires_lock : 'FOR UPDATE NOWAIT' do
...
end
...
end เป็น AASM เริ่มต้นใช้คอลัมน์ aasm_state เพื่อจัดเก็บสถานะ คุณสามารถแทนที่สิ่งนี้ได้โดยกำหนดชื่อคอลัมน์ที่คุณชื่นชอบโดยใช้ :column เช่นนี้:
class Job < ActiveRecord :: Base
include AASM
aasm column : :my_state do
...
end
aasm :another_state_machine , column : :second_state do
...
end
end ไม่ว่าจะใช้ชื่อคอลัมน์ใดก็ตามตรวจสอบให้แน่ใจว่าได้เพิ่มการโยกย้ายเพื่อให้คอลัมน์นี้ (ประเภท string ) อย่าเพิ่มค่าเริ่มต้นสำหรับคอลัมน์ที่ระดับฐานข้อมูล หากคุณเพิ่มค่าเริ่มต้นในฐานข้อมูลการโทรกลับ AASM ในสถานะเริ่มต้นจะไม่ถูกยิงเมื่อสร้างอินสแตนซ์ของโมเดล
class AddJobState < ActiveRecord :: Migration
def self . up
add_column :jobs , :aasm_state , :string
end
def self . down
remove_column :jobs , :aasm_state
end
endการเปลี่ยนแปลงสถานะการบันทึกสามารถทำได้โดยใช้ Paper_trail Gem
ตัวอย่างการใช้งานสามารถพบได้ที่นี่ https://github.com/nitsujri/aasm-papertrail-ex
AASM สนับสนุนวิธีการสอบถามสำหรับรัฐและเหตุการณ์
ได้รับคลาส Job ต่อไปนี้:
class Job
include AASM
aasm do
state :sleeping , initial : true
state :running , :cleaning
event :run do
transitions from : :sleeping , to : :running
end
event :clean do
transitions from : :running , to : :cleaning , guard : :cleaning_needed?
end
event :sleep do
transitions from : [ :running , :cleaning ] , to : :sleeping
end
end
def cleaning_needed?
false
end
end # show all states
Job . aasm . states . map ( & :name )
#=> [:sleeping, :running, :cleaning]
job = Job . new
# show all permitted states (from initial state)
job . aasm . states ( permitted : true ) . map ( & :name )
#=> [:running]
# List all the permitted transitions(event and state pairs) from initial state
job . aasm . permitted_transitions
#=> [{ :event => :run, :state => :running }]
job . run
job . aasm . states ( permitted : true ) . map ( & :name )
#=> [:sleeping]
# show all non permitted states
job . aasm . states ( permitted : false ) . map ( & :name )
#=> [:cleaning]
# show all possible (triggerable) events from the current state
job . aasm . events . map ( & :name )
#=> [:clean, :sleep]
# show all permitted events
job . aasm . events ( permitted : true ) . map ( & :name )
#=> [:sleep]
# show all non permitted events
job . aasm . events ( permitted : false ) . map ( & :name )
#=> [:clean]
# show all possible events except a specific one
job . aasm . events ( reject : :sleep ) . map ( & :name )
#=> [:clean]
# list states for select
Job . aasm . states_for_select
#=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
# show permitted states with guard parameter
job . aasm . states ( { permitted : true } , guard_parameter ) . map ( & :name ) คำเตือนถูกพิมพ์โดยค่าเริ่มต้นไปยัง STDERR หากคุณต้องการบันทึกคำเตือนเหล่านั้นไปยังผลลัพธ์อื่นให้ใช้
class Job
include AASM
aasm logger : Rails . logger do
...
end
end คุณสามารถซ่อนคำเตือนได้โดยการตั้งค่า AASM::Configuration.hide_warnings = true
ตอนนี้รองรับ codedataQuery! อย่างไรก็ตามฉันยังอยู่ในขั้นตอนการส่งการอัปเดตความเข้ากันได้ของฉันไปยังที่เก็บของพวกเขา ในระหว่างนี้คุณสามารถใช้ส้อมของฉันได้อาจมีปัญหาเล็กน้อย แต่ฉันตั้งใจที่จะใช้มันด้วยตัวเองอย่างกว้างขวางดังนั้นการแก้ไขควรมาอย่างรวดเร็ว
คำเตือน:
AASM ให้การจับคู่บางอย่างสำหรับ RSPEC:
transition_fromhave_state , allow_eventallow_transition_to require 'aasm/rspec' ในไฟล์ spec_helper.rb ของคุณ # classes with only the default state machine
job = Job . new
expect ( job ) . to transition_from ( :sleeping ) . to ( :running ) . on_event ( :run )
expect ( job ) . not_to transition_from ( :sleeping ) . to ( :cleaning ) . on_event ( :run )
expect ( job ) . to have_state ( :sleeping )
expect ( job ) . not_to have_state ( :running )
expect ( job ) . to allow_event :run
expect ( job ) . to_not allow_event :clean
expect ( job ) . to allow_transition_to ( :running )
expect ( job ) . to_not allow_transition_to ( :cleaning )
# on_event also accept multiple arguments
expect ( job ) . to transition_from ( :sleeping ) . to ( :running ) . on_event ( :run , :defragmentation )
# classes with multiple state machine
multiple = SimpleMultipleExample . new
expect ( multiple ) . to transition_from ( :standing ) . to ( :walking ) . on_event ( :walk ) . on ( :move )
expect ( multiple ) . to_not transition_from ( :standing ) . to ( :running ) . on_event ( :walk ) . on ( :move )
expect ( multiple ) . to have_state ( :standing ) . on ( :move )
expect ( multiple ) . not_to have_state ( :walking ) . on ( :move )
expect ( multiple ) . to allow_event ( :walk ) . on ( :move )
expect ( multiple ) . to_not allow_event ( :hold ) . on ( :move )
expect ( multiple ) . to allow_transition_to ( :walking ) . on ( :move )
expect ( multiple ) . to_not allow_transition_to ( :running ) . on ( :move )
expect ( multiple ) . to transition_from ( :sleeping ) . to ( :processing ) . on_event ( :start ) . on ( :work )
expect ( multiple ) . to_not transition_from ( :sleeping ) . to ( :sleeping ) . on_event ( :start ) . on ( :work )
expect ( multiple ) . to have_state ( :sleeping ) . on ( :work )
expect ( multiple ) . not_to have_state ( :processing ) . on ( :work )
expect ( multiple ) . to allow_event ( :start ) . on ( :move )
expect ( multiple ) . to_not allow_event ( :stop ) . on ( :move )
expect ( multiple ) . to allow_transition_to ( :processing ) . on ( :move )
expect ( multiple ) . to_not allow_transition_to ( :sleeping ) . on ( :move )
# allow_event also accepts arguments
expect ( job ) . to allow_event ( :run ) . with ( :defragmentation ) AASM ให้การยืนยันและความคาดหวังที่คล้ายกับ RSPEC สำหรับ minitest
รายการการยืนยันที่ได้รับ assert_transition_to_allowed สนับสนุน: assert_have_state , refute_have_state , assert_transitions_from , refute_transitions_from , assert_event_allowed , refute_event_allowed , refute_transition_to_allowed
เพิ่ม require 'aasm/minitest' ในไฟล์ test_helper.rb ของคุณและใช้เช่นนี้:
# classes with only the default state machine
job = Job . new
assert_transitions_from job , :sleeping , to : :running , on_event : :run
refute_transitions_from job , :sleeping , to : :cleaning , on_event : :run
assert_have_state job , :sleeping
refute_have_state job , :running
assert_event_allowed job , :run
refute_event_allowed job , :clean
assert_transition_to_allowed job , :running
refute_transition_to_allowed job , :cleaning
# on_event also accept arguments
assert_transitions_from job , :sleeping , :defragmentation , to : :running , on_event : :run
# classes with multiple state machine
multiple = SimpleMultipleExample . new
assert_transitions_from multiple , :standing , to : :walking , on_event : :walk , on : :move
refute_transitions_from multiple , :standing , to : :running , on_event : :walk , on : :move
assert_have_state multiple , :standing , on : :move
refute_have_state multiple , :walking , on : :move
assert_event_allowed multiple , :walk , on : :move
refute_event_allowed multiple , :hold , on : :move
assert_transition_to_allowed multiple , :walking , on : :move
refute_transition_to_allowed multiple , :running , on : :move
assert_transitions_from multiple , :sleeping , to : :processing , on_event : :start , on : :work
refute_transitions_from multiple , :sleeping , to : :sleeping , on_event : :start , on : :work
assert_have_state multiple , :sleeping , on : :work
refute_have_state multiple , :processing , on : :work
assert_event_allowed multiple , :start , on : :move
refute_event_allowed multiple , :stop , on : :move
assert_transition_to_allowed multiple , :processing , on : :move
refute_transition_to_allowed multiple , :sleeping , on : :move รายการความคาดหวังที่ได้รับการสนับสนุน: must_transition_from , wont_transition_from , must_have_state , wont_have_state , must_allow_event , wont_allow_event , must_allow_transition_to , wont_allow_transition_to
เพิ่ม require 'aasm/minitest_spec' ในไฟล์ test_helper.rb ของคุณและใช้เช่นนี้:
# classes with only the default state machine
job = Job . new
job . must_transition_from :sleeping , to : :running , on_event : :run
job . wont_transition_from :sleeping , to : :cleaning , on_event : :run
job . must_have_state :sleeping
job . wont_have_state :running
job . must_allow_event :run
job . wont_allow_event :clean
job . must_allow_transition_to :running
job . wont_allow_transition_to :cleaning
# on_event also accept arguments
job . must_transition_from :sleeping , :defragmentation , to : :running , on_event : :run
# classes with multiple state machine
multiple = SimpleMultipleExample . new
multiple . must_transition_from :standing , to : :walking , on_event : :walk , on : :move
multiple . wont_transition_from :standing , to : :running , on_event : :walk , on : :move
multiple . must_have_state :standing , on : :move
multiple . wont_have_state :walking , on : :move
multiple . must_allow_event :walk , on : :move
multiple . wont_allow_event :hold , on : :move
multiple . must_allow_transition_to :walking , on : :move
multiple . wont_allow_transition_to :running , on : :move
multiple . must_transition_from :sleeping , to : :processing , on_event : :start , on : :work
multiple . wont_transition_from :sleeping , to : :sleeping , on_event : :start , on : :work
multiple . must_have_state :sleeping , on : :work
multiple . wont_have_state :processing , on : :work
multiple . must_allow_event :start , on : :move
multiple . wont_allow_event :stop , on : :move
multiple . must_allow_transition_to :processing , on : :move
multiple . wont_allow_transition_to :sleeping , on : :move % gem install aasm # Gemfile
gem 'aasm'% rake build
% sudo gem install pkg/aasm-x.y.z.gemหลังจากติดตั้ง AASM คุณสามารถเรียกใช้เครื่องกำเนิดไฟฟ้า:
% rails generate aasm NAME [COLUMN_NAME]แทนที่ชื่อด้วยชื่อรุ่น, column_name เป็นตัวเลือก (ค่าเริ่มต้นคือ 'aasm_state') สิ่งนี้จะสร้างโมเดล (ถ้าไม่มีอยู่) และกำหนดค่าด้วย AASM Block สำหรับ Activerecord orm ไฟล์การย้ายถิ่นจะถูกเพิ่มเพื่อเพิ่มคอลัมน์ AASM State ลงในตาราง
เรียกใช้ชุดทดสอบได้อย่างง่ายดายบน Docker
1. docker-compose build aasm
2. docker-compose run --rm aasm
ลองดูที่ Changelog สำหรับรายละเอียดเกี่ยวกับการเปลี่ยนแปลงล่าสุดของเวอร์ชันปัจจุบัน
อย่าลังเล
aasm )ซอฟต์แวร์นี้มีให้ "ตามสภาพ" และไม่มีการรับประกันโดยชัดแจ้งหรือโดยนัยรวมถึง แต่ไม่ จำกัด เพียงการรับประกันโดยนัยของการค้าและความเหมาะสมสำหรับวัตถุประสงค์เฉพาะ
ลิขสิทธิ์ (c) 2549-2560 Scott Barron
ได้รับอนุญาตโดยไม่ต้องเสียค่าใช้จ่ายสำหรับบุคคลใด ๆ ที่ได้รับสำเนาซอฟต์แวร์นี้และไฟล์เอกสารที่เกี่ยวข้อง ("ซอฟต์แวร์") เพื่อจัดการในซอฟต์แวร์โดยไม่มีการ จำกัด รวมถึง แต่ไม่ จำกัด เฉพาะสิทธิ์ในการใช้สำเนาดัดแปลงผสานเผยแพร่เผยแพร่
ประกาศลิขสิทธิ์ข้างต้นและประกาศการอนุญาตนี้จะรวมอยู่ในสำเนาทั้งหมดหรือส่วนสำคัญของซอฟต์แวร์
ซอฟต์แวร์มีให้ "ตามสภาพ" โดยไม่มีการรับประกันใด ๆ ไม่ว่าโดยชัดแจ้งหรือโดยนัยรวมถึง แต่ไม่ จำกัด เฉพาะการรับประกันความสามารถในการค้าการออกกำลังกายสำหรับวัตถุประสงค์เฉพาะและการไม่เข้าร่วม ไม่ว่าในกรณีใดผู้เขียนหรือผู้ถือลิขสิทธิ์จะต้องรับผิดชอบต่อการเรียกร้องความเสียหายหรือความรับผิดอื่น ๆ ไม่ว่าจะเป็นการกระทำของสัญญาการละเมิดหรืออื่น ๆ ที่เกิดขึ้นจากหรือเกี่ยวข้องกับซอฟต์แวร์หรือการใช้งานหรือการติดต่ออื่น ๆ ในซอฟต์แวร์