Dieses Paket enthält AASM, eine Bibliothek zum Hinzufügen endlicher Zustandsmaschinen zu Ruby -Klassen.
AASM begann als ACTS_AS_STATE_MACHINE -Plugin, hat sich jedoch zu einer generischen Bibliothek entwickelt, die nicht mehr nur ActivereCord -Modelle abzielt. Es bietet derzeit Adapter für viele Ormen, kann jedoch für eine Ruby -Klasse verwendet werden, unabhängig davon, welche übergeordnete Klasse es hat (falls vorhanden).
Schauen Sie sich die Readme_From_VERSION_3_TO_4 an, um Details zu erhalten, wie Sie von Version 3.x auf 4.0 von AASM wechseln.
Das Hinzufügen einer Zustandsmaschine ist so einfach wie das AASM -Modul und die Definition von Zuständen und Ereignissen zusammen mit ihren Übergängen :
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 Dies bietet Ihnen einige öffentliche Methoden für Instanzen des 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 Wenn Sie Ausnahmen nicht mögen und eine einfache true oder false als Antwort bevorzugen, sagen Sie, dass AASM nicht weinerlich ist:
class Job
...
aasm whiny_transitions : false do
...
end
end
job . running? # => true
job . may_run? # => false
job . run # => falseWenn Sie ein Ereignis abfeuern, können Sie einen Block an die Methode übergeben. Er wird nur aufgerufen, wenn der Übergang erfolgreich ist:
job . run do
job . user . notify_job_ran # Will be called if job.may_run? is true
endSie können eine Reihe von Rückrufen für Ihre Ereignisse, Übergänge und Zustände definieren. Diese Methoden, Procs oder Klassen werden aufgerufen, wenn bestimmte Kriterien erfüllt sind, wie beispielsweise in einen bestimmten Zustand:
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 In diesem Fall wird do_something aufgerufen, bevor es tatsächlich in den Status sleeping , während notify_somebody nach dem run (vom sleeping zum running ) beendet wird.
AasM wird auch LogRunTime initialisieren und die call für Sie nach dem Übergang vom running zum finished im obigen Beispiel ausführen. Sie können Argumente an die Klasse weitergeben, indem Sie eine initialisierende Methode wie folgt definieren:
Beachten Sie, dass Procs im Kontext eines Datensatzes ausgeführt werden. Dies bedeutet, dass Sie den Datensatz nicht als Argument erwarten müssen. Rufen Sie einfach die von Ihnen benötigten Methoden auf.
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 Sie können Parameter an Ereignisse übergeben:
job = Job . new
job . run ( :defragmentation ) Alle Wachen und nach Rückrufen erhalten diese Parameter. In diesem Fall würde set_process mit :defragmentation aufgerufen.
Wenn das erste Argument für das Ereignis ein Staat ist (z :running oder :finished ), wird das erste Argument verbraucht und die staatliche Maschine wird versuchen, in diesen Zustand zu wechseln. Fügen Sie den Parameter getrennter Komma für Wachen und Rückrufe hinzu
job = Job . new
job . run ( :running , :defragmentation ) In diesem Fall wird set_process nicht angerufen, der Job wird in den laufenden Status übergeht und der Rückruf erhält :defragmentation als Parameter
Bei einem Fehler während der Ereignisverarbeitung wird der Fehler gerettet und an :error Fehlerrückruf übergeben, der ihn zur weiteren Ausbreitung umgehen oder erneut sammeln kann.
Außerdem können Sie eine Methode definieren, die aufgerufen wird, wenn ein Ereignis fehlschlägt:
def aasm_event_failed ( event_name , old_state_name )
# use custom exception/messages, report metrics, etc
end Während des Übergangs :after Rückruf (und zuverlässig nur dann oder im globalen Rückruf after_all_transitions ) können Sie auf den Ursprungsstatus (den From-State) und den Zielzustand (der Status) zugreifen:
def set_process ( name )
logger . info "from #{ aasm . from_state } to #{ aasm . to_state } "
end Hier sehen Sie eine Liste aller möglichen Rückrufe zusammen mit ihrer Aufrufreihenfolge:
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 Verwenden Sie Events after_commit -Rückruf, wenn er nach dem Datenbank -Update abgefeuert werden soll.
Während Sie die Rückrufe ausführen, können Sie den Namen des Ereignisses, der ausgelöst wird, mithilfe von aasm.current_event einfach abrufen:
# taken the example callback from above
def do_something
puts "triggered #{ aasm . current_event } "
endund dann
job = Job . new
# without bang
job . sleep # => triggered :sleep
# with bang
job . sleep! # => triggered :sleep! Nehmen wir an, Sie möchten bestimmte Übergänge nur dann zulassen, wenn eine definierte Bedingung angegeben wird. Dazu können Sie einen Wachmann pro Übergang einrichten, der vor dem Ausführen des Übergangs ausgeführt wird. Wenn die Wache false zurückgibt, wird der Übergang abgelehnt (erhöhen 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 ) # => trueSie können sogar eine Reihe von Wachen zur Verfügung stellen, die alle erfolgreich sein müssen, um fortzufahren
def walked_the_dog? ; ... ; end
event :sleep do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? , :walked_the_dog? ]
endWenn Sie Wachen für alle Übergänge innerhalb eines Ereignisses zur Verfügung stellen möchten, können Sie Event Guards verwenden
event :sleep , guards : [ :walked_the_dog? ] do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? ]
transitions from : :cleaning , to : :sleeping
end Wenn Sie eine rubinähnlichere Wach-Syntax bevorzugen, können Sie auch dann verwenden, if auch 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 Sie können eine Klasse anstelle einer Methode aufrufen, wenn die Klasse auf call antwortet
event :sleep do
transitions from : :running , to : :sleeping , guards : Dog
end class Dog
def call
cleaning_needed? && walked?
end
...
endBei mehreren Übergängen für ein Ereignis wird der erste Übergang, der erfolgreich abgeschlossen wird, andere Übergänge im selben Ereignis davon abhalten, verarbeitet zu werden.
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 Sie können den Übergang von jedem definierten Zustand definieren, indem from :
event :abort do
transitions to : :aborted
endSie können den Anzeigeamen für Status mithilfe der: Anzeigeoption definieren
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 Mehrere Staatsmaschinen pro Klasse werden unterstützt. Seien Sie sich jedoch bewusst, dass AASM mit berücksichtigender Zustandsmaschine pro Klasse gebaut wurde. Hier erfahren Sie jedoch, wie es geht (siehe unten). Bitte beachten Sie, dass Sie Datenbankspalten angeben müssen, wenn Ihre relevanten Zustände gespeichert werden. Wir haben im folgenden Beispiel zwei Spalten move_state und work_state angegeben. Weitere Informationen finden Sie im Abschnitt Spaltenname und Migration.
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 verbietet es nicht, dasselbe Ereignis in mehr als einer Zustandsmaschine zu definieren. Wenn kein Namespace bereitgestellt wird, ist die neueste Definition "gewinnt" und überschreibt frühere Definitionen. Trotzdem wird eine Warnung ausgegeben: SimpleMultipleExample: overriding method 'run'! .
Alternativ können Sie für jede Statusmaschine einen Namespace bereitstellen:
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 Alle aasm -Methoden der AASM -Klassen- und Instanzebene akzeptieren einen Zustandsmaschinenauswahl. Zum Beispiel müssen Sie zum Beispiel die Inspektion auf einer Klassenebene verwenden, und müssen Sie verwenden
SimpleMultipleExample . aasm ( :move ) . states . map ( & :name )
# => [:standing, :walking, :running]Lassen Sie ein Ereignis an ein anderes gebunden sein
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 generiert automatisch Konstanten für jeden Status, sodass Sie sie nicht explizit definieren müssen.
class Foo
include AASM
aasm do
state :initialized
state :calculated
state :finalized
end
end
> Foo :: STATE_INITIALIZED
#=> :initialized
> Foo :: STATE_CALCULATED
#=> :calculated Mit AMASM können Sie AASM::Base für Ihre eigenen Anwendungszwecke problemlos erweitern.
Nehmen wir an, wir haben eine gemeinsame Logik in vielen AASM -Modellen. Wir können diese Logik in einer Unterklasse von AASM::Base verkörpern.
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 Wenn wir unser Modell erklären, das eine AASM -Zustandsmaschine hat, deklarieren wir einfach den AASM -Block mit A :with_klass Key für unsere eigene Klasse.
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
endAMASM ist mit Unterstützung von ActivereCord unterstützt und ermöglicht die automatische Beständigkeit des Objektstatus in der Datenbank.
Fügen Sie Ihrem GemFile gem 'after_commit_everywhere', '~> 1.0' hinzu.
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
endSie können AASM erkennen, dass er das Objekt automatisch speichern oder nicht gerettet lassen
job = Job . new
job . run # not saved
job . run! # saved
# or
job . aasm . fire ( :run ) # not saved
job . aasm . fire! ( :run ) # saved Das Speichern beinhaltet das Ausführen aller Validierungen in der Job . Wenn die Flagge whiny_persistence auf true gesetzt ist, wird die Ausnahme im Falle eines Scheiterns erhöht. Wenn das Flag whiny_persistence auf false gesetzt ist, gibt Methoden mit einem Knall true zurück, wenn der Zustandsübergang erfolgreich oder false ist, wenn ein Fehler auftritt.
Wenn Sie sicherstellen möchten, dass der Staat ohne Validierungen gespeichert wird (und dadurch möglicherweise einen ungültigen Objektstatus bestehen), sagen Sie einfach, dass er die Validierungen überspringen soll. Beachten Sie, dass beim Überspringen von Validierungen nur die Statusspalte in der Datenbank aktualisiert wird (genau wie ActiveCord update_column funktioniert).
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 Außerdem können Sie die Validierung auf Instanzebene mit some_event_name_without_validation! Verfahren. Damit haben Sie die Flexibilität, standardmäßig für alle Ihre Übergänge zu validieren, und überspringen Sie sie dann, wo immer dies erforderlich ist. Bitte beachten Sie, dass nur die Spalte der Status wie im obigen Beispiel erwähnt wird.
job . run_without_validation!Wenn Sie sicherstellen möchten, dass die AASM -Spalte zum Speichern des Zustands nicht direkt zugewiesen ist, konfigurieren Sie AASM so, dass sie keine direkte Zuordnung wie folgt zulassen:
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
endDies führt dazu:
job = Job . create
job . aasm_state # => 'sleeping'
job . aasm_state = :running # => raises AASM::NoDirectAssignmentError
job . aasm_state # => 'sleeping' Sie können AasM angeben, dass er versuchen soll, einen Zeitstempel zu schreiben, wenn ein neuer Zustand eingegeben wird. Wenn timestamps: true festgelegt ist, sucht AasM nach einem Feld wie dem neuen Status plus _at und versuchen Sie, es zu füllen:
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
endDies führt dazu:
job = Job . create
job . running_at # => nil
job . run!
job . running_at # => 2020-02-20 20:00:00Fehlende Zeitstempelfelder werden stillschweigend ignoriert, sodass bei Verwendung dieser Option keine Setzer (z. B. ActivereCord -Spalten) für alle Zustände haben müssen.
Sie können Aufzählungen in Rails 4.1+ für Ihre Statusspalte verwenden:
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 Sie können den Namen der Methode explizit übergeben, der Zugriff auf die Zählerzuordnung als Wert von enum ermöglicht, oder Sie können ihn einfach auf true einstellen. Im letzteren Fall versucht AasM, den Namen des pluralisierten Spaltennamens zu verwenden, um auf mögliche Enum -Zustände zuzugreifen.
Wenn Ihre Spalte einen ganzzahligen Typ enthält (was normalerweise bei der Arbeit mit Rails Enums der Fall ist) können Sie :enum Einstellung weglassen --- AMASM automatisch kennzeichnet diese Situation und aktiviert Enum-Unterstützung. Wenn etwas schief geht, können Sie die Enum -Funktionalität deaktivieren und auf das Standardverhalten zurückgreifen, indem Sie :enum auf false zurückgreifen.
AASM unterstützt auch die Folge neben Activerecord und Mongoid .
class Job < Sequel :: Model
include AASM
aasm do # default column: aasm_state
...
end
endEs ist jedoch noch nicht so abgeschlossen wie Activerecord . Zum Beispiel gibt es noch Bereiche, die Bereiche definiert sind. Siehe automatische Bereiche.
Seit Version 4.8.0 AASM unterstützt auch Dynamoid als Persistenz ORM.
AASM unterstützt auch die Persistenz für MongoDB, wenn Sie Mongoid verwenden. Stellen Sie sicher, dass Sie Mongoid :: Dokument enthalten, bevor Sie AASM aufnehmen.
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM unterstützt auch die Beharrlichkeit, wenn Sie Nobrainer verwenden. Stellen Sie sicher, dass Sie Nobrainer :: Dokument aufnehmen, bevor Sie AASM aufnehmen.
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM unterstützt auch Persistenz in Redis über Redis :: Objekte. Stellen Sie sicher, dass Redis :: Objekte vorgenommen werden, bevor Sie AASM aufnehmen. Beachten Sie, dass Nicht-Bang-Ereignisse als Bang-Ereignisse funktionieren und die Änderungen bei jedem Anruf bestehen.
class User
include Redis :: Objects
include AASM
aasm do
end
endAasM erstellt automatisch Umfangsmethoden für jeden Zustand im Modell.
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 Wenn Sie keine Bereiche benötigen (oder sie einfach nicht wollen), deaktivieren Sie ihre Schöpfung, wenn Sie die AASM -Zustände wie folgt definieren:
class Job < ActiveRecord :: Base
include AASM
aasm create_scopes : false do
state :sleeping , initial : true
state :running
state :cleaning
end
endSeit Version 3.0.13 unterstützt AASM ACTIERRECORD -Transaktionen. Wenn also ein Übergangsrückruf oder das Status -Update fehlschlägt, werden alle Änderungen an einem Datenbankdatensatz zurückgerollt. MongoDB unterstützt keine Transaktionen.
Derzeit gibt es 3 Transaktions -Rückrufe, die auf der Veranstaltung und 2 Transaktions -Rückrufe für alle Ereignisse behandelt werden können.
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 Wenn Sie sicherstellen möchten, dass eine abhängige Aktion erst nach der Verpflichtung der Transaktion stattfindet, verwenden Sie den after_commit Rückruf zusammen mit den automatischen (Bang-) Methoden wie folgt:
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 Beachten Sie, dass die folgenden after_commit nicht ausführen, da die automatische Save-Methode nicht verwendet wird:
job = Job . where ( state : 'sleeping' ) . first!
job . run
job . save! #notify_about_running_job is not run Bitte beachten Sie, dass :after_commit AasM-Rückrufe sich eher um eine benutzerdefinierte Implementierung des Transaktionsmusters als um eine reale DB-Transaktion verhalten. Diese Tatsache führt immer noch zu den Rennbedingungen und den redundanten Rückrufanrufen innerhalb der verschachtelten Transaktion. Um festzustellen, dass es dringend empfohlen wird, gem 'after_commit_everywhere', '~> 1.0' zu deinem Gemfile hinzuzufügen.
Wenn Sie staatliche Änderungen innerhalb einer eigenen Transaktion zusammenfassen möchten, kann das Verhalten dieser verschachtelten Transaktion verwirrend sein. Werfen Sie einen Blick auf ActiveCord Nested Transactions, wenn Sie mehr darüber erfahren möchten. Dennoch erfordert AASM standardmäßig eine neue transaction(requires_new: true) . Sie können dieses Verhalten überschreiben, indem Sie die Konfiguration ändern
class Job < ActiveRecord :: Base
include AASM
aasm requires_new_transaction : false do
...
end
...
end Dies führt dann zu transaction(requires_new: false) , die Schienen standardmäßig.
Wenn Sie nicht möchten, dass eine Ihrer ActiveCord -Aktionen in eine Transaktion eingewickelt wird, können Sie das Gebrauchsflag use_transactions angeben. Dies kann nützlich sein, wenn Sie die Dinge in der Datenbank fortsetzen möchten, die aufgrund einer Transaktion oder eines Rückrufs geschehen, auch wenn ein Fehler auftritt. Das use_transactions -Flag ist standardmäßig wahr.
class Job < ActiveRecord :: Base
include AASM
aasm use_transactions : false do
...
end
...
end AASM unterstützt ActiveCord Pessimistische Verriegelung über with_lock der Datenbank -Persistenzschichten.
| Option | Zweck |
|---|---|
false (Standard) | Es wird kein Schloss erhalten |
true | Erhalten Sie eine blockierende pessimistische Sperre, EG FOR UPDATE |
| Saite | Erhalten Sie eine Sperre basierend auf der SQL -String -EG 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 Als Standard verwendet AASM die Spalte aasm_state , um die Zustände zu speichern. Sie können dies überschreiben, indem Sie Ihren bevorzugten Spaltennamen mit :column wie folgt definieren:
class Job < ActiveRecord :: Base
include AASM
aasm column : :my_state do
...
end
aasm :another_state_machine , column : :second_state do
...
end
end Unabhängig davon, welcher Spaltenname verwendet wird, stellen Sie sicher, dass Sie eine Migration hinzufügen, um diese Spalte (vom Typ string ) bereitzustellen. Fügen Sie auf der Datenbankebene keinen Standardwert für die Spalte hinzu. Wenn Sie den Standardwert in der Datenbank hinzufügen, werden AASM -Rückrufe im Ausgangszustand bei der Instanziierung des Modells nicht abgefeuert.
class AddJobState < ActiveRecord :: Migration
def self . up
add_column :jobs , :aasm_state , :string
end
def self . down
remove_column :jobs , :aasm_state
end
endDie Änderung des Protokollierungszustands kann mit paper_trail Gem durchgeführt werden
Beispiel für die Implementierung finden Sie hier https://github.com/nitsujri/aasm-papertrail-plample
AASM unterstützt Abfragemethoden für Zustände und Ereignisse
Angesichts der folgenden 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 ) Warnungen werden standardmäßig nach STDERR gedruckt. Wenn Sie diese Warnungen für eine andere Ausgabe protokollieren möchten, verwenden Sie
class Job
include AASM
aasm logger : Rails . logger do
...
end
end Sie können Warnungen ausblenden, indem Sie AASM::Configuration.hide_warnings = true einstellen
Unterstützt jetzt codedataquery! Ich bin jedoch immer noch dabei, meine Kompatibilitätsaktualisierungen an ihr Repository einzureichen. In der Zwischenzeit können Sie meine Gabel verwenden, es gibt möglicherweise noch einige kleinere Probleme, aber ich beabsichtige, sie selbst ausgiebig zu verwenden. Daher sollten die Korrekturen schnell kommen.
Warnungen:
AASM liefert einige Matcher für RSPEC:
transition_from ,have_state , allow_eventallow_transition_to . spec_helper.rb -Datei require 'aasm/rspec' . # 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 liefert Aussagen und RSPEC-ähnliche Erwartungen für Minitest.
Liste der unterstützten Behauptungen: 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 .
Fügen require 'aasm/minitest' Ihrer Datei test_helper.rb hinzu und verwenden Sie sie wie folgt:
# 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 Liste der unterstützten Erwartungen: 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 .
Fügen Sie Ihrer Datei test_helper.rb zu require 'aasm/minitest_spec' hinzu und verwenden Sie sie wie folgt:
# 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.gemNach der Installation von AASM können Sie den Generator ausführen:
% rails generate aasm NAME [COLUMN_NAME]Ersetzen Sie den Namen durch den Modellnamen, Column_Name ist optional (Standard ist 'aasm_state'). Dadurch wird ein Modell erstellt (falls es nicht existiert) und konfigurieren es mit einem AASM -Block. Für ActivereCord ORM wird eine Migrationsdatei hinzugefügt, um der Tabelle eine Spalte der AASM -Status hinzuzufügen.
Führen Sie die Testsuite leicht auf Docker aus
1. docker-compose build aasm
2. docker-compose run --rm aasm
Schauen Sie sich den ChangeLog an, um Einzelheiten zu den letzten Änderungen der aktuellen Version zu erhalten.
Fühlen Sie sich frei zu
aasm )Diese Software wird "wie es ist" und ohne ausdrückliche oder implizite Garantien bereitgestellt, einschließlich der impliziten Garantien für Handelsbarkeit und Eignung für einen bestimmten Zweck ohne Einschränkung.
Copyright (C) 2006-2017 Scott Barron
Die Erlaubnis wird hiermit einer Person, die eine Kopie dieser Software und zugehörigen Dokumentationsdateien (der "Software") erhält, kostenlos erteilt, um die Software ohne Einschränkung zu behandeln, einschließlich ohne Einschränkung der Rechte, zu verwenden, zu kopieren, zu modifizieren, zusammenzufassen, zu veröffentlichen, zu veröffentlichen, zu verteilen, zu verteilt, und/oder Kopien der Software zu ermöglichen, um Personen zu beanstanden, an denen die Software zugänglich ist, um die folgenden Bedingungen zu beantragen.
Die oben genannte Copyright -Mitteilung und diese Erlaubnisbekanntmachung müssen in alle Kopien oder wesentlichen Teile der Software enthalten sein.
Die Software wird "wie es ist" ohne Garantie jeglicher Art, ausdrücklich oder stillschweigend bereitgestellt, einschließlich, aber nicht beschränkt auf die Gewährleistung der Handelsfähigkeit, die Eignung für einen bestimmten Zweck und die Nichtverletzung. In keinem Fall sind die Autoren oder Urheberrechtsinhaber für Ansprüche, Schäden oder andere Haftungen haftbar, sei es in einer Vertragsklage, unerbittlich oder auf andere Weise, die sich aus oder im Zusammenhang mit der Software oder anderen Geschäften in der Software ergeben.