تحتوي هذه الحزمة على AASM ، وهي مكتبة لإضافة آلات الحالة المحدودة إلى فصول الياقوت.
بدأ AASM كمكوّن إضافي ACTS_AS_STATE_MACHINE ولكنه تطور إلى مكتبة أكثر عامة لم تعد تستهدف نماذج ACTIVERECORD فقط. يوفر حاليًا محولات للعديد من ORMS ولكن يمكن استخدامه لأي فصل روبي ، بغض النظر عن فئة الوالدين التي لديها (إن وجدت).
ألقِ نظرة على 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 بسيطة ، فأخبر AASM أن يكون متشوقًا :
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يمكنك تحديد عدد من عمليات الاسترجاعات للأحداث والتحولات والولايات. سيتم استدعاء هذه الأساليب أو البرامج أو الفئات عند استيفاء معايير معينة ، مثل دخول حالة معينة:
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 event_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 إذا كنت تفضل بناء جملة واقي شبيهة بالروبي ، فيمكنك استخدام 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 ومستوى 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 #=> :askedيقوم AASM تلقائيًا بإنشاء ثوابت لكل حالة حتى لا تضطر إلى تعريفها بشكل صريح.
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 ، فإننا نعلن ببساطة كتلة AASM باستخدام مفتاح :with_klass لفصلنا.
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
endيأتي AASM مع دعم 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يمكنك إخبار AASAS بتوضيح الكائن أو تركه غير محفوظ
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 ، فستكون الأساليب ذات الإرجاع 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 إعداد --- AASM يكتشف هذا الموقف ودعم التعداد الممكّن. إذا حدث خطأ ما ، فيمكنك تعطيل وظائف التعداد والتراجع إلى السلوك الافتراضي عن طريق الإعداد :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 كمثابرة.
يدعم AASM أيضًا الثبات إلى MongoDB إذا كنت تستخدم mongoid. تأكد من تضمين mongoid :: وثيقة قبل تضمين AASM.
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endيدعم AASM أيضًا الثبات لإعادة التفكير إذا كنت تستخدم Nobrainer. تأكد من تضمين Nobrainer :: Document قبل تضمين AASM.
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM يدعم أيضًا الثبات في redis عبر redis :: objects. تأكد من تضمين Redis :: Objects قبل تضمين AASM. لاحظ أن الأحداث غير المحببة ستعمل كأحداث بانغ ، وتستمر التغييرات في كل مكالمة.
class User
include Redis :: Objects
include AASM
aasm do
end
endسيقوم AASM تلقائيًا بإنشاء طرق نطاق لكل حالة في النموذج.
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 عمليات استدعاء للمعاملات التي يمكن معالجتها على الحدث ، واستراعات معاملات لجميع الأحداث.
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 مع أساليب (الانفجار) التلقائي ، مثل هذا:
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) ، الافتراضي القضبان.
بالإضافة إلى ذلك ، إذا كنت لا تريد أن يتم لف أي من إجراءات ActivereCord في معاملة ، فيمكنك تحديد علامة 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يمكن إجراء تغيير حالة التسجيل باستخدام GEM Paper_Trail
يمكن العثور على مثال على التنفيذ هنا https://github.com/nitsujri/Aasm-Papertrail-example
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_from ،have_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 لأفضل MINITIST.
قائمة التأكيدات المدعومة: assert_have_state ، refute_have_state ، assert_transitions_from ، refute_transitions_from ، assert_event_allowed ، refute_event_allowed ، assert_transition_to_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. بالنسبة إلى ActivereCord ، تتم إضافة ملف ترحيل لإضافة عمود حالة AASM إلى الجدول.
قم بتشغيل جناح الاختبار بسهولة على Docker
1. docker-compose build aasm
2. docker-compose run --rm aasm
ألقِ نظرة على changelog للحصول على تفاصيل حول التغييرات الحديثة على الإصدار الحالي.
لا تتردد في
aasm )يتم توفير هذا البرنامج "كما هو" وبدون أي ضمانات صريحة أو ضمنية ، بما في ذلك ، على سبيل المثال لا الحصر ، الضمانات الضمنية للتجار واللياقة لغرض معين.
حقوق الطبع والنشر (C) 2006-2017 Scott Barron
يتم منح الإذن بموجب هذا ، مجانًا ، لأي شخص يحصل على نسخة من هذا البرنامج وملفات الوثائق المرتبطة به ("البرنامج") ، للتعامل في البرنامج دون تقييد ، بما في ذلك على سبيل المثال لا الحصر حقوق استخدام الأشخاص ونسخها ودمجها ودمجها وتوزيعها وتوزيعها على ما يلي:
يجب إدراج إشعار حقوق الطبع والنشر أعلاه وإشعار الإذن هذا في جميع النسخ أو الأجزاء الكبيرة من البرنامج.
يتم توفير البرنامج "كما هو" ، دون أي ضمان من أي نوع ، صريح أو ضمني ، بما في ذلك على سبيل المثال لا الحصر ضمانات القابلية للتسويق واللياقة لغرض معين وعدم الانفجار. لا يجوز بأي حال من الأحوال أن يكون المؤلفون أو حاملي حقوق الطبع والنشر مسؤولاً عن أي مطالبة أو أضرار أو مسؤولية أخرى ، سواء في إجراء عقد أو ضرر أو غير ذلك ، ناشئة عن أو خارج البرنامج أو الاستخدام أو غيرها من المعاملات في البرنامج.