Tablas rápidas para Grails

Si hay algo odioso cuando hay que hacer una interfaz de administración, es armar la lista para cada modelo del dominio para los AMBs. Esta semana me tocó mejorar el diseño para una aplicación grails, por lo que decidí ver como se podían generar “helpers” o TagLibs en nomenclatura grails.

El siguiente código genera usando bootstrap3 una tabla con paginado, botón de editar y eliminar de forma genérica:

package test
 
class ResourceTableTagLib {
    /**
     * Creates a bootstrap3 compatible datatable.<br/>
     *
     * @emptyTag
     *
     * @attr list REQUIRED List of domain models to show
     * @attr count REQUIRED Total registers (for pagination)
     * @attr labels REQUIRED Comma-separated values for the columns headers
     * @attr columns REQUIRED Comma-separated of model attributes to show
     */
    def table_for = { attrs ->
        def writer = out
        def list = attrs.list
        def count = attrs.count
        def labels = attrs.labels.split(/,/).collect { x -> x.trim() }
        def columns = attrs.columns.split(/,/).collect { x -> x.trim() }
 
        writer << '<div class="dataTable_wrapper">'
        writer << '<table class="table table-striped table-bordered table-hover" id="dataTables-example">'
        writer << '<thead>'
        writer << '<tr>'
        labels.eachWithIndex { a, i ->
            writer << "<th>${a}</th>"
        }
        writer << '<th></th>'
        writer << '</tr>'
        writer << '</thead>'
        writer << '<tbody>'
        list.eachWithIndex { a, i ->
            writer << "  <tr class=\"${(i % 2) == 0 ? 'even' : 'odd'}\">"
            writer << "  <td>"
            writer << g.link(action: "show", id: a.id, a."${columns[0]}")
            writer << "  </td>"
            columns.eachWithIndex { c, j ->
                if (j != 0) {
                    writer << "<td>${a."${columns[j]}"}</td>"
                }
            }
            writer << '  <td>'
            writer << g.form(url: [resource:a, action:'delete'], method: "DELETE") {
                writer << g.link(class: "btn btn-default btn-sm", action: "edit", resource: a, "Editar")
                writer << "&nbsp;"
                writer << g.actionSubmit(class: "btn btn-danger btn-sm", action: "delete", value: "Eliminar", onclick: "return confirm('¿Eliminar curso?');")
            }
            writer << '  </td>'
            writer << '</tr>'
        }
        writer << '</tbody>'
        writer << '</table>'
        writer << '<div class="row">'
        writer << '<div class="col-sm-6">'
 
        def offset = params.offset as Integer ?: 0
 
        def page_start = offset + 1
        def page_end = Math.min(offset+10, count)
 
        writer << "<div aria-live=\"polite\" role=\"status\" class=\"dataTables_info\">Mostrando ${page_start} a ${page_end} de ${pluralize(count: count, singular: "registro")}</div>"
        writer << '</div>'
        writer << '<div class="col-sm-6 dataTables_paginate">'
        writer << g.paginate(total: count)
        writer << '</div>'
        writer << '</div>'
        writer << '</div>'
    }
 
    def pluralize = { attrs, body ->
        def plural = attrs['plural'] ?: attrs['singular'] + "s"
        out << attrs['count'] + " " + ( attrs['count'] > 1 ? plural : attrs['singular'] )
    }
}

PD : Seguramente exista forma de sacar el nombre de columna desde el nombre del atributo del modelo, pero todavía no lo encontré.

Para usarlo simplemente basta con agregar en nuestro template :

<div class="row">
    <div class="col-lg-12">
        <g:table_for list="${list}" count="${employeeInstanceCount}" labels="Legajo,Nombre,Apellido" columns="fileNumber,firstName,lastName" />
    </div>
 
    <!-- /.col-lg-12 -->
</div>

10 hechos sobre los programadores que deberías conocer

El otro día leyendo el artículo “Ten hilarious programmers facts that only programmers can understand” me dí cuenta que muchas de las respuestas que doy siempre están reflejadas en estos simples hechos, por lo que decidí hacer una traducción libre y criolla del asunto. A continuación, mis “10 hechos sobre los programadores que deberías conocer

Hecho #0

Empezamos a contar desde 0, no desde 1.

Desde nuestros primeros pasos nos encontramos que todo lo que tiene “posiciones” arranca de 0, es por eso que se nos hizo costumbre.

Hecho #1

