Este pacote contém AASM, uma biblioteca para adicionar máquinas de estado finitas às aulas de rubi.
O AASM começou como o plug -in Atos_AS_STATE_MACHINE , mas evoluiu para uma biblioteca mais genérica que não tem mais alvos apenas de modelos ActiveRecord. Atualmente, ele fornece adaptadores para muitos ORMs, mas pode ser usado para qualquer aula do Ruby, independentemente da classe dos pais (se houver).
Dê uma olhada no readme_from_version_3_to_4 para obter detalhes como alternar da versão 3.x para 4.0 de AASM .
A adição de uma máquina de estado é tão simples quanto incluir o módulo AASM e começar a definir estados e eventos , juntamente com suas transições :
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 Isso fornece alguns métodos públicos para instâncias do Job de classe:
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 Se você não gosta de exceções e prefere um true ou false simples como resposta, diga a AASM para não ser chorão :
class Job
...
aasm whiny_transitions : false do
...
end
end
job . running? # => true
job . may_run? # => false
job . run # => falseAo disparar um evento, você pode passar um bloco para o método, ele será chamado apenas se a transição for bem -sucedida:
job . run do
job . user . notify_job_ran # Will be called if job.may_run? is true
endVocê pode definir uma série de retornos de chamada para seus eventos, transições e estados. Esses métodos, procs ou classes serão chamados quando certos critérios forem atendidos, como entrar em um estado específico:
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 Nesse caso, do_something é chamado antes de entrar no estado sleeping , enquanto notify_somebody é chamado após a conclusão da run (do sleeping para running ).
O AASM também inicializará LogRunTime e executará o método call para você após a transição da running para finished no exemplo acima. Você pode passar argumentos para a classe definindo um método de inicialização, assim:
Observe que os Procs são executados no contexto de um registro, significa que você não precisa esperar o registro como um argumento, basta chamar os métodos necessários.
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 Você pode passar parâmetros para eventos:
job = Job . new
job . run ( :defragmentation ) Todos os guardas e retornos de chamada receberão esses parâmetros. Nesse caso, set_process seria chamado com :defragmentation .
Se o primeiro argumento para o evento for um estado (por exemplo :running ou :finished ), o primeiro argumento é consumido e a máquina estadual tentará fazer a transição para esse estado. Adicione o parâmetro separado por vírgula para guardas e retornos de chamada
job = Job . new
job . run ( :running , :defragmentation ) set_process :defragmentation
No caso de um erro durante o processamento do evento, o erro é resgatado e transmitido para :error , que pode lidar com ele ou re-ele para a propagação adicional.
Além disso, você pode definir um método que será chamado se algum evento falhar:
def aasm_event_failed ( event_name , old_state_name )
# use custom exception/messages, report metrics, etc
end Durante a transição :after o retorno de chamada (e apenas de maneira confiável, ou no retorno de chamada after_all_transitions global), você pode acessar o estado de origem (do estado do estado) e o estado-alvo (o estado), assim:
def set_process ( name )
logger . info "from #{ aasm . from_state } to #{ aasm . to_state } "
end Aqui você pode ver uma lista de todos os possíveis retornos de chamada, juntamente com a ordem de chamada:
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 o retorno de chamada after_commit do evento, se ele for disparado após a atualização do banco de dados.
Ao executar os retornos de chamada, você pode recuperar facilmente o nome do evento desencadeado usando aasm.current_event :
# taken the example callback from above
def do_something
puts "triggered #{ aasm . current_event } "
ende então
job = Job . new
# without bang
job . sleep # => triggered :sleep
# with bang
job . sleep! # => triggered :sleep! Vamos supor que você queira permitir transições específicas apenas se uma condição definida for fornecida. Para isso, você pode configurar um guarda por transição, que será executado antes de executar a transição. Se o guarda retornar false a transição será negada (levantando 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 ) # => trueVocê pode até fornecer vários guardas, todos que têm que ter sucesso para prosseguir
def walked_the_dog? ; ... ; end
event :sleep do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? , :walked_the_dog? ]
endSe você deseja fornecer guardas para todas as transições dentro de um evento, você pode usar guardas de eventos
event :sleep , guards : [ :walked_the_dog? ] do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? ]
transitions from : :cleaning , to : :sleeping
end Se você preferir uma sintaxe de guarda mais semelhante a rubi, você pode usar if e a unless que também:
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 Você pode invocar uma aula em vez de um método se a classe responder para call
event :sleep do
transitions from : :running , to : :sleeping , guards : Dog
end class Dog
def call
cleaning_needed? && walked?
end
...
endNo caso de ter várias transições para um evento, a primeira transição que concluir com êxito impedirá que outras transições no mesmo evento sejam processadas.
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 Você pode definir a transição de qualquer estado definido omitindo from :
event :abort do
transitions to : :aborted
endVocê pode definir o nome de exibição para estado usando: opção de exibição
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 Múltiplas máquinas de estado por classe são suportadas. Esteja ciente de que o AASM foi construído com uma máquina de estado por classe em mente. No entanto, veja como fazê -lo (veja abaixo). Observe que você precisará especificar colunas de banco de dados para onde seus estados pertinentes serão armazenados - especificamos duas colunas move_state e work_state no exemplo abaixo. Consulte o nome da coluna e a seção de migração para obter mais informações.
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 O AASM não proíbe definir o mesmo evento em mais de uma máquina de estado. Se nenhum espaço para nome for fornecido, a definição mais recente "vence" e substitui as definições anteriores. No entanto, um aviso é emitido: SimpleMultipleExample: overriding method 'run'! .
Como alternativa, você pode fornecer um espaço de nome 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 os métodos AASM em nível de classe e no nível aasm instância aceitam um seletor de máquina de estado. Por exemplo, para usar a inspeção em um nível de aula, você precisa usar
SimpleMultipleExample . aasm ( :move ) . states . map ( & :name )
# => [:standing, :walking, :running]Permitir que um evento esteja ligado a outro
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 #=> :askedO AASM gera automaticamente constantes para cada status para que você não precise defini -las explicitamente.
class Foo
include AASM
aasm do
state :initialized
state :calculated
state :finalized
end
end
> Foo :: STATE_INITIALIZED
#=> :initialized
> Foo :: STATE_CALCULATED
#=> :calculated O AASM permite que você estenda facilmente AASM::Base para seus próprios fins de aplicativo.
Suponhamos que tenhamos lógica comum em muitos modelos AASM. Podemos incorporar essa lógica em uma subclasse 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 Quando declaramos nosso modelo que possui uma máquina de estado AASM, simplesmente declaramos o bloco AASM com a :with_klass Chave para nossa própria classe.
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
endO AASM vem com suporte ao ActiveRecord e permite persistir automático do estado do objeto no banco de dados.
Adicione gem 'after_commit_everywhere', '~> 1.0' ao seu 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
endVocê pode dizer AASM para salvar automaticamente o objeto ou deixá-lo não salvo
job = Job . new
job . run # not saved
job . run! # saved
# or
job . aasm . fire ( :run ) # not saved
job . aasm . fire! ( :run ) # saved A economia inclui a execução de todas as validações na classe Job . Se o sinalizador whiny_persistence estiver definido como true , a exceção será aumentada em caso de falha. Se o sinalizador whiny_persistence estiver definido como false , os métodos com um bang retornam true se a transição do estado for bem -sucedida ou false se ocorrer um erro.
Se você deseja garantir que o estado seja salvo sem as validações (e, assim, persistir em um estado de objeto inválido), basta dizer a AMM para pular as validações. Esteja ciente de que, ao pular as validações, apenas a coluna do estado será atualizada no banco de dados (assim como o 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 Além disso, você pode pular a validação no nível da instância com some_event_name_without_validation! método. Com isso, você tem a flexibilidade de ter validação para todas as suas transições por padrão e, em seguida, pule -a sempre que necessário. Observe que apenas a coluna do estado será atualizada conforme mencionado no exemplo acima.
job . run_without_validation!Se você deseja garantir que a coluna AASM para armazenar o estado não seja atribuída diretamente, configure AASM para não permitir uma atribuição direta, como esta:
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 nisso:
job = Job . create
job . aasm_state # => 'sleeping'
job . aasm_state = :running # => raises AASM::NoDirectAssignmentError
job . aasm_state # => 'sleeping' Você pode dizer AASM para tentar escrever um carimbo de data / hora sempre que um novo estado for inserido. Se timestamps: true for definido, o AASM procurará um campo nomeado como o novo estado mais _at e tentará preenchê -lo:
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 nisso:
job = Job . create
job . running_at # => nil
job . run!
job . running_at # => 2020-02-20 20:00:00Os campos de timestamp ausentes são ignorados silenciosamente, portanto, não é necessário ter setters (como colunas ActiveRecord) para todos os estados ao usar essa opção.
Você pode usar enumerações no Rails 4.1+ para sua coluna 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 Você pode passar explicitamente o nome do método que fornece acesso ao mapeamento de enumeração como um valor de enum , ou você pode simplesmente defini -lo como true . Neste último caso, o AASM tentará usar o nome da coluna pluralizada para acessar possíveis estados enum.
Além disso, se a sua coluna tiver tipo inteiro (que normalmente é o caso quando você está trabalhando com enums do Rails), você pode omitir :enum a configuração-aasm auto-detecta essa situação e o suporte a enum. Se algo der errado, você pode desativar a funcionalidade enum e voltar ao comportamento padrão, configurando :enum para false .
AASM também suporta sequela além do ActiveRecord e Mongoid .
class Job < Sequel :: Model
include AASM
aasm do # default column: aasm_state
...
end
endNo entanto, ainda não é tão completo quanto o ActiveRecord . Por exemplo, ainda existem escopos definidos. Veja escopos automáticos.
Desde a versão 4.8.0 o AASM também suporta dinamóide como persistência ORM.
O AASM também apóia a persistência em MongoDB se você estiver usando mongóides. Certifique -se de incluir o documento Mongoid :: antes de incluir o AASM.
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endO AASM também apóia a persistência de repensar se você estiver usando o Nobrainer. Certifique -se de incluir o documento Nobrainer :: antes de incluir o AASM.
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endO AASM também apóia a persistência em Redis via Redis :: Objetos. Certifique -se de incluir Redis :: Objetos antes de incluir o AASM. Observe que os eventos que não são de bang funcionarão como eventos de bang, persistindo as mudanças em todas as chamadas.
class User
include Redis :: Objects
include AASM
aasm do
end
endO AASM criará automaticamente métodos de escopo para cada estado no 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 Se você não precisa de escopos (ou simplesmente não os quer), desative a criação deles ao definir os Estados AASM , assim:
class Job < ActiveRecord :: Base
include AASM
aasm create_scopes : false do
state :sleeping , initial : true
state :running
state :cleaning
end
endDesde a versão 3.0.13, o AASM suporta transações ActiveRecord. Portanto, sempre que um retorno de chamada de transição ou a atualização do estado falha, todas as alterações em qualquer registro do banco de dados são revertidas. O MongoDB não suporta transações.
Atualmente, existem três retornos de chamada transacionais que podem ser tratados no evento e 2 retornos de chamada transacionais para todos os 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 Se você deseja garantir que uma ação dependente aconteça somente depois que a transação é cometida, use o retorno de chamada after_commit , juntamente com os métodos de salvamento automático (BANG), 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 Observe que o seguinte não executará os retornos de chamada after_commit , porque o método Auto-Save não é usado:
job = Job . where ( state : 'sleeping' ) . first!
job . run
job . save! #notify_about_running_job is not run Observe que :after_commit Aasm Retornos de chamada se comporta em torno da implementação personalizada do padrão de transação em vez de uma transação de banco de dados da vida real. Esse fato ainda causa as condições da corrida e as chamadas de retorno de chamada redundantes dentro da transação aninhada. Para corrigir que é altamente recomendável adicionar gem 'after_commit_everywhere', '~> 1.0' ao seu Gemfile .
Se você deseja encapsular as alterações do estado em uma transação própria, o comportamento dessa transação aninhada pode ser confuso. Dê uma olhada nas transações do ActiveRecord aninhadas, se quiser saber mais sobre isso. No entanto, o AASM por padrão requer uma nova transaction(requires_new: true) . Você pode substituir esse comportamento alterando a configuração
class Job < ActiveRecord :: Base
include AASM
aasm requires_new_transaction : false do
...
end
...
end O que leva à transaction(requires_new: false) , os trilhos padrão.
Além disso, se você não deseja que nenhuma das suas ações do ActiveRecord seja envolvida em uma transação, poderá especificar o sinalizador de use_transactions . Isso pode ser útil se você quiser persistir coisas no banco de dados que acontecem como resultado de uma transação ou retorno de chamada, mesmo quando ocorre algum erro. O sinalizador use_transactions é verdadeiro por padrão.
class Job < ActiveRecord :: Base
include AASM
aasm use_transactions : false do
...
end
...
end O AASM suporta o bloqueio pessimista do ActiveRecord via with_lock para camadas de persistência do banco de dados.
| Opção | Propósito |
|---|---|
false (padrão) | Nenhuma trava é obtida |
true | Obtenha um bloqueio pessimista bloqueador, por exemplo, FOR UPDATE |
| Corda | Obtenha um bloqueio baseado na string sql, por exemplo, 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 padrão, o AASM usa a coluna aasm_state para armazenar os estados. Você pode substituir isso definindo seu nome de coluna 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 Qualquer que seja o nome da coluna, adicione uma migração para fornecer esta coluna (da string do tipo). Não adicione valor padrão para a coluna no nível do banco de dados. Se você adicionar valor padrão no banco de dados, os retornos de chamada AASM no estado inicial não serão disparados após a instanciação do modelo.
class AddJobState < ActiveRecord :: Migration
def self . up
add_column :jobs , :aasm_state , :string
end
def self . down
remove_column :jobs , :aasm_state
end
endA mudança de estado de registro pode ser feita usando paper_trail gem
Exemplo de implementação pode ser encontrado aqui https://github.com/nitsujri/aasm-papertrail-example
AASM suporta métodos de consulta para estados e eventos
Dada a seguinte aula 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 ) Os avisos são, por padrão, impressos para STDERR . Se você quiser registrar esses avisos em outra saída, use
class Job
include AASM
aasm logger : Rails . logger do
...
end
end Você pode ocultar avisos definindo AASM::Configuration.hide_warnings = true
Agora suporta codedataquery! No entanto, ainda estou no processo de enviar minhas atualizações de compatibilidade ao repositório deles. Enquanto isso, você pode usar meu garfo, ainda pode haver alguns problemas menores, mas pretendo usá -lo extensivamente, para que as correções venham rapidamente.
AVISOS:
AASM fornece alguns Matcuers para RSPEC:
transition_from ,have_state , allow_eventallow_transition_to . require 'aasm/rspec' ao seu arquivo 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 ) O AASM fornece afirmações e expectativas do tipo RSPEC para o MinItest.
Lista de asserções suportadas: 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 .
Adicionar require 'aasm/minitest' ao seu arquivo test_helper.rb e use -os assim:
# 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 suportadas: 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 .
Adicionar require 'aasm/minitest_spec' ao seu arquivo test_helper.rb e use -os assim:
# 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.gemDepois de instalar o AASM, você pode executar o gerador:
% rails generate aasm NAME [COLUMN_NAME]Substitua o nome pelo nome do modelo, column_name é opcional (o padrão é 'aasm_state'). Isso criará um modelo (se não houver) e o configurará com um bloco AASM. Para o ActiveRecord ORM, um arquivo de migração é adicionado para adicionar a coluna Aasm State à tabela.
Execute a suíte de teste facilmente no Docker
1. docker-compose build aasm
2. docker-compose run --rm aasm
Dê uma olhada no Changelog para obter detalhes sobre alterações recentes na versão atual.
Sinta -se à vontade para
aasm )Este software é fornecido "como está" e sem garantias expressas ou implícitas, incluindo, sem limitação, as garantias implícitas de comercialização e aptidão para uma finalidade específica.
Copyright (C) 2006-2017 Scott Barron
A permissão é concedida, gratuita, a qualquer pessoa que obtenha uma cópia deste software e arquivos de documentação associados (o "software"), para lidar com o software sem restrição, inclusive sem limitação os direitos de usar, copiar, modificar, mesclar, publicar, distribuir, mobilizar o software e/ou vender cópias do software e permitir que as pessoas a quem
O aviso de direitos autorais acima e este aviso de permissão devem ser incluídos em todas as cópias ou em partes substanciais do software.
O software é fornecido "como está", sem garantia de qualquer tipo, expresso ou implícito, incluindo, entre outros, as garantias de comercialização, aptidão para uma finalidade específica e não innoculação. Em nenhum caso os autores ou detentores de direitos autorais serão responsáveis por qualquer reclamação, danos ou outro passivo, seja em uma ação de contrato, delito ou não, decorrente de, fora ou em conexão com o software ou o uso ou outras negociações no software.