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

Lanzar una Rails Task dentro de una migración

Para uno de los proyecto en los que estoy trabajando he creado una migración y una task para migrar unos datos. En concreto he usado, no se si llamarle pattern, counter_cache en una asociación entre modelos para evitar el problema de N+1 queries. Hasta ahí todo normal.

Lo curioso es que a la hora de publicar a producción la nueva versión he encontrado una manera de lanzar la task dentro de la migración. Así con una simple llamada de capistrano se ejecuta todo y no tengo que acceder al server a ejecutar la task manualmente. Os pongo el ejemplo:

class AddCommentsCountToArticle < ActiveRecord::Migration[5.0]
  def change
    add_column :articles, :comments_count, :integer, default: 0

    reversible do |dir|
      dir.up do
        Rake::Task['model:reset_counters'].invoke
      end
    end
  end
end

La clave está en llamar (invocar) la task dentro de este código:

reversible do |dir|
  dir.up do
    Rake::Task['namespace:taskname'].invoke
  end
end

Donde namespace es le namespace de la task y taskname el nombre que le hayas dado a tu task. Así de fácil.

:P

Generar helpers con Laravel 5

Estoy enfrascado en un proyecto en el que uso el framework de PHP Laravel 5.3. Y bueno, en un alarde de mantener limpio el código (seguir los principios KISS y DRY por ahí en medio) se me ocurrió crear un helper para visualizar números con formato en euros, algo así como number_to_currency de Ruby on Rails.

Y aquí empezaron mis dudas, ¿existe ya?, ¿cómo se llamará? Empecé a buscar pero no encontré nada en los helpers del propio framework. Así que vamos a crearlo de cero.

Buscando por Google encontré la solución, bastante sencilla además. Debes seguir los siguientes pasos:

1. Crea tu fichero helpers.php en app/Http/helpers.php por ejemplo. Ya que se va a utilizar para visualizar datos me parece un buen lugar para colocar el fichero.

2. Añade tu función helper dentro del fichero. Esta es la mía:

<?php

function currency($price) {
    return number_format($price, 2, ',', '.') . ' €';
}

3. Registra el fichero helpers en el fichero composer.json:

"autoload": {
    ...,
    "files": [
        "app/Http/helpers.php"
    ]

4. Haz que la aplicación se entere con este comando:

composer dump-autoload

Y ala, a utilizar el helper!

Comenzar de nuevo

Voy de camino a Madrid. Más concreto, a la Conferencia Rails que se celebra este fin de semana. Y me siento como si volviera a empezar como freelance. 

Hacía cuatro meses que me levantaba cada mañana para ir al mismo sitio. Una oficina de la Diagonal, abrir un pc, conectarme a dos VPNs (sí, a falta de una, dos), y pasar la jornada laboral en el mismo proyecto. Pero eso se acabó ayer. 

Lo mejor de este tiempo ha sido sin duda la gente que he encontrado. Embutido en un bocadillo de Guillermos, qué grandes sois bros., con Lucia, Clara, Sandy y Toño por un lado, y la frontera ES-PT ;) por otro he pasado buenos momentos. Roberto, Noemi, Oriol, Laura y Toni. Bueno, a todos los que han ayudado cuando ha hecho falta. Un placer haberos conocido. 

Disponible para contratación

Actualmente me encuentro en búsqueda y captura. Vamos, que busco nuevos proyectos a los que aportar mi granito de arena. Si tienes interés en realizar un proyecto, o sabes de alguien a quien le pueda interesar, ponte en contacto conmigo o pasa la voz. =P

En qué te puedo ayudar:

  • Consultoría, programación o maquetación principalmente. Le estoy cogiendo mucho cariño a hacer webs con Ruby on Rails, aunque también trabajo con PHP (Laravel, o Slim al poder) y Java.
  • Más de 13 años de experiencia en el mercado laboral. He pasado desde programar cosas divertidas con Action Script (hace ya unos cuantos años) a ser el responsable tecnológico en varios proyectos del sector de hostelería. Ahora soy programador freelance.
  • Si quieres puedes ver mi currículum actualizado en mi perfil de Linkedin.
  • O puedes ver algo de código en mi perfil de Github.

Qué busco:

  • Me da igual si tu proyecto ya está iniciado o ahora mismo es una hoja en blanco. Idealmente me gustaría una colaboración a media jornada, pero si necesitas que sea a jornada completa se puede estudiar.
  • Trabajo remoto. Entiendo las necesidades de hacer reuniones y mantener una comunicación fluida. Pero si puedo organizarme mi tiempo trabajaré mucho más a gusto.

A continuación verás mis datos de contacto. ¿Hablamos?