Este paquete contiene AASM, una biblioteca para agregar máquinas de estado finitas a las clases de Ruby.
AASM comenzó como el complemento ACTS_AS_STATE_MACHINE , pero se ha convertido en una biblioteca más genérica que ya no se dirige solo a los modelos Activerecord. Actualmente proporciona adaptadores para muchos ORM, pero se puede usar para cualquier clase de Ruby, sin importar la clase principal que tenga (si corresponde).
Eche un vistazo a ReadMe_From_Version_3_TO_4 para más detalles sobre cómo cambiar de la versión 3.x a 4.0 de AASM .
Agregar una máquina de estado es tan simple como incluir el módulo AASM y comenzar a definir estados y eventos junto con sus transiciones :
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 Esto le proporciona un par de métodos públicos para instancias del Job de clase:
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 Si no le gustan las excepciones y prefiere una respuesta simple o false true , dígale a Aasm que no sea quejumbroso :
class Job
...
aasm whiny_transitions : false do
...
end
end
job . running? # => true
job . may_run? # => false
job . run # => falseAl disparar un evento, puede pasar un bloque al método, se llamará solo si la transición tiene éxito:
job . run do
job . user . notify_job_ran # Will be called if job.may_run? is true
endPuede definir una serie de devoluciones de llamada para sus eventos, transiciones y estados. Estos métodos, procesos o clases se llamarán cuando se cumplan ciertos criterios, como ingresar a un estado particular:
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 En este caso, do_something se llama antes de ingresar al estado sleeping , mientras que notify_somebody se llama después de que se termine la run de transición (de sleeping a running ).
AASM también inicializará LogRunTime y ejecutará el método call para usted después de la transición de running a finished en el ejemplo anterior. Puede transmitir argumentos a la clase definiendo un método de inicialización en ella, así:
Tenga en cuenta que los procesos se ejecutan en el contexto de un registro, significa que no necesita esperar el registro como argumento, solo llame a los métodos que necesita.
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 Puede pasar los parámetros a los eventos:
job = Job . new
job . run ( :defragmentation ) Todos los guardias y después de las devoluciones de llamada recibirán estos parámetros. En este caso, set_process se llamaría con :defragmentation .
Si el primer argumento al evento es un estado (por ejemplo :running o :finished ), se consume el primer argumento y la máquina estatal intentará hacer la transición a ese estado. Agregue el parámetro separado por coma para guardias y devoluciones de llamada
job = Job . new
job . run ( :running , :defragmentation ) En este caso, no se llamará a set_process , el trabajo hará la transición al estado en ejecución y la devolución de llamada recibirá :defragmentation como parámetro
En el caso de un error durante el procesamiento del evento, el error se rescata y se pasa a :error , que puede manejarlo o volver a criarlo para una mayor propagación.
Además, puede definir un método que se llamará si algún evento falla:
def aasm_event_failed ( event_name , old_state_name )
# use custom exception/messages, report metrics, etc
end Durante la transición :after la devolución de llamada (y de manera confiable solo entonces, o en la devolución global after_all_transitions ) puede acceder al estado de origen (el estado desde el estado) y el estado objetivo (el estado), como este:
def set_process ( name )
logger . info "from #{ aasm . from_state } to #{ aasm . to_state } "
end Aquí puede ver una lista de todas las devoluciones de llamada posibles, junto con su orden de llamadas:
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 Use la devolución de llamada after_commit de Event si se debe disparar después de la actualización de la base de datos.
Mientras ejecuta las devoluciones de llamada, puede recuperar fácilmente el nombre del evento activado usando aasm.current_event :
# taken the example callback from above
def do_something
puts "triggered #{ aasm . current_event } "
endy luego
job = Job . new
# without bang
job . sleep # => triggered :sleep
# with bang
job . sleep! # => triggered :sleep! Supongamos que desea permitir transiciones particulares solo si se da una condición definida. Para esto, puede configurar un guardia por transición, que se ejecutará antes de ejecutar la transición. Si el guardia regresa false se negará la transición (elevando 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 ) # => trueIncluso puede proporcionar una serie de guardias, que todos tienen que tener éxito para proceder
def walked_the_dog? ; ... ; end
event :sleep do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? , :walked_the_dog? ]
endSi desea proporcionar guardias para todas las transiciones dentro de un evento, puede usar los guardias de eventos
event :sleep , guards : [ :walked_the_dog? ] do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? ]
transitions from : :cleaning , to : :sleeping
end Si prefiere una sintaxis de protección más rubí, puede usar if y unless que también:
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 Puede invocar una clase en lugar de un método si la clase responde a call
event :sleep do
transitions from : :running , to : :sleeping , guards : Dog
end class Dog
def call
cleaning_needed? && walked?
end
...
endEn el caso de tener múltiples transiciones para un evento, la primera transición que se completa con éxito evitará que se procesen otras transiciones en el mismo evento.
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 Puede definir la transición de cualquier estado definido omitiendo from :
event :abort do
transitions to : :aborted
endPuede definir el nombre de la pantalla para el estado usando: opción de visualización
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 Se admiten múltiples máquinas estatales por clase. Sin embargo, tenga en cuenta que AASM se ha construido con una máquina de estado por clase en mente. No obstante, aquí le mostramos cómo hacerlo (ver más abajo). Tenga en cuenta que deberá especificar columnas de bases de datos para dónde se almacenarán sus estados pertinentes; hemos especificado dos columnas move_state y work_state en el siguiente ejemplo. Consulte la sección Nombre y migración de la columna para obtener más información.
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 no prohíbe definir el mismo evento en más de una máquina de estado. Si no se proporciona ningún espacio de nombres, la última definición "gana" y anula definiciones anteriores. No obstante, se emite una advertencia: SimpleMultipleExample: overriding method 'run'! .
Alternativamente, puede proporcionar un espacio de nombres para cada máquina de estado:
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 Todos los métodos AASM de clase y nivel de instancia aasm aceptan un selector de máquinas de estado. Entonces, por ejemplo, para usar la inspección en un nivel de clase, debe usar
SimpleMultipleExample . aasm ( :move ) . states . map ( & :name )
# => [:standing, :walking, :running]Permitir que un evento esté atado a otro
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 genera automáticamente constantes para cada estado para que no tenga que definirlos explícitamente.
class Foo
include AASM
aasm do
state :initialized
state :calculated
state :finalized
end
end
> Foo :: STATE_INITIALIZED
#=> :initialized
> Foo :: STATE_CALCULATED
#=> :calculated AASM le permite extender fácilmente AASM::Base para sus propios fines de aplicación.
Supongamos que tenemos una lógica común en muchos modelos AASM. Podemos encarnar esta lógica en una subclase de 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 Cuando declaramos nuestro modelo que tiene una máquina de estado AASM, simplemente declaramos el bloque de AASM con una clave :with_klass clave con nuestra propia clase.
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 viene con soporte para Activerecord y permite la persistir automática del estado del objeto en la base de datos.
Agregue gem 'after_commit_everywhere', '~> 1.0' a su archivo gem.
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
endPuede decirle a AASM que guarde el objeto o deje que lo deje sin salvamento
job = Job . new
job . run # not saved
job . run! # saved
# or
job . aasm . fire ( :run ) # not saved
job . aasm . fire! ( :run ) # saved Guardar incluye ejecutar todas las validaciones en la clase Job . Si el indicador whiny_persistence se establece en true , se plantea la excepción en caso de falla. Si el indicador whiny_persistence se establece en false , los métodos con un retorno de Bang true si la transición de estado es exitosa o false si se produce un error.
Si desea, asegúrese de que el estado se guarde sin ejecutar validaciones (y por lo tanto, tal vez persistir un estado de objeto no válido), simplemente dígale a AASM que saltea las validaciones. Tenga en cuenta que al omitir las validaciones, solo la columna de estado se actualizará en la base de datos (al igual que actúa de Activerecord, update_column está funcionando).
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 Además, ¡puede omitir la validación a nivel de instancia con some_event_name_without_validation! método. Con esto, tiene la flexibilidad de tener validación para todas sus transiciones de forma predeterminada y luego omitirlo donde sea necesario. Tenga en cuenta que solo la columna de estado se actualizará como se menciona en el ejemplo anterior.
job . run_without_validation!Si desea asegurarse de que la columna AASM para almacenar el estado no esté asignada directamente, configure AASM para no permitir la asignación directa, así:
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
endresultando en esto:
job = Job . create
job . aasm_state # => 'sleeping'
job . aasm_state = :running # => raises AASM::NoDirectAssignmentError
job . aasm_state # => 'sleeping' Puede decirle a AASM que intente escribir una marca de tiempo cada vez que se ingrese un nuevo estado. Si timestamps: true están establecidas, AASM buscará un campo llamado como el nuevo estado más _at e intentará llenarlo:
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
endresultando en esto:
job = Job . create
job . running_at # => nil
job . run!
job . running_at # => 2020-02-20 20:00:00Los campos de marca de tiempo faltantes se ignoran en silencio, por lo que no es necesario tener setters (como columnas de Activerecord) para todos los estados al usar esta opción.
Puede usar enumeraciones en Rails 4.1+ para su columna de estado:
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 Puede pasar explícitamente el nombre del método que proporciona acceso a la asignación de enumeración como un valor de enum , o simplemente puede configurarlo en true . En el último caso, AASM intentará usar el nombre de la columna pluralizada para acceder a posibles estados de enum.
Además, si su columna tiene el tipo entero (que normalmente es el caso cuando trabaja con Rails enums), puede omitir :enum --- Aasm detecta automáticamente esta situación y habilitó el soporte de enum. Si algo sale mal, puede deshabilitar la funcionalidad de Enum y recurrir al comportamiento predeterminado configurando :enum a false .
AASM también apoya la secuela además de Activerecord y Mongoide .
class Job < Sequel :: Model
include AASM
aasm do # default column: aasm_state
...
end
endSin embargo, aún no es una característica completa como Activerecord . Por ejemplo, todavía hay ámbitos definidos. Ver alcances automáticos.
Dado que la versión 4.8.0 AASM también admite dinamoide como persistencia ORM.
AASM también apoya la persistencia a MongoDB si está usando Mongoide. Asegúrese de incluir el documento Mongoid :: antes de incluir AASM.
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM también apoya la persistencia a RethinkdB si está usando Nobrainer. Asegúrese de incluir Nobrainer :: Document antes de incluir AASM.
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM también apoya la persistencia en Redis a través de Redis :: Objects. Asegúrese de incluir Redis :: Objetos antes de incluir AASM. Tenga en cuenta que los eventos no bang funcionarán como eventos de explosión, persistiendo los cambios en cada llamada.
class User
include Redis :: Objects
include AASM
aasm do
end
endAASM creará automáticamente métodos de alcance para cada estado en el modelo.
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 Si no necesita alcances (o simplemente no los quiere), deshabilite su creación al definir los estados AASM , así:
class Job < ActiveRecord :: Base
include AASM
aasm create_scopes : false do
state :sleeping , initial : true
state :running
state :cleaning
end
endDado que la versión 3.0.13 AASM admite las transacciones de Activerecord. Entonces, cada vez que falla una devolución de llamada de transición o la actualización del estado, todos los cambios en cualquier registro de la base de datos se retroceden. MongoDB no admite transacciones.
Actualmente hay 3 devoluciones de llamada transaccionales que se pueden manejar en el evento y 2 devoluciones de llamada transaccionales para todos los eventos.
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 Si desea asegurarse de que una acción dependiente ocurra solo después de que se comprometa la transacción, use la devolución de llamada after_commit junto con los métodos de Save (Bang) de Auto_Commit, como este:
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 Tenga en cuenta que lo siguiente no ejecutará las devoluciones de llamada after_commit porque no se utiliza el método de Save Auto::
job = Job . where ( state : 'sleeping' ) . first!
job . run
job . save! #notify_about_running_job is not run Tenga en cuenta que :after_commit las devoluciones de llamada Aasm de Aasm se comportan en torno a la implementación personalizada del patrón de transacción en lugar de una transacción DB de la vida real. Este hecho aún causa las condiciones de carrera y las llamadas de devolución de llamada redundantes dentro de la transacción anidada. Para corregir que se recomienda agregar gem 'after_commit_everywhere', '~> 1.0' a su Gemfile .
Si desea encapsular los cambios de estado dentro de una transacción propia, el comportamiento de esta transacción anidada podría ser confuso. Eche un vistazo a las transacciones anidadas de Activerecord si desea saber más sobre esto. Sin embargo, AASM por defecto requiere una nueva transaction(requires_new: true) . Puede anular este comportamiento cambiando la configuración
class Job < ActiveRecord :: Base
include AASM
aasm requires_new_transaction : false do
...
end
...
end Lo que luego conduce a transaction(requires_new: false) , los rieles predeterminados.
Además, si no desea que ninguna de sus acciones de Activerecord esté envuelta en una transacción, puede especificar el indicador use_transactions . Esto puede ser útil si desea persistir en la base de datos que ocurre como resultado de una transacción o devolución de llamada, incluso cuando ocurre algún error. El indicador use_transactions es verdadero de forma predeterminada.
class Job < ActiveRecord :: Base
include AASM
aasm use_transactions : false do
...
end
...
end AASM admite el bloqueo pesimista de Activerecord a través de with_lock para capas de persistencia de la base de datos.
| Opción | Objetivo |
|---|---|
false (predeterminado) | No se obtiene bloqueo |
true | Obtenga un bloqueo de bloqueo de bloqueo, por ejemplo, FOR UPDATE |
| Cadena | Obtenga un bloqueo basado en la cadena SQL 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 Como AASM predeterminado utiliza la columna aasm_state para almacenar los estados. Puede anular esto definiendo su nombre de columna favorito, usando :column como esta:
class Job < ActiveRecord :: Base
include AASM
aasm column : :my_state do
...
end
aasm :another_state_machine , column : :second_state do
...
end
end Cualquiera que sea el nombre de la columna, asegúrese de agregar una migración para proporcionar esta columna (de string de tipo). No agregue el valor predeterminado para la columna en el nivel de la base de datos. Si agrega valor predeterminado en la base de datos, las devoluciones de llamada AASM en el estado inicial no se dispararán tras la instancia del modelo.
class AddJobState < ActiveRecord :: Migration
def self . up
add_column :jobs , :aasm_state , :string
end
def self . down
remove_column :jobs , :aasm_state
end
endEl cambio de estado de registro se puede hacer usando gem paper_trail
El ejemplo de implementación se puede encontrar aquí https://github.com/nitsujri/aasm-papertrail-example
AASM admite métodos de consulta para estados y eventos
Dada la siguiente clase 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 ) Las advertencias se imprimen por defecto en STDERR . Si desea registrar esas advertencias a otra salida, use
class Job
include AASM
aasm logger : Rails . logger do
...
end
end Puede ocultar advertencias configurando AASM::Configuration.hide_warnings = true
¡Ahora es compatible con CodedataQuery! Sin embargo, todavía estoy en el proceso de enviar mis actualizaciones de compatibilidad a su repositorio. Mientras tanto, puede usar mi bifurcación, aún puede haber algunos problemas menores, pero tengo la intención de usarlo mucho yo mismo, por lo que las soluciones deberían llegar rápidamente.
Advertencias:
AASM proporciona algunos Matchers para RSPEC:
transition_from ,have_state , allow_eventallow_transition_to . require 'aasm/rspec' a su archivo 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 proporciona afirmaciones y expectativas similares a RSPEC para Minitest.
Lista de afirmaciones compatibles: 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 .
Agregue require 'aasm/minitest' a su archivo test_helper.rb y úselo así:
# 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 Lista de expectativas compatibles: 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 .
Agregue require 'aasm/minitest_spec' a su archivo test_helper.rb y úselo así:
# 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.gemDespués de instalar AASM, puede ejecutar el generador:
% rails generate aasm NAME [COLUMN_NAME]Reemplace el nombre con el nombre del modelo, column_name es opcional (el valor predeterminado es 'aasm_state'). Esto creará un modelo (si uno no existe) y lo configurará con el bloque AASM. Para Activerecord ORM se agrega un archivo de migración para agregar la columna de estado AASM a la tabla.
Ejecute Test Suite fácilmente en Docker
1. docker-compose build aasm
2. docker-compose run --rm aasm
Eche un vistazo al ChangeLog para obtener detalles sobre los cambios recientes en la versión actual.
No tener en cuenta
aasm )Este software se proporciona "tal cual" y sin ninguna garantía expresa o implícita, incluidas, sin limitación, las garantías implícitas de mercantilización y aptitud para un propósito particular.
Copyright (c) 2006-2017 Scott Barron
El permiso se otorga, de forma gratuita, a cualquier persona que obtenga una copia de este software y archivos de documentación asociados (el "software"), para tratar en el software sin restricción, incluidos los derechos de los derechos de usar, copiar, modificar, fusionar, publicar, distribuir, sublicense y/o vender copias del software, y para permitir que las personas a quienes se les proporciona el software para hacer, sujeto a las siguientes condiciones: las siguientes condiciones: las siguientes condiciones: las siguientes condiciones:
El aviso de derechos de autor anterior y este aviso de permiso se incluirán en todas las copias o porciones sustanciales del software.
El software se proporciona "tal cual", sin garantía de ningún tipo, expresa o implícita, incluidas, entre otros, las garantías de comerciabilidad, idoneidad para un propósito particular y no infracción. En ningún caso los autores o titulares de derechos de autor serán responsables de cualquier reclamo, daños u otra responsabilidad, ya sea en una acción de contrato, agravio o de otra manera, que surge, de o en relación con el software o el uso u otros tratos en el software.