2010-08-19 12 views
16

¿Es posible enviar variables en la transición? es decirPasar variables a Rails StateMachine gem transitions

@car.crash!(:crashed_by => current_user) 

tengo devoluciones de llamada en mi modelo, pero tengo que enviarles el usuario que promovió la transición

after_crash do |car, transition| 
    # Log the car crashers name 
end 

No puedo acceder a current_user porque estoy en el modelo y no el controlador /Ver.

Y antes de decirlo ... Sé que lo sé.

No trate de acceder a las variables de sesión en el modelo

lo entiendo.

Sin embargo, siempre que desee crear una devolución de llamada que registre o audite algo, entonces es muy probable que quiera saber quién lo causó. Normalmente tendría algo en mi controlador que hizo algo así como ...

@foo.some_method(current_user) 

y mi modelo de Foo estaría esperando algún usuario para instigar some_method pero ¿cómo puedo hacer esto con una transición con la gema StateMachine?

Respuesta

32

Si se está refiriendo a la gema state_machine - https://github.com/pluginaweek/state_machine - entonces es compatible con argumentos a eventos

after_crash do |car, transition| 
    Log.crash(:car => car, :driver => transition.args.first) 
end 
+2

Por coincidencia terminé tropezando con mi propia pregunta de nuevo en una búsqueda en Google y esta respuesta fue muy útil. Gracias. –

+0

Creo que esto es definitivamente mejor que User.current que veo que otros implementan cuando se encuentran con este tipo de problema. La interacción del usuario pertenece a la capa del controlador. –

+2

Esto funciona bien, pero * do * tiene que deletrear la transición correctamente. La solución anterior tiene un extra 's'. – Brenda

2

No creo que pueda pasar parámetros a los eventos con esa gema, así que tal vez podría tratar de almacenar el usuario_actual en @car (temporalmente) para que su devolución de llamada de auditoría pueda acceder a él.

En controlador

@car.driver = current_user 

En devolución de llamada

after_crash do |car, transition| 
    create_audit_log car.driver, transition 
end 

O algo por el estilo .. :)

+0

Gracias Keeran. Esto es básicamente lo que terminé haciendo al final. Como el atributo no tiene permanencia, ¿piensa en cómo podría hacerlo sin crear un nuevo campo en el DB? –

+0

Puede intentar agregar un attr_accessor para: driver, pero no estoy seguro de si la devolución de llamada se volverá a cargar/usar una instancia "nueva" del modelo (y así borrar la var temporal). – keeran

7

Estaba teniendo problemas con todas las otras respuestas, y luego descubrí que simplemente puede anular el evento en la clase.

class Car 
    state_machine do 
    ... 
    event :crash do 
     transition any => :crashed 
    end 
    end 
    def crash(current_driver) 
    logger.debug(current_driver) 
    super 
    end 
end 

Sólo asegúrese de llamar "super" en su método personalizado

+0

Esta es, de lejos, la respuesta más fácil y directa. –

0

Otro patrón común (véase la state_machine docs) que le ahorra de tener que pasar variables entre el controlador y el modelo es definir dinámicamente un estado - método de comprobación dentro del método de devolución de llamada. Esto no sería muy elegante en el ejemplo anterior, pero podría ser preferible en los casos en que el modelo necesite manejar la misma variable en diferentes estados.Por ejemplo, si tiene 'estrellado', 'robado', y los estados 'prestadas' en su modelo de coche, todo lo cual puede estar asociado con una persona responsable, usted podría tener:

state :crashed, :stolen, :borrowed do 
    def blameable? 
    true 
    end 

state all - [:crashed, :stolen, :borrowed] do 
    def blameable? 
    false 
    end 

Luego, en el controlador , puede hacer algo como:

car.blame_person(person) if car.blameable? 
1

He usado transacciones, en lugar de actualizar el objeto y cambiar el estado en una llamada. Por ejemplo, en acción de actualización,

ActiveRecord::Base.transaction do 
    if @car.update_attribute!(:crashed_by => current_user) 
    if @car.crash!() 
     format.html { redirect_to @car } 
    else 
     raise ActiveRecord::Rollback 
    else 
    raise ActiveRecord::Rollback 
    end 
end