« HE:labs
HE:labs

State Machine

Postado por Rodrigo Reginato em 05/11/2013

Nesse post mostrarei algumas funcionalidades da gem state_machine para gerenciar e simplificar o comportamento de uma classe.

O estado do objeto normalmente é mantido através de vários estados booleanos, isso pode-se tornar complicado de manter quando a complexidade da classe aumenta. Com o state_machine, fica bem visível para qualquer desenvolvedor que olhe o código e observe as transições dos estados que são possíveis.

Primeiro, farei uma validação de inclusão desses estados:

1 validates :status, :inclusion => { :in => %w(new allowed disabled) }

Agora, vamos ao código do state_machine:

 1 class Permission < ActiveRecord::Base
 2   state_machine :status, initial: :new do
 3       event :allow do
 4         transition [:new, :disabled] => :allowed
 5       end
 6       event :disable do
 7         transition [:new, :allowed] => :disabled
 8       end
 9       event :restart do
10         transition [:disabled, :allowed] => :new
11       end
12   end
13 end

Temos o estado inicial que é :new. Sempre que um novo objeto for criado, o primeiro estado deve ser :new. Existem 3 eventos possíveis nesse caso: :allow, :disable e :restart.

No primeiro evento (:allow), se o status for :new ou :disabled, a transição para :allowed é válida quando executado o comando abaixo:

1 @permission.allow

Executando o evento :disable: só será válido se o status estiver :new ou :allowed.

1 @permission.disable

E no Terceiro evento (:restart), se o status for :disabled ou :allowed, a transição para :new é válida quando executado o comando abaixo:

1 @permission.restart

Uma função muito utilizada é o after_transaction.

1 after_transition :new => :allowed do |permission, transition|
2      permission.send_mail_to_user(permission.user)
3   end

Nesse exemplo, após a transição do status de :new para :allowed, é enviado um email para o usuário informando que a permissão foi concedida.

E como não poderia faltar, um exemplo de teste para as transições:

 1 describe 'status state machine' do
 2     let!(:user) { create(:user) }
 3     let!(:permission) { create(:permission, user: user, status: "new") }
 4 
 5     it 'permission allowed' do
 6       expect{
 7         permission.allow
 8       }.to change(permission, :status).from('new').to('allowed')
 9     end
10 
11     it 'permission is disabled' do
12       expect{
13         permission.disable
14       }.to change(permission, :status).from('new').to('disabled')
15     end
16   end

Apenas um exemplo básico. Para o evento restart, é basicamente o mesmo código.

Um abraço.

Compartilhe

Sabia que nosso blog agora está no Medium? Confira Aqui!