rieles: implementa un bloqueo simple para evitar que los usuarios editen los mismos datos simultáneamente

StackOverflow https://stackoverflow.com/questions/1803574

Pregunta

Tengo una aplicación donde necesito evitar que los usuarios editen datos mientras un usuario diferente los está editando. Estoy tratando de pensar en la mejor manera de hacerlo y quería pedir ideas. Hasta ahora, he creado un modelo de configuración que almacena la configuración de toda la aplicación en la base de datos en pares clave / valor. Entonces, para el bloqueo, tengo una instancia de configuración que se llama LOCKED_TABLE_UID, y almacenó el user_id del usuario que edita la tabla o nulo (nulo) si la tabla está libre.

>> lock = Setting.find_by_key('LOCKED_TABLE_UID')

Luego, implementé 2 métodos en mi controlador de aplicación para adquirir y liberar el bloqueo:

# current_user returns the user currently logged in
def acquire_lock
  lock = Setting.find_by_key("LOCKED_TABLE_UID")
  if lock.value
    # if lock taken, see if it's the current_user or someone else
    if lock.value.to_i == current_user.id.to_i
      return true
    else
      return false
    end
  else
    # lock is free, assign it to this user
    lock.value = current_user.id
    return true if lock.save
  end
end

def release_lock
  lock = Setting.find_by_key("LOCKED_TABLE_UID")
  if lock.value
    # the lock belongs to current_user, so he can release it
    if lock.value.to_i == current_user.id.to_i
      lock.value = nil
      return true if lock.save
    else
      # not your lock, go away
      return false
    end
  else
    # lock is free, quit bugging
    return true
  end
end

Lo que quiero es crear algún tipo de código de bloque que contenga el mecanismo de bloqueo, algo como esto:

def some_crud_action
  requires_locking do |lock|
    if lock
      # do some CRUD stuff here
    else
      # decline CRUD and give some error
    end
  end
end

Apreciaría la ayuda en esto, pero también estoy abierto a otras sugerencias sobre cómo lograr todo eso, o algunas cosas que puedo haber pasado por alto. Este bloqueo no tiene que ser atómico, sino bastante básico y lo más importante: que funcione :) gracias.

¿Fue útil?

Solución

Ya casi estás allí. ¿Crear su require_locking? acción como mejor te parezca. Luego procesalo con un before_filter.

 before_filter :requires_locking?, :only => [:update, :destroy]
 after_filter :release_lock, :only => [:update, :destroy]

 def requires_locking do |lock|
   unless acquire_lock
      lock = Setting.find_by_key("LOCKED_TABLE_UID")
      user_with_lock = User.find(lock.value)
      flash[:message] = "Action denied: Table locked by: #{user_with_lock.name}"
      redirect_to :back
   end
 end

Otros consejos

¿Has visto la función de bloqueo integrada ActiveRecord?

Me gusta la idea, pero veo un gran problema con su solución, y es que está obteniendo y liberando bloqueos de tablas enteras.

Para una aplicación muy pequeña que podría estar bien, pero imagínese si tiene miles de usuarios intentando acceder a, digamos, la tabla 'PRODUCTOS' y tener que esperar porque alguien está editando una entrada que no está relacionada con sus propios productos.

Tal vez podría tener un enfoque de grano más fino y bloquear filas particulares en lugar de tablas. Luego, el bloqueo incluiría el nombre de la tabla, el ID de fila y el ID de usuario.

Creo que Act_as_lockable_by gema está haciendo exactamente lo que pediste en términos más simples y con menos código. Es fácilmente integrable con rieles o incluso un proyecto de rubí desnudo.

Con esta gema, obtienes métodos atómicos de bloqueo , desbloqueo y renew_lock . Además, obtienes bloqueos ttl de caducidad automática, por lo que si la mierda golpea al ventilador y no puedes desbloquear el recurso, ¡se desbloqueará automáticamente para ti!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top