Los programas que usas, son como una cámara de una rueda de tu bicicleta.

Anda, se infla, rueda. Pero puede estar llena de parches, improvisaciones y manotazos de ahogado que tuvimos que agregar cinco minutos antes de la entrega para que parezca que ande como se había acordado. No te preocupes, en la próxima versión cambiamos la cámara por una nueva (y seguramente le meteremos más parches aún :D)

Hecho #2

El 35% de nuestro tiempo de trabajo se utiliza en pensar qué cosas podrías, vos, el usuario, hacer mal al usar nuestro programa.

Y tratar de evitarlo. Si tan solo pensaran como programadores …

Hecho #3

Irse a dormir con un problema en la cabeza, realmente puede derivar en una solución.

Nos ha pasado a casi todos, despertar de madrugada y sentarse en la PC a terminar de programar eso que no nos salió durante todo el día. Luego a seguir durmiendo :)

Hecho #4

Programar es Pensar, no Escribir.

Si me ves mirando el monitor sin hacer nada, o mirando Youtube, seguramente estoy pensando de qué disfrazarme para atender a un pedido ridículo :D.

Hecho #5

Un programador NO es un reparador de PC.

Que entienda que significa RAM, ROM, CPU no significa que sepa que le pasa al puto Windows! (más aún cuando hace años que uso Linux :)

Hecho #6

Somos noctámbulos. Supérenlo.

Nos gusta la noche, pero no en el sentido que están pensando, bueno, un poco ;). La noche trae paz, la paz trae momentos de reflexión, cuando pensamos, bueno, ya vimos en el 5to hecho, programamos (Nota del redactor, recordar que contamos desde cero, el 5to hecho, es el Hecho #4 :D!).

Hecho #7

Un padre puede matar a su hijo. Es legal. punto.

En una computadora, un programa (llamémoslo proceso) puede tener varios hijos (o hilos, o threads en inglés). Cuando el padre decide que no se va a seguir ejecutando, puede matar a sus hijos y terminar con toda ejecución.

Hecho #8

Mientras que a Uds no les interese la informática, no esperen que a nosotros nos interese el mundo real.

No me interesa la política, a quién vas a votar, quién va al bailando o quien le hizo un hijo a no se quién que es de no se donde!, salvo que quieras hablar de los beneficios de la computación cuántica, algoritmos evolutivos o de búsqueda de textos.

Hecho #9

Programar no es tan fácil como parece.

Pero dedicándole tiempo, todos somos capaces de hacerlo. Para más detalles los invito a ver code.org.

 

Volviendo a PHP

Hace fácil 5 años que no hago nada serio en PHP, empezando desde cero. Mayormente me ha tocado retocar cosas andando o parchar bugs boludos. Sin embargo el otro día tuve que hacer un demo chiquitito en PHP. El principal limitante era el tiempo, por lo que tenía que hacerlo rápido.

Al principio intenté el viejo HTML+PHP all-in-one con ifs $_GET, $_POST, etc. Una locura, no podía avanzar. Mi mente me decía “esto va en el controlador, esto en el view” :D. Borre todo y empecé de nuevo. Pensé dos segundos y dije “tiene que haber algo”. No, CakePHP no es ese “algo”. Era mucha config al pedo, bloated y cada vez que tuve en mi poder un site de Cake para poner en producción terminé a los cabezazos.

Googleando me topé con SlimFramework un micro framework para hacer apps (el Sinatra de CakePHP 😀 ?) y la verdad que dije “ah, mirá, en php también se puede programar como al gente”. No tiene ORM, solo un modo fácil de declarar rutas, con arguments y verbos REST (post, delete, put, etc) y un body para ejecutar la lógica.

Los templates son modulares, podemos llamar a otro .php que genera HTML o usar HAML, Smarty (dios que alguien de de baja este proyecto :P) o armar el propio. Un hola mundo :

<?php
  $app = new \Slim\Slim();
  $app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
  });
  $app->run();
?>

Simple, no?. La documentación está bastante completa y no tuve mayores drama en sacar andando el demo. Lo único que no me gustaba era que los “views” no podían tener un main template (al menos en la version básica de usar php como template language). Es decir, tenía que poner <? require “header.php”; ?> <? require “footer.php” ?> por todos lados, por lo que decidí implementar me propia “view” :

