martes, 20 de septiembre de 2011

Cambiando el formato de fecha

Si tu formato de fecha no es YYYY-MM-DD esto te puede interesar.

Por defecto en rails tenemos dos tipos de objectos para gestionar fechas los objectos date (de aquí en adelante "_on") que permiten guardar una fecha con año, mes y día y los objetos timestamp (de aquí en adelante "_at") que permiten guardar una fecha con año, mes, día, hora, minuto y segundo (sin zona horaria). Sus formato de salida por defecto son:

Fecha (output):

Fecha (input):

Primer solución (y la oficial):

Utilizar la API de internacionalización de RAILS, resumo los pasos de forma rápida:
  1. Descargar los "locales" que no interesen de https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale
  2. Copiarlos en "config\locales\" los que vayamos a utilizar.
  3. En el fichero "config\aplication.rb" descomentar la siguientes línias
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de
  4. Y establecer nuestro idioma por defecto, para español sería:
    config.i18n.default_locale = :es
  5. Reiniciar el servidor web
Con lo que tendremos:
Con lo que hemos solucionado las entradas de fecha (input), pero no las salidas (output). Para solucionar las salidas tenemos que:
  1. Crear el fichero "initializers\date_formats.rb"
  2. Añadir:
    Time::DATE_FORMATS[:default] = lambda { |time| I18n.l(time) }
    Date::DATE_FORMATS[:default] = lambda { |date| I18n.l(date) }
  3. Reiniciar el servidor web
Con lo que tendremos:
En mi caso el formato por defecto para el locale ca (catalán) no era el deseado, con lo que abrí "config\locales\ca.yml" y modifiqué "default: "%A, %d de %B de %Y %H:%M:%S %z"" por "default: "%d de %b %H:%M""

Solución alternativa (sin internacionalizar):

Una solución única:
  1. Crear el fichero "initializers\date_formats.rb"
  2. Añadir nuestra definición de formatos. Ejemplo:

    DATE_AT_FORMAT = "%d/%m/%Y %H:%M"
    DATE_ON_FORMAT = "%d/%m/%Y"

    Time::DATE_FORMATS.merge!(
    :standard => DATE_AT_FORMAT,
    :default => DATE_AT_FORMAT,
    :standard_time => DATE_AT_FORMAT
    )

    Date::DATE_FORMATS.merge!(
    :standard => DATE_ON_FORMAT,
    :default => DATE_ON_FORMAT,
    :standard_time => DATE_ON_FORMAT
    )

    La siguiente alternativa también funciona:

    DATE_AT_FORMAT = "%d/%m/%Y %H:%M"
    DATE_ON_FORMAT = "%d/%m/%Y"

    Time::DATE_FORMATS[:default] = DATE_AT_FORMAT
    Date::DATE_FORMATS[:default] = DATE_ON_FORMAT

Con esto habremos conseguido corregir los outputs, ahora si queremos corregir los inputs tendremos que ir uno por uno y añadir la línia ":order => [:day, :month, :year]", ejemplos:

    <%= f.datetime_select :start_at, :order => [:day, :month, :year]%>
    <%= f.date_select :start_on, :order => [:day, :month, :year] %>

domingo, 18 de septiembre de 2011

Double submit -> Double post -> Duplicated data

Si tus usuarios hacen esto:
  1. Pulsar dos veces la tecla enter/intro
  2. Doble click en "guardar"
Y te has encontrado con:
  1. Registros duplicados aleatorios inexplicables.
  2. Usuarios que intentan sabotear / automatizar la aplicación mandando múltiples submits.
  3. Una lógica de modelo que no gestiona los duplicados.
Esto te puede interesar:

En algunos casos, bien por la acción del mismo usuario o por algún error derivado del envió del post, el servidor recibe dos peticiones de post idénticas, entonces se puede dar el caso que acepte las dos y se generen registros duplicados o que se guarde un registro y se devuelva un mensaje de error para el segundo, con lo que confundiremos al usuario.

Aproximaciones al problema:
  1. Implementación javascript del tipo "<form onSubmit="doublePostCheck()"> donde la función solo devuelve verdadero la primera vez.
  2. Token / Flag validation: de un variable tipo "hidden" en el cliente y validación en el servido.
  3. Hash de les variables del post y comparación de este con el último.
En el punto 1 dependemos del usuario (que no desactive el Javascript o lo salte), en los puntos 2 y 3 no queda claro como gestionar el segundo submit.

La solución ':disable_with => 'Saving...' :

Modificar la vista y añadir "<%=  f.submit 'Save', :disable_with => 'Saving...' %>".

La solución cumple con:
  • Evita el problema del "doble enter", "doble click".
  • Evita el problema del doble envio del post
  • KISS (Keep it simple stupid).
  • SPOT (Single point of thrut).

BluePrint y CSS from rails 3.0 to rails 3.1

Si utilizas el framewrok Blueprint y has migrado una aplicación de rails 3.0 a 3.1 esto te puede interesar, experiencias:

>> La solución oficial (Estilos dentro de assets):
  1. Mover las hojas de estilo de "public\stylesheets" a "app\assets\stylesheets"
  2. Mover las imágenes de "\public\images" a "app\assets\images"
  3. Eliminar las llamadas a CSS "stylesheet_link_tag" de los layouts

Problemas:
  1. Perdemos el condicional de "print, :media => 'print'" y "screen, :media => 'screen, projection'", con lo que si queremos utilizar BluePrint tendremos que borrar uno de los dos (app\views\layouts\*.erb)
  2. Si tenemos alguno estilo condicional como en nuestro caso "devise.css" lo tenemos que aplicar a todas las hojas.

>> Migrar blueprint a SASS:

Si migrar a SASS es una condición sinecuanum os puede interesar el siguiente proyecto "https://github.com/chriseppstein/blueprint-sass", nosotros no lo hemos probado.

>> Nuestra solución mixta:
  1. Mover las hojas de estilo propias (por ejemplo devise.css) y el framework Blueprint a "vendor\assets\stylesheets".
  2. Mover las hojas de estilo comunes (típicamente proyecto.css) de "public\stylesheets" a "app\assets\stylesheets"
  3. Mover las imágenes de "\public\images" a "app\assets\images"
  4. Corregir las rutas de las imágenes en los hojas estilos, típicamente "proyecto.css". Ejemplo
    • Original: "background-image: url(/images/logo.png);"
    • Final: "background-image: url(/assets/logo.png);"
    • Este paso solo hace falta si queremos mantener todas nuestras imágenes en la carpeta "app\assets\images" si las dejamos en "\public\images" no hará falta hacer el cambio, pero tendremos que acordarnos que tenemos imágenes en dos carpetas distintas.
Ventajas:
  1. Nos permite migrar la aplicación a 3.1 con pocos cambios
  2. Nos permite en un futuro empezar a utilizar SASS
  3. Nos permite mantener estilos condicionales.

Hello world

Hello world... o como decía mi profesor de programación Hola Manola.