En Rails existe una manera muy sencilla de redireccionar a la última acción visitada por el usuario de nuestra aplicación. Es tan sencillo como llamar a esta función dentro de cualquier acción:
redirect_back(fallback_location, **args)
Opcionalmente se le puede pasar el parámetro fallback_location (con valor a una url o path por defecto) para asegurarnos que siempre llegará a algún sitio. Imagina que no ha habido ninguna petición anterior a esta llamada… 404?
Bueno, esto es a partir de la versión 5 de Rails, para versiones anteriores sería:
redirect_to :back
Pero no pasa lo mismo si lo que queremos es simplemente renderizar la última página vista.
Os pongo un ejemplo: tenemos un modelo que se puede editar/actualizar desde 2 páginas distintas en nuestra aplicación, pero como lo que estamos haciendo es actualizar los datos del modelo siempre acabamos llamando a la misma acción ‘update’ del controller del modelo. Algo así para el modelo User
def update
@user = User.find(params[:id])
if @user.update user_params
redirect_to @user, notice: 'Usuario actualizado de manera correcta.'
else
render :edit
end
end
Si las validaciones fallan a la hora de actualizar los datos, es habitual renderizar la pantalla anterior y mostrar los errores que se han producido. La clave es esta línea:
render :edit
El problema con esto es que siempre nos llevará a la misma pantalla, a la de edición, y a lo mejor queremos que la pantalla que vea nuestro usuario sea la de la vista de otro modelo (donde hemos editado los datos).
Bueno, pues aquí viene nuestro amigo referrer a la ayuda! En el objeto ActionDispatch::Request contiene la url previa visitada. Podemos acceder a dicha url mediante el header ‘Referer’ o directamente por el método request.referrer. Con esta información podemos traducir la url por un controlador y una acción gracias al router de la aplicación. La llamada sería la siguiente:
# con request.referrer = 'http://localhost:3000/user/1'
Rails.application.routes.recognize_path(request.referrer)
# Devolería: { :controller => 'users', :action => 'show', :id => '1' }
De esta manera, podemos acceder al action y llamar a render sin más en nuestro controller:
prev = Rails.application.routes.recognize_path(request.referrer)
render prev[:action].to_sym
Si lo queremos hacer extensible a toda nuestra aplicación podemos crear el siguiente método dentro del ApplicationController:
def render_back(fallback)
if request.referrer
prev = Rails.application.routes.recognize_path(request.referrer)
render prev[:action].to_sym
else
render fallback
end
end
Y llamarlo siempre que necesitemos así:
render_back(fallback: :edit)
Ale, eso es todo. Bueno, un problema que veo en esta implementación es que no tenemos en cuenta el controller, por lo que sólo funciona de manera correcta siempre y cuando la edición/actualización del modelo sea desde dos acciones del mismo controller.
:P