<?php
namespace Slim\Extras\Views;
class ViewWithLayout extends \Slim\View
{
    public function render($template)
    {
        $this->setTemplate($template);
        extract($this->data);
        ob_start();
 
        $this->setTemplate("template_pre.php");
        require $this->templatePath;
 
        $this->setTemplate($template);
        require $this->templatePath;
 
        $this->setTemplate("template_post.php");
        require $this->templatePath;
 
        return ob_get_clean();
    }
}
?>

Simplemente hago los 3 render y así tengo view con layout a’la Rails :P.

El segundo punto a resolver era el acceso a la base de datos. Hoy en día laburar con mysql_query, mysql_fetch es inpensable, al menos para prototipado ;). Otra vez, googleando me topé con Idiorm, “A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5″, o sea, un Arel para PHP, super limiteado pero suficiente por el momento.

Este ORM nos pemite encadenar condiciones y luego obtener objetos, manipularlos y guardarlos de nuevo :

$user = ORM::for_table('user')
    ->where_equal('username', 'j4mie')
    ->find_one();
 
$user->first_name = 'Jamie';
$user->save();

Si queremos más abstracción todavía el mismo autor implementó Paris, una implementación de ActiveRecord sobre Idiorm. Yo en mi caso no la usé así que lo dejo a criterio del lector probarla.

A modo de ilustrar como quedó mi demo, les comparti el método del index, que obtiene la lista de registros y hace un render del view acorde :

$app->get('/', function () use ($app) {
  $contacts = ORM::for_table('contact')->find_many();
  $app->render('home.php', array('contacts' => $contacts));
});

Y la vista (recortada) :

<h1>Contactos</h1>
 
<table class="table table-striped table-hover">
  <tr>
    ...
  </tr>
 
  <? foreach($this->data['contacts'] as $contact) { ?>
  <tr>
    <td><?= $contact->nombre ?></td>
    ...
    <td>
      <form action="/contacts/<?= $contact->id ?>" method="POST" class="form-inline">
        <a href="/contacts/<?= $contact->id ?>/edit" class="btn">Modificar</a>
        <input type="hidden" name="_METHOD" value="DELETE"/>
        <input type="submit" class="btn btn-danger" value="Eliminar" />
      </form>
    </td>
  </tr>
  <? } ?>
</table>
 
<a href="/contacts/new" class="btn btn-primary">Agregar</a>

Mejorando el “Doble panel” en Android

Siguendo con la serie de casos en mejorar las interfaces en Android, esta vez le toca al doble panel que solemos ver en las aplicaciones cuando las usamos en una tablet. No voy a entrar en detalle de cómo implementar un doble panel con Fragments ya que en la web hay más que suficiente información para hacerlo.

De lo que si vamos a hablar es de cómo mejorar el aspecto y darle al usuario una mejor experiencia. Para lograr esto lo que vamos a hacer es copiar lo que hacen Google con sus aplicaciones, com Gmail o la misma aplicación de Contactos que viene en IceCream Sandwich y que se ve algo así :

ics_effects

El simple agregado de un gradiente a modo de “dropshadow” entre los paneles y el indicador de selección cambian radicalmente la experiencia de usuario, por más simple que parezca el cambio, pueden verlo al final del post donde dejo el antes y el después. Ahora bien, lo simple no siempre es fácil :), por lo que para lograr este efecto tuve que trabajar un poco. Empecemos por la sombra.

Existen varias formas de lograrla, desde las más simples como usar un FrameLayour con un bitmap como overlay hasta backgrounds complicados. Sin embargo se pueden obtener efectos raros en versiones nuevas de Android, ya que la ActionBar también tiene un dropshadow. Estas técnicas además pueden hacer complicado la segunda parte que es mostrar el selector.

La forma más prolija que encontré es meterse en el ciclo de render y aprovechar el método “dispatchDraw(Canvas canvas)”. Este método es el encargado de dibujar un View, por lo que si lo sobreescribimos podemos dibujar cosas antes o despues del render original (que lo llamaremos con “super.dispatchDraw(canvas)”). Como es un método protegido, lo que debemos hacer entonces es nuestra propia View, en este caso basta con hacer nuestra propia ListView :

;
public class ShadowListView extends ListView {
  public ShadowListView(Context context) {
    super(context);
  }
 
  private void prepareShadow() {
    // Load drawables and initialize stuff
  }
 
  @Override
  protected void dispatchDraw(Canvas canvas) {
    // Draw the children
    super.dispatchDraw(canvas);
 
    if (!initializes)
      prepareShadow();
 
    drawShadow(canvas);
  }
 
