sisè / segona

Soy Víctor Rodríguez, programador web freelance con más de 10 años de experiencia

Render la última página vista en Rails

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