Этот пакет содержит AASM, библиотеку для добавления конечных штатных машин в классы Ruby.
AASM начинался как плагин ACTS_AS_STATE_MACHINE , но превратился в более общую библиотеку, которая больше не нацелена на модели Activerecord. В настоящее время он предоставляет адаптеры для многих ORM, но его можно использовать для любого класса Ruby, независимо от того, какой у него родительский класс (если есть).
Посмотрите на readme_from_version_3_to_4, чтобы узнать, как переключиться с версии 3.x на 4.0 AASM .
Добавление состояния машины так же просто, как включать модуль AASM и начать определение состояний и событий вместе с их переходом :
class Job
include AASM
aasm do
state :sleeping , initial : true
state :running , :cleaning
event :run do
transitions from : :sleeping , to : :running
end
event :clean do
transitions from : :running , to : :cleaning
end
event :sleep do
transitions from : [ :running , :cleaning ] , to : :sleeping
end
end
end Это дает вам несколько публичных методов для случаев Job класса:
job = Job . new
job . sleeping? # => true
job . may_run? # => true
job . run
job . running? # => true
job . sleeping? # => false
job . may_run? # => false
job . run # => raises AASM::InvalidTransition Если вам не нравятся исключения и предпочитаете простое true или false в качестве ответа, скажите AASM не плакать :
class Job
...
aasm whiny_transitions : false do
...
end
end
job . running? # => true
job . may_run? # => false
job . run # => falseПри запуске события вы можете передать блок методу, оно будет вызвано только в случае успеха перехода:
job . run do
job . user . notify_job_ran # Will be called if job.may_run? is true
endВы можете определить ряд обратных вызовов для ваших событий, переходов и состояний. Эти методы, Procs или классы будут вызваны, когда будут соблюдены определенные критерии, например, ввод конкретного состояния:
class Job
include AASM
aasm do
state :sleeping , initial : true , before_enter : :do_something
state :running , before_enter : Proc . new { do_something && notify_somebody }
state :finished
after_all_transitions :log_status_change
event :run , after : :notify_somebody do
before do
log ( 'Preparing to run' )
end
transitions from : :sleeping , to : :running , after : Proc . new { |* args | set_process ( * args ) }
transitions from : :running , to : :finished , after : LogRunTime
end
event :sleep do
after do
...
end
error do | e |
...
end
transitions from : :running , to : :sleeping
end
end
def log_status_change
puts "changing from #{ aasm . from_state } to #{ aasm . to_state } (event: #{ aasm . current_event } )"
end
def set_process ( name )
...
end
def do_something
...
end
def notify_somebody
...
end
end
class LogRunTime
def call
log "Job was running for X seconds"
end
end В этом случае do_something вызывается, прежде чем фактически войти в sleeping , в то время как notify_somebody вызывается после того, run (от sleeping до running ) завершена.
AASM также инициализирует LogRunTime и запустит метод call для вас после перехода от running к finished в примере выше. Вы можете передавать аргументы в класс, определив на нем метод инициализации, например, это:
Обратите внимание, что Procs выполняются в контексте записи, это означает, что вам не нужно ожидать записи в качестве аргумента, просто назовите необходимые вам методы.
class LogRunTime
# optional args parameter can be omitted, but if you define initialize
# you must accept the model instance as the first parameter to it.
def initialize ( job , args = { } )
@job = job
end
def call
log "Job was running for #{ @job . run_time } seconds"
end
end Вы можете передать параметры событиям:
job = Job . new
job . run ( :defragmentation ) Все охранники и после обратных вызовов получат эти параметры. В этом случае set_process будет вызван :defragmentation .
Если первым аргументом в мероприятие является состояние (например :running или :finished ), первый аргумент используется, и государственный аппарат попытается перейти к этому состоянию. Добавить отдельный параметр запятой для охранников и обратных вызовов
job = Job . new
job . run ( :running , :defragmentation ) В этом случае set_process не будет вызвана, задание будет перейти к запуску состояния, а обратный вызов получит :defragmentation как параметр
В случае ошибки во время обработки событий ошибка спасена и передается :error , который может обрабатывать его или повторно его для дальнейшего распространения.
Кроме того, вы можете определить метод, который будет вызван, если какое -либо событие не удалось:
def aasm_event_failed ( event_name , old_state_name )
# use custom exception/messages, report metrics, etc
end Во время перехода :after обратного вызова (и надежно только тогда, или в глобальном обратном вызове after_all_transitions ) вы можете получить доступ к исходному состоянию (от государства) и целевому состоянию (для состояния), например:
def set_process ( name )
logger . info "from #{ aasm . from_state } to #{ aasm . to_state } "
end Здесь вы можете увидеть список всех возможных обратных вызовов вместе с их приказом звонка:
begin
event before_all_events
event before
event guards
transition guards
old_state before_exit
old_state exit
after_all_transitions
transition after
new_state before_enter
new_state enter
... update state ...
event before_success # if persist successful
transition success # if persist successful, database update not guaranteed
event success # if persist successful, database update not guaranteed
old_state after_exit
new_state after_enter
event after
event after_all_events
rescue
event error
event error_on_all_events
ensure
event ensure
event ensure_on_all_events
end Используйте обратный вызов after_commit , если он должен быть запущен после обновления базы данных.
Во время запуска обратных вызовов вы можете легко получить имя события, запускаемое с помощью aasm.current_event :
# taken the example callback from above
def do_something
puts "triggered #{ aasm . current_event } "
endа потом
job = Job . new
# without bang
job . sleep # => triggered :sleep
# with bang
job . sleep! # => triggered :sleep! Давайте предположим, что вы хотите разрешить определенные переходы, только если указано определенное условие. Для этого вы можете настроить охрану за переход, который будет работать до фактического запуска перехода. Если охранник вернет false переход будет отказано (поднятие AASM::InvalidTransition ):
class Cleaner
include AASM
aasm do
state :idle , initial : true
state :cleaning
event :clean do
transitions from : :idle , to : :cleaning , guard : :cleaning_needed?
end
event :clean_if_needed do
transitions from : :idle , to : :cleaning do
guard do
cleaning_needed?
end
end
transitions from : :idle , to : :idle
end
event :clean_if_dirty do
transitions from : :idle , to : :cleaning , guard : :if_dirty?
end
end
def cleaning_needed?
false
end
def if_dirty? ( status )
status == :dirty
end
end
job = Cleaner . new
job . may_clean? # => false
job . clean # => raises AASM::InvalidTransition
job . may_clean_if_needed? # => true
job . clean_if_needed! # idle
job . clean_if_dirty ( :clean ) # => raises AASM::InvalidTransition
job . clean_if_dirty ( :dirty ) # => trueВы даже можете предоставить ряд охранников, которые должны быть успешными, чтобы продолжить
def walked_the_dog? ; ... ; end
event :sleep do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? , :walked_the_dog? ]
endЕсли вы хотите предоставить охранникам для всех переходов в рамках события, вы можете использовать охранники событий
event :sleep , guards : [ :walked_the_dog? ] do
transitions from : :running , to : :sleeping , guards : [ :cleaning_needed? ]
transitions from : :cleaning , to : :sleeping
end Если вы предпочитаете более рубиноподобный синтаксис охраны, вы можете использовать if и unless это тоже.
event :clean do
transitions from : :running , to : :cleaning , if : :cleaning_needed?
end
event :sleep do
transitions from : :running , to : :sleeping , unless : :cleaning_needed?
end
end Вы можете вызвать класс вместо метода, если класс отвечает на call
event :sleep do
transitions from : :running , to : :sleeping , guards : Dog
end class Dog
def call
cleaning_needed? && walked?
end
...
endВ случае наличия нескольких переходов для события, первый переход, который успешно завершает, остановит другие переходы в том же событии.
require 'aasm'
class Job
include AASM
aasm do
state :stage1 , initial : true
state :stage2
state :stage3
state :completed
event :stage1_completed do
transitions from : :stage1 , to : :stage3 , guard : :stage2_completed?
transitions from : :stage1 , to : :stage2
end
end
def stage2_completed?
true
end
end
job = Job . new
job . stage1_completed
job . aasm . current_state # stage3 Вы можете определить переход из любого определенного состояния, исключив from :
event :abort do
transitions to : :aborted
endВы можете определить имя отображения для состояния с помощью: опция отображения
class Job
include AASM
aasm do
state :stage1 , initial : true , display : 'First Stage'
state :stage2
state :stage3
end
end
job = Job . new
job . aasm . human_state Поддерживаются несколько государственных машин на класс. Имейте в виду, что AASM был построен с одной государственной машиной для каждого класса. Тем не менее, вот как это сделать (см. Ниже). Обратите внимание, что вам нужно будет указать столбцы базы данных для того, где будут храниться ваши соответствующие состояния - мы указали два столбца move_state и work_state в примере ниже. См. Раздел «Имя и миграцию столбца» для получения дополнительной информации.
class SimpleMultipleExample
include AASM
aasm ( :move , column : 'move_state' ) do
state :standing , initial : true
state :walking
state :running
event :walk do
transitions from : :standing , to : :walking
end
event :run do
transitions from : [ :standing , :walking ] , to : :running
end
event :hold do
transitions from : [ :walking , :running ] , to : :standing
end
end
aasm ( :work , column : 'work_state' ) do
state :sleeping , initial : true
state :processing
event :start do
transitions from : :sleeping , to : :processing
end
event :stop do
transitions from : :processing , to : :sleeping
end
end
end
simple = SimpleMultipleExample . new
simple . aasm ( :move ) . current_state
# => :standing
simple . aasm ( :work ) . current_state
# => :sleeping
simple . start
simple . aasm ( :move ) . current_state
# => :standing
simple . aasm ( :work ) . current_state
# => :processing AASM не запрещает определять одно и то же событие в более чем одной машине состояния. Если пространство имен не предоставлено, последнее определение «выигрывает» и переопределяет предыдущие определения. Тем не менее, выдается предупреждение: SimpleMultipleExample: overriding method 'run'! Полем
В качестве альтернативы, вы можете предоставить пространство имен для каждой государственной машины:
class NamespacedMultipleExample
include AASM
aasm ( :status ) do
state :unapproved , initial : true
state :approved
event :approve do
transitions from : :unapproved , to : :approved
end
event :unapprove do
transitions from : :approved , to : :unapproved
end
end
aasm ( :review_status , namespace : :review ) do
state :unapproved , initial : true
state :approved
event :approve do
transitions from : :unapproved , to : :approved
end
event :unapprove do
transitions from : :approved , to : :unapproved
end
end
end
namespaced = NamespacedMultipleExample . new
namespaced . aasm ( :status ) . current_state
# => :unapproved
namespaced . aasm ( :review_status ) . current_state
# => :unapproved
namespaced . approve_review
namespaced . aasm ( :review_status ) . current_state
# => :approved Все методы AASC AASM классовой и уровня aasm принимают селектор машин состояния. Так, например, для использования проверки на уровне класса, вы должны использовать
SimpleMultipleExample . aasm ( :move ) . states . map ( & :name )
# => [:standing, :walking, :running]Позволить событию быть связанным с другим
class Example
include AASM
aasm ( :work ) do
state :sleeping , initial : true
state :processing
event :start do
transitions from : :sleeping , to : :processing
end
event :stop do
transitions from : :processing , to : :sleeping
end
end
aasm ( :question ) do
state :answered , initial : true
state :asked
event :ask , binding_event : :start do
transitions from : :answered , to : :asked
end
event :answer , binding_event : :stop do
transitions from : :asked , to : :answered
end
end
end
example = Example . new
example . aasm ( :work ) . current_state #=> :sleeping
example . aasm ( :question ) . current_state #=> :answered
example . ask
example . aasm ( :work ) . current_state #=> :processing
example . aasm ( :question ) . current_state #=> :askedAASM автоматически генерирует постоянные для каждого статуса, поэтому вам не нужно явно определять их.
class Foo
include AASM
aasm do
state :initialized
state :calculated
state :finalized
end
end
> Foo :: STATE_INITIALIZED
#=> :initialized
> Foo :: STATE_CALCULATED
#=> :calculated AASM позволяет вам легко расширить AASM::Base для ваших собственных прикладных целей.
Предположим, у нас есть общая логика во многих моделях AASM. Мы можем воплотить эту логику в подклассе AASM::Base .
class CustomAASMBase < AASM :: Base
# A custom transition that we want available across many AASM models.
def count_transitions!
klass . class_eval do
aasm with_klass : CustomAASMBase do
after_all_transitions :increment_transition_count
end
end
end
# A custom annotation that we want available across many AASM models.
def requires_guards!
klass . class_eval do
attr_reader :authorizable_called ,
:transition_count ,
:fillable_called
def authorizable?
@authorizable_called = true
end
def fillable?
@fillable_called = true
end
def increment_transition_count
@transition_count ||= 0
@transition_count += 1
end
end
end
end Когда мы объявляем нашу модель, которая имеет машину состояния AASM, мы просто объявляем блок AASM с помощью :with_klass -ключа в наш собственный класс.
class SimpleCustomExample
include AASM
# Let's build an AASM state machine with our custom class.
aasm with_klass : CustomAASMBase do
requires_guards!
count_transitions!
state :initialised , initial : true
state :filled_out
state :authorised
event :fill_out do
transitions from : :initialised , to : :filled_out , guard : :fillable?
end
event :authorise do
transitions from : :filled_out , to : :authorised , guard : :authorizable?
end
end
endAASM поставляется с поддержкой ActiveRecord и позволяет автоматическому сохранять состояние объекта в базе данных.
Добавьте gem 'after_commit_everywhere', '~> 1.0' в свой Gemfile.
class Job < ActiveRecord :: Base
include AASM
aasm do # default column: aasm_state
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
event :sleep do
transitions from : :running , to : :sleeping
end
end
endВы можете сказать AASM, чтобы автоматически усугубить объект или оставить его неспасенным
job = Job . new
job . run # not saved
job . run! # saved
# or
job . aasm . fire ( :run ) # not saved
job . aasm . fire! ( :run ) # saved Сохранение включает в себя выполнение всех подтверждений на классе Job . Если флаг whiny_persistence устанавливается на true , исключение поднимается в случае неудачи. Если флаг whiny_persistence устанавливается на false , методы с возвратом к удару true если переход состояния является успешным или false если возникает ошибка.
Если вы хотите убедиться, что состояние сохраняется без проверки (и, тем самым, может сохранять неверное состояние объекта), просто скажите AASM, чтобы пропустить проверки. Имейте в виду, что при пропуске проверки, только столбец состояния будет обновлен в базе данных (точно так же, как Activerecord update_column работает).
class Job < ActiveRecord :: Base
include AASM
aasm skip_validation_on_save : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
event :sleep do
transitions from : :running , to : :sleeping
end
end
end Кроме того, вы можете пропустить валидацию на уровне экземпляра с помощью some_event_name_without_validation! метод При этом вы по умолчанию обладаете гибкостью для валидации для всех ваших переходов, а затем пропустите ее везде, где это необходимо. Обратите внимание, что только столбец состояния будет обновлен, как указано в приведенном выше примере.
job . run_without_validation!Если вы хотите убедиться, что столбец AASM для хранения состояния не назначается напрямую, настройте AASM, чтобы не разрешать прямое назначение, например, это:
class Job < ActiveRecord :: Base
include AASM
aasm no_direct_assignment : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
end
endв результате этого:
job = Job . create
job . aasm_state # => 'sleeping'
job . aasm_state = :running # => raises AASM::NoDirectAssignmentError
job . aasm_state # => 'sleeping' Вы можете сказать AASM , чтобы попытаться написать временную метку всякий раз, когда вводится новое состояние. Если timestamps: true установлено, AASM будет искать поле, названное как новое состояние плюс _at и попытайтесь заполнить его:
class Job < ActiveRecord :: Base
include AASM
aasm timestamps : true do
state :sleeping , initial : true
state :running
event :run do
transitions from : :sleeping , to : :running
end
end
endв результате этого:
job = Job . create
job . running_at # => nil
job . run!
job . running_at # => 2020-02-20 20:00:00Отсутствующие поля временной метки молча игнорируются, поэтому нет необходимости иметь сеттеров (например, столбцы Activerecord) для всех состояний при использовании этой опции.
Вы можете использовать перечисления в Rails 4.1+ для столбца состояния:
class Job < ActiveRecord :: Base
include AASM
enum state : {
sleeping : 5 ,
running : 99
}
aasm column : :state , enum : true do
state :sleeping , initial : true
state :running
end
end Вы можете явно передать имя метода, который обеспечивает доступ к отображению перечисления как значение enum , или вы можете просто установить его на true . В последнем случае AASM попытается использовать плюрализованное имя столбца для доступа к возможным состояниям перечисления.
Кроме того, если в вашем столбце есть целочисленный тип (что обычно имеет место, когда вы работаете с перечислениями Rails), вы можете опустить :enum --- AASM автоматически определяет эту ситуацию и включает поддержку перечисления. Если что -то пойдет не так, вы можете отключить функциональность Enum и вернуться к поведению по умолчанию, установив :enum на false .
AASM также поддерживает продолжение, помимо Activerecord и Mongoid .
class Job < Sequel :: Model
include AASM
aasm do # default column: aasm_state
...
end
endОднако это еще не так, как функция, как Activerecord . Например, есть еще определены области. Смотрите автоматические области.
Поскольку версия 4.8.0 AASM также поддерживает динамоид как Persistence Orm.
AASM также поддерживает постоянство в MongoDB, если вы используете монгоид. Обязательно включите Mongoid :: Document, прежде чем включить AASM.
class Job
include Mongoid :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM также поддерживает постоянство для переосмысления, если вы используете Nobrainer. Обязательно включите Nobrainer :: Document, прежде чем включить AASM.
class Job
include NoBrainer :: Document
include AASM
field :aasm_state
aasm do
...
end
endAASM также поддерживает настойчивость в Redis через Redis :: Объекты. Обязательно включите redis :: объекты, прежде чем включить AASM. Обратите внимание, что события, не являющиеся банками, будут работать в качестве бинга, сохраняя изменения при каждом вызове.
class User
include Redis :: Objects
include AASM
aasm do
end
endAASM автоматически создаст методы масштаба для каждого состояния в модели.
class Job < ActiveRecord :: Base
include AASM
aasm do
state :sleeping , initial : true
state :running
state :cleaning
end
def self . sleeping
"This method name is already in use"
end
end class JobsController < ApplicationController
def index
@running_jobs = Job . running
@recent_cleaning_jobs = Job . cleaning . where ( 'created_at >= ?' , 3 . days . ago )
# @sleeping_jobs = Job.sleeping #=> "This method name is already in use"
end
end Если вам не нужны прицелы (или просто не нужны их), отключите их творение при определении состояний AASM , например:
class Job < ActiveRecord :: Base
include AASM
aasm create_scopes : false do
state :sleeping , initial : true
state :running
state :cleaning
end
endПоскольку версия 3.0.13 Aasm поддерживает транзакции Activerecord. Поэтому всякий раз, когда вызов перехода или обновление состояния не сбои, все изменения в любой записи базы данных отказываются обратно. MongoDB не поддерживает транзакции.
В настоящее время существует 3 транзакционных обратных вызовов, которые можно обработать на мероприятии, и 2 транзакционных вызова для всех событий.
event before_all_transactions
event before_transaction
event aasm_fire_event ( within transaction )
event after_commit ( if event successful )
event after_transaction
event after_all_transactions Если вы хотите убедиться, что действие в зависимости от зависимости только после того, как транзакция будет совершена, используйте обратный вызов after_commit вместе с методами автоматического сав (взблетения), например, это:
class Job < ActiveRecord :: Base
include AASM
aasm do
state :sleeping , initial : true
state :running
event :run , after_commit : :notify_about_running_job do
transitions from : :sleeping , to : :running
end
end
def notify_about_running_job
...
end
end
job = Job . where ( state : 'sleeping' ) . first!
job . run! # Saves the model and triggers the after_commit callback Обратите внимание, что следующее не будет выполнять обратные вызовы after_commit , потому что метод автоматического сажа не используется:
job = Job . where ( state : 'sleeping' ) . first!
job . run
job . save! #notify_about_running_job is not run Обратите внимание, что :after_commit Aasm Callbacks ведет себя вокруг пользовательской реализации шаблона транзакций, а не реальной транзакции DB. Этот факт по -прежнему вызывает условия гонки и избыточные вызовы в пределах вложенной транзакции. Чтобы исправить, что настоятельно рекомендуется добавить gem 'after_commit_everywhere', '~> 1.0' в ваш Gemfile .
Если вы хотите инкапсулировать изменения состояния в собственную транзакцию, поведение этой вложенной транзакции может сбить с толку. Взгляните на вложенные транзакции Activerecord, если вы хотите узнать об этом больше. Тем не менее, AASM по умолчанию требует новой transaction(requires_new: true) . Вы можете переопределить это поведение, изменив конфигурацию
class Job < ActiveRecord :: Base
include AASM
aasm requires_new_transaction : false do
...
end
...
end что затем приводит к transaction(requires_new: false) , рельсы по умолчанию.
Кроме того, если вы не хотите, чтобы какие -либо из ваших действий Activerecord были завернуты в транзакцию, вы можете указать флаг use_transactions . Это может быть полезно, если вы хотите сохранить вещи в базе данных, которая происходит в результате транзакции или обратного вызова, даже когда возникает некоторая ошибка. Флаг use_transactions верен по умолчанию.
class Job < ActiveRecord :: Base
include AASM
aasm use_transactions : false do
...
end
...
end AASM поддерживает пессимистическую блокировку Activerecord VIA with_lock для упорных слоев базы данных.
| Вариант | Цель |
|---|---|
false (по умолчанию) | Не получается блокировка |
true | Получить блокирующую пессимистическую блокировку, например, FOR UPDATE |
| Нить | Получите блокировку на основе строки SQL, например FOR UPDATE NOWAIT |
class Job < ActiveRecord :: Base
include AASM
aasm requires_lock : true do
...
end
...
end class Job < ActiveRecord :: Base
include AASM
aasm requires_lock : 'FOR UPDATE NOWAIT' do
...
end
...
end В качестве по умолчанию AASM использует столбец aasm_state для хранения состояний. Вы можете переопределить это, определив свое любимое имя столбца, используя :column , как это:
class Job < ActiveRecord :: Base
include AASM
aasm column : :my_state do
...
end
aasm :another_state_machine , column : :second_state do
...
end
end Какое бы ни использовалось имя столбца, обязательно добавьте миграцию, чтобы предоставить этот столбец (из string типа). Не добавляйте значение по умолчанию для столбца на уровне базы данных. Если вы добавите значение по умолчанию в базу данных, то обратные вызовы AASM в начальном состоянии не будут запущены при экземпляре модели.
class AddJobState < ActiveRecord :: Migration
def self . up
add_column :jobs , :aasm_state , :string
end
def self . down
remove_column :jobs , :aasm_state
end
endИзменение состояния журнала может быть сделано с помощью GEM Paper_trail
Пример реализации можно найти здесь https://github.com/nitsujri/aasm-papertrail-example
AASM поддерживает методы запроса для состояний и событий
Учитывая следующий класс Job :
class Job
include AASM
aasm do
state :sleeping , initial : true
state :running , :cleaning
event :run do
transitions from : :sleeping , to : :running
end
event :clean do
transitions from : :running , to : :cleaning , guard : :cleaning_needed?
end
event :sleep do
transitions from : [ :running , :cleaning ] , to : :sleeping
end
end
def cleaning_needed?
false
end
end # show all states
Job . aasm . states . map ( & :name )
#=> [:sleeping, :running, :cleaning]
job = Job . new
# show all permitted states (from initial state)
job . aasm . states ( permitted : true ) . map ( & :name )
#=> [:running]
# List all the permitted transitions(event and state pairs) from initial state
job . aasm . permitted_transitions
#=> [{ :event => :run, :state => :running }]
job . run
job . aasm . states ( permitted : true ) . map ( & :name )
#=> [:sleeping]
# show all non permitted states
job . aasm . states ( permitted : false ) . map ( & :name )
#=> [:cleaning]
# show all possible (triggerable) events from the current state
job . aasm . events . map ( & :name )
#=> [:clean, :sleep]
# show all permitted events
job . aasm . events ( permitted : true ) . map ( & :name )
#=> [:sleep]
# show all non permitted events
job . aasm . events ( permitted : false ) . map ( & :name )
#=> [:clean]
# show all possible events except a specific one
job . aasm . events ( reject : :sleep ) . map ( & :name )
#=> [:clean]
# list states for select
Job . aasm . states_for_select
#=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
# show permitted states with guard parameter
job . aasm . states ( { permitted : true } , guard_parameter ) . map ( & :name ) Предупреждения по умолчанию печатаются в STDERR . Если вы хотите записать эти предупреждения на другой вывод, используйте
class Job
include AASM
aasm logger : Rails . logger do
...
end
end Вы можете скрыть предупреждения, установив AASM::Configuration.hide_warnings = true
Теперь поддерживает CodedataQuery! Однако я все еще нахожусь в процессе подачи моих обновлений совместимости в их хранилище. В то же время вы можете использовать мою вилку, все еще могут быть небольшие проблемы, но я собираюсь широко использовать ее самостоятельно, поэтому исправления должны происходить быстро.
Предупреждения:
AASM предоставляет некоторые совпадения для RSPEC:
transition_from ,have_state , allow_eventallow_transition_to . require 'aasm/rspec' в ваш файл spec_helper.rb . # classes with only the default state machine
job = Job . new
expect ( job ) . to transition_from ( :sleeping ) . to ( :running ) . on_event ( :run )
expect ( job ) . not_to transition_from ( :sleeping ) . to ( :cleaning ) . on_event ( :run )
expect ( job ) . to have_state ( :sleeping )
expect ( job ) . not_to have_state ( :running )
expect ( job ) . to allow_event :run
expect ( job ) . to_not allow_event :clean
expect ( job ) . to allow_transition_to ( :running )
expect ( job ) . to_not allow_transition_to ( :cleaning )
# on_event also accept multiple arguments
expect ( job ) . to transition_from ( :sleeping ) . to ( :running ) . on_event ( :run , :defragmentation )
# classes with multiple state machine
multiple = SimpleMultipleExample . new
expect ( multiple ) . to transition_from ( :standing ) . to ( :walking ) . on_event ( :walk ) . on ( :move )
expect ( multiple ) . to_not transition_from ( :standing ) . to ( :running ) . on_event ( :walk ) . on ( :move )
expect ( multiple ) . to have_state ( :standing ) . on ( :move )
expect ( multiple ) . not_to have_state ( :walking ) . on ( :move )
expect ( multiple ) . to allow_event ( :walk ) . on ( :move )
expect ( multiple ) . to_not allow_event ( :hold ) . on ( :move )
expect ( multiple ) . to allow_transition_to ( :walking ) . on ( :move )
expect ( multiple ) . to_not allow_transition_to ( :running ) . on ( :move )
expect ( multiple ) . to transition_from ( :sleeping ) . to ( :processing ) . on_event ( :start ) . on ( :work )
expect ( multiple ) . to_not transition_from ( :sleeping ) . to ( :sleeping ) . on_event ( :start ) . on ( :work )
expect ( multiple ) . to have_state ( :sleeping ) . on ( :work )
expect ( multiple ) . not_to have_state ( :processing ) . on ( :work )
expect ( multiple ) . to allow_event ( :start ) . on ( :move )
expect ( multiple ) . to_not allow_event ( :stop ) . on ( :move )
expect ( multiple ) . to allow_transition_to ( :processing ) . on ( :move )
expect ( multiple ) . to_not allow_transition_to ( :sleeping ) . on ( :move )
# allow_event also accepts arguments
expect ( job ) . to allow_event ( :run ) . with ( :defragmentation ) AASM обеспечивает утверждения и RSPEC, подобные ожиданиям для министров.
Список поддерживаемых утверждений: assert_have_state , refute_have_state , assert_transitions_from , refute_transitions_from , refute_event_allowed , assert_event_allowed , assert_transition_to_allowed , refute_transition_to_allowed .
Добавьте require 'aasm/minitest' в ваш файл test_helper.rb и используйте их так:
# classes with only the default state machine
job = Job . new
assert_transitions_from job , :sleeping , to : :running , on_event : :run
refute_transitions_from job , :sleeping , to : :cleaning , on_event : :run
assert_have_state job , :sleeping
refute_have_state job , :running
assert_event_allowed job , :run
refute_event_allowed job , :clean
assert_transition_to_allowed job , :running
refute_transition_to_allowed job , :cleaning
# on_event also accept arguments
assert_transitions_from job , :sleeping , :defragmentation , to : :running , on_event : :run
# classes with multiple state machine
multiple = SimpleMultipleExample . new
assert_transitions_from multiple , :standing , to : :walking , on_event : :walk , on : :move
refute_transitions_from multiple , :standing , to : :running , on_event : :walk , on : :move
assert_have_state multiple , :standing , on : :move
refute_have_state multiple , :walking , on : :move
assert_event_allowed multiple , :walk , on : :move
refute_event_allowed multiple , :hold , on : :move
assert_transition_to_allowed multiple , :walking , on : :move
refute_transition_to_allowed multiple , :running , on : :move
assert_transitions_from multiple , :sleeping , to : :processing , on_event : :start , on : :work
refute_transitions_from multiple , :sleeping , to : :sleeping , on_event : :start , on : :work
assert_have_state multiple , :sleeping , on : :work
refute_have_state multiple , :processing , on : :work
assert_event_allowed multiple , :start , on : :move
refute_event_allowed multiple , :stop , on : :move
assert_transition_to_allowed multiple , :processing , on : :move
refute_transition_to_allowed multiple , :sleeping , on : :move Список поддерживаемых ожиданий: must_transition_from , wont_transition_from , must_have_state , wont_have_state , must_allow_event , wont_allow_event , must_allow_transition_to , wont_allow_transition_to .
Добавьте require 'aasm/minitest_spec' в ваш файл test_helper.rb и используйте их так:
# classes with only the default state machine
job = Job . new
job . must_transition_from :sleeping , to : :running , on_event : :run
job . wont_transition_from :sleeping , to : :cleaning , on_event : :run
job . must_have_state :sleeping
job . wont_have_state :running
job . must_allow_event :run
job . wont_allow_event :clean
job . must_allow_transition_to :running
job . wont_allow_transition_to :cleaning
# on_event also accept arguments
job . must_transition_from :sleeping , :defragmentation , to : :running , on_event : :run
# classes with multiple state machine
multiple = SimpleMultipleExample . new
multiple . must_transition_from :standing , to : :walking , on_event : :walk , on : :move
multiple . wont_transition_from :standing , to : :running , on_event : :walk , on : :move
multiple . must_have_state :standing , on : :move
multiple . wont_have_state :walking , on : :move
multiple . must_allow_event :walk , on : :move
multiple . wont_allow_event :hold , on : :move
multiple . must_allow_transition_to :walking , on : :move
multiple . wont_allow_transition_to :running , on : :move
multiple . must_transition_from :sleeping , to : :processing , on_event : :start , on : :work
multiple . wont_transition_from :sleeping , to : :sleeping , on_event : :start , on : :work
multiple . must_have_state :sleeping , on : :work
multiple . wont_have_state :processing , on : :work
multiple . must_allow_event :start , on : :move
multiple . wont_allow_event :stop , on : :move
multiple . must_allow_transition_to :processing , on : :move
multiple . wont_allow_transition_to :sleeping , on : :move % gem install aasm # Gemfile
gem 'aasm'% rake build
% sudo gem install pkg/aasm-x.y.z.gemПосле установки AASM вы можете запустить генератор:
% rails generate aasm NAME [COLUMN_NAME]Замените имя с именем модели, Column_Name является необязательным (по умолчанию «AASM_STATE»). Это создаст модель (если никто не существует) и настроит ее с помощью блока AASM. Для ActiveRecord orm добавляется файл миграции, чтобы добавить столбец состояния AASM в таблицу.
Легко запустить тестовый набор на Docker
1. docker-compose build aasm
2. docker-compose run --rm aasm
Взгляните на изменение изменений о недавних изменениях в текущей версии.
Не стесняйтесь
aasm )Это программное обеспечение предоставляется «как есть» и без каких -либо явных или подразумеваемых гарантий, включая, без ограничения, подразумеваемые гарантии товарности и пригодности для определенной цели.
Copyright (C) 2006-2017 Скотт Баррон
Настоящим дается разрешение, бесплатно, любому лицу, получающему копию этого программного обеспечения и связанные с ними файлы документации («Программное обеспечение»), чтобы иметь дело в программном обеспечении без ограничений, включая, без ограничения, права на использование, копирование, изменение, объединение, публикацию, распределение, сублиценность и/или продавать копии программного обеспечения и разрешения лиц, на которые программное обеспечение подходит для того, чтобы поступить так, чтобы поступить на следующие условия: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: на следующие условия: к следующим условиям: на следующие условия: на следующие условия.
Вышеуказанное уведомление об авторском праве и это уведомление о разрешении должно быть включено во все копии или существенные части программного обеспечения.
Программное обеспечение предоставляется «как есть», без гарантии любого рода, явного или подразумеваемого, включая, помимо прочего, гарантии товарной пригодности, пригодности для определенной цели и несоответствия. Ни в коем случае авторы или владельцы авторских прав не будут нести ответственность за любые претензии, убытки или другую ответственность, будь то в действии контракта, деликт или иным образом, возникающие из или в связи с программным обеспечением или использованием или другими сделками в программном обеспечении.