  private void drawShadow(Canvas canvas) {
    canvas.save();
    canvas.translate(getWidth() - 20, 0);
    drawable.setBounds(0, 0, 20, getHeight());
    drawable.draw(canvas);
    canvas.restore();
  }
}

Lo siguientes es utilizarla en nuestro layout directamente en lugar de usar <ListView> usar <ShadowListView>. Con esto lo que hacemos es dibujar en el borde derecho, desde arriba hasta abajo con un ancho de “20” la sombra, haciendo parecer que el panel derecho está por arriba y que le hace “sombra” a la lista, cool :), no?.

Lo siguiente a lograr es dibujar el triángulo y queremos tener en cuanta varias cosas : debe estar dibujado por encima de la sombra (así el efecto queda bien) y debe coincidir en la posición del elemento seleccionado en la lista, si hay alguno.

El primer problema con el que me encontré es que quise utilizar “getSelectedView” para obtener la View seleccionada. Lamentablemente no pude entender por qué no funciona como parecería indicar el nombre :D. Por lo que fui por un camino diferente : como ya tengo mi clase de ListView, puedo sobreescribir otros métodos, como por ejemplo “performItemClick” para saber cuando un item fue “clickeado” (ya sea via touch o via teclado). Cuando eso pasa me guardo esa posición para futuro :

@Override
public boolean performItemClick(View view, int position, long id) {
  selectedPosition = position;
  return super.performItemClick(view, position, id);
}

Y luego puedo definir el método que dibuja la flecha :

private void drawArrow(Canvas canvas) {
  View selectedView = getChildAt(selectedPosition - getFirstVisiblePosition());
  if (selectedView != null) {
    selectedView.getDrawingRect(rect);
 
    offsetDescendantRectToMyCoords(selectedView, rect);
 
    int h = selectedView.getHeight();
 
    canvas.save();
    canvas.translate(getWidth() - h, rect.top);
    arrow.setBounds(0, 0, h, h);
    arrow.draw(canvas);
    canvas.restore();
  }
}

Lo primero que hago es ver que vista está seleccionada. Para eso, como ListView puede reutilizar un View para optimizar el render, debo tener en cuenta el View que se está mostrando. Si tengo una vista obtengo su recta de de dibujado. Esta recta está en coordenadas de la vista, por lo que tengo que trasladar esas coordenadas a mi espacio de coordenadas, y eso android me permite hacerlo fácil llamando a “offsetDescendantRectToMyCoords”.

Por último dibujo la flecha que es un PNG cuadrado que tiene la flecha con su propio dropshadow. El paso final es llamar a drawArrow despues de drawShadow en el dispatchDraw, de manera que si dibujo la flecha, esta se dibuja por arriba.

El resultado final, antes y después :

table_sin_efectos tablet_conf_efectos

Mejorando las UI en Android

En los últimos días me puse a ver varios de los videos de Google I/O del año pasado referidos al diseño de interfaces y diseño gráfico de android. La verdad es que con muy poco se puede cambiar el aspecto radicalmente, y la experiencia termina siendo muy superior (y si tenemos suerte podemos tener una mayor user base lo que significa más ingresos :D).

Para probar diferentes consejos decidí mejorar la interfaz de mi lector del diario Río Negro, el cual era bastante feo y se puede ver al final del post lado a lado con la imagen luego de mejorar el diseño.
La idea era resaltar un poco más los títulos y acomodar el layout. Lo primero que hice fue agregar “paddings”, de manera de descomprimir los diferentes elementos de la lista. Luego de un par de pruebas y error el valor de “10dp” fue el ganador.Una vez hecho eso tire los LinearLayout que usaba y converti a RelativeLayout lo que además hizo que quede más natural al rotar o usar la aplicación en otra resolución, ya que los “floats” se adapan mucho mejor una vez que uno entiende bien que opción va en cada caso :D.Lo último fue mejorar el render de la imagen. Como primer paso decidí cropear las imágenes a un tamaño único, para que tenga todo más sentido : da la sensación de uniformidad y distrae mucho menos si queremos ignorar las imágenes.El último punto fue hacer mas “fancy” la imagen poniéndole bordes redondeados. Para eso hice un custom View que cache la imagen usando una versión modificada de esta técnica.La imagen original y el resultado final a continuación :

Todavía no está subida a Google Play esta modificación pero lo estará en los próximos días. Mientras si pueden bajar la versión actual desde el market ;).


Get it on Google Play