[Tradução] Ruby on Rails Finite State Machine Plugin: acts_as_state_machine

janeiro 27th, 2008 | by Ramon Soares |

Click aqui para ler a versão original em inglês

Uma Maquina de Estado Finito é um modelo de comportamento com um número finito de estados, interligados através de transições e eventos.

Neste guia, eu vou mostrar como o modelo de Maquina de Estado Finito pode ser recriado facilmente com um plugin do Ruby on Rails. O plugin que vamos usar é o acts_as_state_machine, que não é muito bem documentado. O Google retorna alguns resultados, mais não são muito bons, principalmente para iniciantes.

Porque você deve usar uma Maquina de Estado Finito?

Para iniciar, se seu modelo tem um numero finito de diferentes estados, e você procura uma maneira fácil de fazer chamadas entre as mudanças de estados, como uma notificação, validação, incremento ou qualquer outra coisa..

Instalando o acts_as_state_machine

Vá ate a pasta raiz de sua aplicação e execute:

 $ ./script/plugin install http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/

Usando o acts_as_state_machine

  class Person < ActiveRecord::Base
    acts_as_state_machine :initial => :sleeping  

    state :sleeping
    state :showering
    state :working
    state :dating  

    event :shower do
      transitions :from => :sleeping, :to => :showering
      transitions :from => :working, :to => :showering
      transitions :from => :dating, :to => :showering
    end  

    event :work do
      transitions :from => :showering, :to => :working
      # Going to work before showering?  Stinky.
      transitions :from => :sleeping, :to => :working
    end  

    event :date do
      transitions :from => :showering, :to => :dating
    end  

    event :sleep do
      transitions :from => :showering, :to => :sleeping
      transitions :from => :working, :to => :sleeping
      transitions :from => :dating, :to => :sleeping
    end
  end

NOTA: O plugin assume que o estado de seu model fica no campo state. Isso pode ser substituido com a adição da opção :column => 'field'.

AVISO: Se você estiver usando um model que armazena endereços e existir o campo "state". Você passara horas se perguntando por que as coisas não funcionam.

Observe que na linha 2 é declarado explicitamente o estado inicial. Dá linha 4 à 7 são indicados os vários estados que o Person pode estar.

Existe um aspecto bem peculiar ao se criar objetos usando o metodo new, quando o estado do objeto não é informado. O estado só será especificado quando salvar o novo registro. Uma solução é especificar o valor padrão do estado dentro da migração. Outra solução é usar o método create

Exemplo:

  person = Person.new
  person.state # nil
  person.save # true
  person.state # "sleeping"

  person = Person.create
  person.state # "sleeping"
  person.sleeping? # true
  person.rotting? # false

  person = Person.new
  person.state = "rotting" # "rotting"
  person.rotting? # true
  person.sleeping? # false

NOTA: Caso não tenha notado, o método para testar se o model“state?”

Os events que você especifica também criam métodos na instância, para transitar de um state para outro.

Segue exemplos dos métodos que foram criados:

  person.shower!
  person.work!
  person.date!
  person.sleep!

NOTA: Os métodos criados seguem o padrão "event!"

Events

NOTA: Ao chamar um event, você também chama ActiveRecord::Base.save. Para quando ele falhar, apenas retorna false. Assegure-se de chamar valid? e save!.

Events ajuda a você a migrar de um state para outro. Supondo que seu person esteja dormindo (sleeping), e queremos que ele vá para o chuveiro (shower), é só chama shower!.

  person.state # "sleeping"
  person.shower!
  person.state # "showering"

Events te ajuda a organizar o fluxo no seu model. Porem eles podem ficar mais “poderosos” com os callbacks.

Callbacks

O state possui alguns callbacks que podem ser usados.

 state :sleeping,
          :enter => :get_into_bed,
          :after => Proc.new {|model| model.whack_alarm_clock },
          :exit => :make_up_bed

Callbacks são chamados quando os models estão em transição para um state especifico.

NOTA:

  • Callbacks podem ser symbolou Proc. Se usar um symbol, um método da instancia do model será chamado.
  • Os callbacks agem diferentes em um model salvo de um novo e não salvo.

Levando em consideração as chamadas ActiveRecord, o callback de uma nova entrada seria:

  • ActiveRecord::Base.before_save
  • ActiveRecord::Base.save
  • acts_as_state_machine :enter sleeping
  • acts_as_state_machine :after sleeping
  • ActiveRecord::Base.after_save

Se o model não é uma nova entrada, os callbacks executam o seguinte, se eu tiver chamado o método showe!.

  • acts_as_state_machine :enter showering
  • ActiveRecord::Base.before_save
  • ActiveRecord::Base.save
  • ActiveRecord::Base.after_save
  • acts_as_state_machine :after showering
  • acts_as_state_machine :exit sleeping

Protegendo os states

Mas como saber se você quer alguma espécie de validação para uma transição. Você sabe, só para garantir a integridade dos dados.

  event :work do
    transitions :from => :showering, :to => :working
    # Going to work before showering?  Stinky.
    transitions :from => :sleeping, :to => :working, :guard => Proc.new {|o| o.clean? }
  end

A transição pode ser protegida especificando a opção :guard com um symbol ou um Proc (semelhante aos callbacks). O método ou Proc deve retornar true para prosseguir com a transição, ou ira falhar silenciosamente.

UPDATE: 26-05-2008

LINKS

  1. http://diogenesf.wordpress.com/2007/12/11/acts_as_state_machine-gerenciando-estados-de-um-model-rails/
  1. One Response to “[Tradução] Ruby on Rails Finite State Machine Plugin: acts_as_state_machine”

  2. By Djalma on jan 28, 2008 | Reply

    Fale mestre!

Post a Comment