Geek & Roll » programacion

Iconos de Eclipse y Netbeans para Docky

Netbeans

Ya que andamos en eso de hacer que Eclipse se vea bien en GNU/Linux, ahora le toca el turno a los iconos tanto de Eclipse como de Netbeans.

Normalmente tengo los dos IDEs instalados, y sus iconos de aplicación se ven pésimo a resoluciones altas como las que maneja Docky. Afortunadamente la solución es simple, reemplazarlos por unos de mayor resolución.

Para Eclipse, hay un excelente icono en formato SVG que sigue las especificaciones del proyecto Tango. Este icono lo encuentran en Gnome-Look.org.

Para Netbeans es más interesante, se puede usar el mismo icono que se usa en OSX. Resulta que cuando instalas Netbeans en OSX el icono se ve muy bien, pero usuarios de otros sistemas operativos nos tenemos que conformar con una versión de menor resolución. Afortunadamente podemos tomar el icono de OSX en formato icns y usar icns2png (disponible en tu distro favorita) para convertirlo a PNG. Ahora que si se quieren ahorrar esos pasos, solo click derecho y guardar como. Gracias este blog por el tip.

Cómo hacer que Eclipse se vea más bonito en Ubuntu

Cesar July 9th, 2010 aplicaciones, linux, programacion, tips 1 comentario

Tradicionalmente Ubuntu no le ha dado la prioridad que debería a eclipse, mi IDE de preferencia a la hora de programar. La diferencia es más notable si, como yo, constantemente te encuentras cambiando entre sistemas operativos.

Eclipse IDE en Windows 7
eclipse_windows

Eclipse IDE en Ubuntu
eclipse_ubuntuhuge

Notese como en Ubunto en el mismo espacio se muestra mucho menos área útil, ya que los botones y la interfaz en general ocupan más espacio del que deberían. El problema real está en GTK y sus limitantes, no en eclipse o SWT (el toolkit gráfico utilizado por eclipse). Para arreglarlo, al lanzar eclipse es posible especificarle a gnome que use un tema diferente, modificado especialmente para que eclipse use mejor el espacio disponible.

Lo primero es asegurarnos que las fuentes liberation-fonts se encuentran instaladas (en mi caso ya estaban instaladas):

sudo aptitude install ttf-liberation

Después descargamos este zip y lo descomprimimos en el directorio raíz donde se encuentra eclipse. El zip contiene dos archivos:

  • gtkrc-sar es el archivo de propiedades para el tema de GTK que vamos a utilizar específicamente con eclipse.
  • ec es un shell script muy sencillo que se encarga de crear la variable de entorno para el tema de eclipse y finalmente lanzarlo

Ahora hacemos el shell script ejecutable:

chmod 755 ec

Y finalmente ejecutamos ec en vez de eclipse:

./ec

Eclipse en Ubuntu después de las modificaciones
eclipse_ubuntubetter

El resultado es un eclipse con un espacios mucho más normales en las tabs y los iconos de la barra de herramientas, aprovechando mejor el espacio y mejorando la experiencia de usar este IDE en Ubuntu.

Si se quiere modificar la entrada del menú para lanzar eclipse, lo cual a su vez tiene el efecto de permitirnos de lanzar eclipse con el nuevo tema desde Gnome Do, o Docky por ejemplo, lo que se tiene hacer es editar el script ec y dejarlo de la siguiente manera:

#!/bin/sh
GTK2_RC_FILES=/home/usuario/eclipse/gtkrc-sar /home/usuario/eclipse/eclipse

Lo que estamos haciendo es utilizar rutas absolutas en vez de relativas. En este caso estoy suponiendo que eclipse se encuentra instalado en /home/usuario/eclipse/ hay que editar el archivo para que concuerde con el sistema de cada quien.

Con esta modificación al script, en el menú principal podemos agregar una entrada para eclipse (si no es que ya tenemos una) poniendo como comando lo siguiente:

sh /home/usuario/eclipse/ec

Ahora sí, tanto Gnome Do como un lanzador de Docky creado a partir del menú principal deberán de iniciar eclipse con el nuevo tema.

Gracias a Sarath Chandra por el post original.

Cargando recursos binarios desde JavaScript

Desde que Google mostró al mundo su port de Quake 2 a JavaScript usando GWT, no me podía sacar una pregunta de la cabeza: ¿Cómo hacen para cargar los recursos (como por ejemplo los modelos de los jugadores, o los niveles) desde el servidor al cliente, para su uso por el motor del juego en JavaScrip? ¿Es posible obtener archivos binarios del servidor, guardarlos en el sistema de archivos local, y ejecutarlos finalmente por una aplicación en JavaScript? La respuesta obviamente era sí, pero no sabía cómo.

Pero una consulta al código fuente del proyecto quake2-gwt aclaró las dudas. El secreto es sobreescribir un tipo MIME, lo que le permite al objeto XMLHttpRequest recibir respuestas binarias en vez de texto, para lo cual es más comúnmente utilizado.

function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType('text/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';
  return req.responseText;
}

En GWT necesitamos hacer uso de una funcion JavaScript nativa (JSNI) para lograr lo mismo:

private native void overrideMimeType(XMLHttpRequest req, String mimeType) /*-{
    req.overrideMimeType(mimeType);
}-*/;

Y finalmente crear un objeto XMLHttpRequest, hacer la petición y procesar los datos binarios en el cliente:

XMLHttpRequest req = XMLHttpRequest.create();
overrideMimeType(req, "text/plain; charset=x-user-defined");

req.setOnReadyStateChange(new ReadyStateChangeHandler(){
    //procesar la respuesta binaria
});

req.open("GET", "archivo.binario");
req.send();

Para convertir la respuesta en un InputStream, en este caso utilizando GWT y clases de emulación io y nio que tomé prestadas del proyecto quake2-gwt:

response = xhr.getResponseText();
byte[] responseBytes = new byte[sbb.stringToByteBuffer(response).remaining()];
InputStream inputStream = new ByteArrayInputStream(responseBytes);

En este punto pueden usar el InputStream como lo harían en un programa Java tradicional, por ejemplo si cargaron los bytes de una imagen, pueden aplicarle filtros a-la-photoshop, en tiempo real, en el cliente, sin necesidad de procesamiento en el servidor mas allá de descargar la imagen.

El CPU del Nintendo Entertainment System

Soy un geek de la emulación desde que supe se podía emular el SNES en mi modesta Pentium 2. Desde entonces he utilizado varios emuladores y hasta programado un simulador por mi cuenta (para el microcontrolador 68HC11 de Motorola).

Lo que me lleva a este post: el NES. El NES es interesante ya que es el sistema más emulado que hay, aún así crear un emulador del NES no es tarea fácil. Además las computadoras (y otros gadgets) de hoy tienen lo suficiente para emularlo sin penalización en la velocidad. También, su CPU (MOS 6502 ligeramente modificado) de 8 bits fue el CPU elegido para la mítica Commodore 64, el Atari 2600, Terminator y Bender. Leer el resto de este post.

Cuando Hibernate cascade te juega bromas

Cesar March 23rd, 2010 programacion 4 comentarios

Ah, Hibernate. ¿Que podemos decir? Te atrae con promesas de cerrar la brecha entre lo relacional y el mundo de los objetos, pero luego te deja a que trates de entender por qué demonios no funciona como tu lo esperas. En cierto modo es como C++ que evita que te dispares en el pie, pero cuando lo haces te vuelas la pierna completa.

De lo que quiero hablar en este post es de las operaciones en cascada. Hibernate te permite configurar operaciones en cascada, de tal manera que si así tu lo deseas, cuando creas un objeto que a su vez tiene relación con otro objeto, ambos pueden ser guardados con una sola operación. Por ejemplo:

public class Member(){
    private ExpertiseArea expertiseArea;
}

En este caso, Member tiene una relación uno a uno con ExpertiseArea, y sería deseable que al guardar Member también se guardara ExpertiseArea. Para lograrlo, le decimos a Hibernate cuando debe realizar las operaciones en cascada:

<one-to-one name="expertiseArea"
    class="ExpertiseArea"
    cascade="persist,save-update" />

Con lo anterior le decimos a Hibernate “Cuando ejecutes las operaciones de crear (persist) y de actualizar (save-update) en Member, también hazlo para ExpertiseArea”.

Creamos una prueba para verificar que lo anterior funciona como debería:

	@Test
	public void testMemberRetrieval(){
		Member m = buildMember();
		md.makePersistent(m);
		md.flush();

		Member retrievedMember = md.findById(m.getId(), false);
		assertNotNull("Entity should not be null",retrievedMember);
		Member retrievedMember = md.findById(m.getId(), true);
		assertNotNull("Entity should not be null",retrievedMember);
		assertNotNull("ExpertiseArea should be persistent",
                    retrievedMember.getExpertiseArea());
	}

	private Member buildMember(){
		ExpertiseArea ea = new ExpertiseArea("Singer");
		Member m = new Member("Britney Spears", ea, "Queen of Pop");
		return m;
	}

¿Qué estamos haciendo aquí? Creamos un nuevo Member, le asignamos un ExpertiseArea, y lo guardamos todo en una sola instrucción makePersistent. Como tenemos configuradas las operaciones en cascada, esperamos que se guarden en la base de datos tanto la instancia de Member, como la de ExpertiseArea. Efectivamente, la prueba pasa y podemos revisar en los logs lo que Hibernate hace tras bambalinas:

Hibernate: insert into Member (VERSION, name, tagLine, id) values (?, ?, ?, ?)
Hibernate: insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)

Pass

No esperaba menos. Leer el resto de este post.

Formas dinámicas con GWT

Cesar March 8th, 2010 javascript, programacion Haz un comentario

Mi relación con GWT es una de amor – odio. Por un lado, tenemos a un toolkit que me permite utilizar mis conocimientos de Java para crear aplicaciones Web dinámicas, aplicando mejores prácticas en la arquitectura del código del cliente, mejorando el tiempo de respuesta gracias al uso de técnicas como agrupar imágenes para usarlas en el cliente con una sola descarga y código segmentado.

Por el otro lado, algunas tareas se vuelven más complicadas. Estoy convencido que las herramientas como GWT son el futuro, más no me queda claro si la actual implementación sea adecuada para toda la programación del lado del cliente. Además la curva de aprendizaje es más inclinada de lo que debería de ser; existen muchos gotchas todavía que hacen que el programador tenga que estar consciente de que esto es Web y no una aplicación regular.

En esta ocasión el requerimiento era el siguiente: tengo una aplicación que hace uso extensivo de formas para captura de datos. Necesito una manera de declarar dichas formas y que la aplicación automáticamente las ordene en diferentes tabs cuando estas crezcan demasiado. Las formas generadas deben poder ser insertadas en cualquier sitio, además de poder aplicarles estilos con CSS.

Como dije anteriormente, lo que me gusta de GWT es que puedo generar la solución a mis requerimientos de la misma manera en la que estoy acostumbrado a hacerlo. En este caso, creo una clase DynaForm que representa mi forma dinámica:

/*Represents the client side DynaForm*/
public class DynaForm {
	private String formSource;
	private ArrayList<String> panelTitles;
	private ArrayList<HTML> panels;
	private Node root;

	public DynaForm(String formSource){
		setFormSource(formSource);
	}

Ahora viene algo interesante. El método setFormSource(). Es aquí donde vamos a parsear el código de la forma para extraer los valores que nos interesan, y descomponerla en sus partes para después volverla a construir pero con las propiedades que nos interesan, en este caso los tabs.

public void setFormSource(String formSource){
	try{
		Document d = XMLParser.parse(formSource);
		root = d.getFirstChild();
		XMLParser.removeWhitespace(root);
		setPanels(root, formSource);
	}catch(Exception e){
		System.out.println(e.getMessage());
		e.printStackTrace();
	}

	this.formSource = formSource;
}

Como vemos, parseamos la fuente y de esa manera nos aseguramos que es código válido. Aquí podríamos aplicar otras reglas, como por ejemplo eliminar cualquier tag o propiedad maliciosa como una llamada a JavaScript. El método setPanels() es el que se encarga de construir los páneles necesarios.

/*
 * This method creates an array of HTML panels. Both panels and panelTitles must
 * contain the same number of elements. In case a <span> element doesn't provide
 * a 'name' attribute, it is replaced by an empty string.
 *
 * The number of HTML panels created is = # of <span> elements. All elements inside <form>
 * without a <span> are ignored.
 * */
private void setPanels(Node root, String formSource){
	panelTitles = new ArrayList<String>();
	panels = new ArrayList<HTML>();
	NamedNodeMap attribs;

	NodeList allElements = root.getChildNodes();

	/*
	 * Start looping all elements, and create new HTML panels as needed
	 * */
	for(int i=0; i<allElements.getLength(); i++){
		Node n = allElements.item(i);
		//if it's a span, create a new empty HTML panel and set its name.
		if(n.getNodeName().equals("span")){
			//Set the panel title entry
			attribs = n.getAttributes();
			panelTitles.add(getAttribValue(attribs, "name"));
			panels.add(new HTML());
		}else{
			try{
				//get the last panel
				HTML panel = panels.get(panels.size()-1);
				//add the node source to the panel HTML
				panel.setHTML(panel.getHTML() + n.toString());
			}catch(Exception e){
				System.out.println(e.getMessage());
			}
		}
	}
}

El método setPanels() toma todos los elementos de la forma y los analiza de la siguiente manera: Si es un elemento span, quiere decir que es el inicio de un nuevo tab. Toma la propiedad name del span y lo convierte en el título de la nueva tab. Si el elemento no es un span, entonces lo agrega a la lista de elementos que forman el tab actual.

Después, en el punto de entrada de la aplicación:

TabPanel tabPanel = new TabPanel();
dynaForm = new DynaForm(formSource);

//All each HTML panel to the tabPanel
for(int i=0; i<dynaForm.getPanels().size(); i++){
	quirksTabPanel.add(dynaForm.getPanels().get(i), dynaForm.getPanelTitles().get(i));
}

RootPanel.get("dynaForm").add(tabPanel);

Utilizando el siguiente código HTML como fuente de la forma:

<form method='POST' action='contactengine.php' name='contact'>
	<span name='Datos' />
	<label for='Nombre'>Nombre:</label>
    	<input type='text' name='Nombre' id='Nombre' />"

    	<label for='Correo'>Correo:</label>
    	<input type='text' name='Correo' id='Correo' />

	<label for='Mensaje'>Mensaje:</label>
	<textarea name='Mensaje' rows='20' cols='20' id='Mensaje'></textarea>

	<span name='Enviar' />
	<input type='submit' name='submit' value='Enviar' class='submit-button' />
</form>

El resultado es:
DynaForm

Claro que se deben usar estilos para hacer que la forma se vea más presentable. También creo que se puede lograr exactamente el mismo resultado utilizando una combinación de jQuery, selectores CSS y jQuery UI.

Controles custom en BlackBerry

Cesar February 12th, 2010 gadgets, programacion Haz un comentario

Crear un control custom para una aplicación de BlackBerry es muy similar a crearlo para una aplicación Java con Swing. La arquitectura del toolkit de Swing es una de las cosas que más me gustan de Java, haciendo muy claro el cómo reemplazar algo por una implementación propia.

En este caso, mi requerimiento era tener una gran “palomita” o checkmark cuando cierta variable era verdadera, y una gran cruz o tache cuando la misma variable era falsa. En otras palabras, un checkbox pero con otra interfaz, más bonita (aunque bonita es una variable que depende del observador, y como eso no lo puedo controlar…)

Como muchas cosas en Java, todo comienza extendiendo una clase, en este caso la clase CheckboxField

public class CustomCheckBox extends CheckboxField

Este checkbox, contrario al checkbox normal, no va a tener un texto asociado, solo la parte gráfica. Así que sobreescribimos el constructor:

public CustomCheckBox(){
	super();
}

public CustomCheckBox(boolean check){
	super("",check);
}

Similar a Swing, hay que sobreescribir el método paint para ahí pintar lo que nosotros queremos. Cuando el checkbox esta check dibujamos la palomita, y cuando esta uncheck, dibujamos la cruz:

public void paint(Graphics g){
	if(getChecked()){
		drawCheck(g);
	}else{
		drawCross(g);
	}
}

Esos dos métodos drawCheck y drawCross son los responsables de pintar el control. El método getChecked heredado de CheckboxField nos dice si el control esta checked o unchecked.

Primero veamos drawCheck

private void drawCheck(Graphics g){
	int left = getBorderLeft();
	int top = getBorderTop();

	int x0 = left, x1 = x0, x2 = x1+20, x3 = x2+50, x4 = x3,
		x5 = x4-50, x6 = x5-20;
	int y0 = top, y1 = y0+30, y2 = y1+15, y3 = y2-45,
		y4 = y3+25, y5 = y4+45, y6 = y5-15;

	g.setColor(0x5B9058);
	g.setBackgroundColor(isFocus() ? 0xEEEEEE : 0xFFFFFF);
	drawBackground(g);
	g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, true);

	int[] xPts = {x1, x2, x3, x4, x5, x6};
	int[] yPts = {y1, y2, y3, y4, y5, y6};
	g.drawFilledPath(xPts, yPts, null, null);

	g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, false);
}

De antemano perdonen ustedes el uso de números mágicos. Lo interesante es el método drawFilledPath, que toma dos arreglos con coordenadas XY y dibuja, como su nombre lo indica, un polígono. Es algo como dibujar con Logo, uno de mis primeros lenguajes de programación muy similar a Lisp. Primero preparamos las coordenadas, después verificamos si nuestro control tiene el foco para dibujarle un fondo distintivo.

El otro método, drawCross, es muy parecido solo que las coordenadas cambian para formar una cruz:

private void drawCross(Graphics g){
	int left = 0;
	int top = 10;

	int x0 = left, x1=x0, x2=x1+25, x3=x2+10, x4=x3+10, x5=x4+25,
		x6=x4+5, x7=x5, x8=x4, x9=x3, x10=x2, x11=x1, x12=x2-5;
	int y0 = top, y1=y0,y2=y1, y3=y2+15, y4=y2, y5=y4, y6=y5+25,
		y7=y6+25, y8=y7, y9=y8-15, y10=y8, y11=y10, y12=y6;

	g.setColor(0xB54E41);
	g.setBackgroundColor(isFocus() ? 0xEEEEEE : 0xFFFFFF);
	drawBackground(g);
	g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, true);

	int[] xPts = {x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12};
	int[] yPts = {y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12};
	g.drawFilledPath(xPts, yPts, null, null);

	g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, false);
}

Y finalmente la clase completa

public class CustomCheckBox extends CheckboxField{
	public CustomCheckBox(){
		super();
	}

	public CustomCheckBox(boolean check){
		super("",check);
	}

	public void paint(Graphics g){
		if(getChecked()){
			drawCheck(g);
		}else{
			drawCross(g);
		}
	}

	protected void drawFocus(Graphics g, boolean focus){
		//do nothing
	}

	protected void onFocus(int direction){
		super.onFocus(direction);
		invalidate();
	}

	protected void onUnfocus(){
		super.onUnfocus();
		invalidate();
	}

	protected void layout(int width, int height){
		width = 71;
		height = 71;
		setExtent(width, height);
	}

	private void drawCheck(Graphics g){
		int left = getBorderLeft();
		int top = getBorderTop();

		int x0 = left, x1 = x0, x2 = x1+20, x3 = x2+50, x4 = x3,
			x5 = x4-50, x6 = x5-20;
		int y0 = top, y1 = y0+30, y2 = y1+15, y3 = y2-45,
			y4 = y3+25, y5 = y4+45, y6 = y5-15;

		g.setColor(0x5B9058);
		g.setBackgroundColor(isFocus() ? 0xEEEEEE : 0xFFFFFF);
		drawBackground(g);
		g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, true);

		int[] xPts = {x1, x2, x3, x4, x5, x6};
		int[] yPts = {y1, y2, y3, y4, y5, y6};
		g.drawFilledPath(xPts, yPts, null, null);

		g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, false);
	}

	private void drawCross(Graphics g){
		int left = 0;
		int top = 10;

		int x0 = left, x1=x0, x2=x1+25, x3=x2+10, x4=x3+10, x5=x4+25,
			x6=x4+5, x7=x5, x8=x4, x9=x3, x10=x2, x11=x1, x12=x2-5;
		int y0 = top, y1=y0,y2=y1, y3=y2+15, y4=y2, y5=y4, y6=y5+25,
			y7=y6+25, y8=y7, y9=y8-15, y10=y8, y11=y10, y12=y6;

		g.setColor(0xB54E41);
		g.setBackgroundColor(isFocus() ? 0xEEEEEE : 0xFFFFFF);
		drawBackground(g);
		g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, true);

		int[] xPts = {x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12};
		int[] yPts = {y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12};
		g.drawFilledPath(xPts, yPts, null, null);

		g.setDrawingStyle(Graphics.DRAWSTYLE_AAPOLYGONS, false);
	}

	private void drawBackground(Graphics g){
		g.clear();
	}

	public boolean isFocusable(){
		return true;
	}
}

El resultado:
customcheckbox

Look ma! custom controls with no images!

Desarrollo de software en un mundo post-agil

Cesar February 9th, 2010 opinion, programacion, tips 1 comentario

Del artículo:

Un proceso es un instrumento de autoridad. Un proceso es una proclamación de qué hacer, cómo debemos de hacerlo y en qué orden. Un proceso es conservador, jerárquico y formal. Un proceso mantiene el status quo. Es la administración imponiendo orden y control desde arriba. Si los procesos fueran un partido político, sería de derecha.

Es fácil ser seducido por las bondades de la metodología ágil. Promesas de equipos auto suficientes, entregas frecuentes y listas para producción, Yippi-kay-ay. Demasiado proceso y en vez de avanzar nos quedamos llenando reportes y formas; muy poco proceso y todo se vuelve un manojo de actividad descontrolada y poco empuje (y entrega).

El artículo discute la etiqueta “ágil” que tanto se usa en administración de proyectos de software hoy en día. Por ahí leí que “scrum” (intercambiado por ágil en esta ocasión) no es una bala de plata para tener proyectos exitosos. Existen factores como la experiencia y motivación del equipo, así como también la naturaleza del proyecto, por lo que una aproximación orientada a personas tiene más sentido que orientado a procesos, y viceversa.

Lean el artículo fuente (en Inglés), y los invito a dejar sus comentarios sobre el tema, experiencias personales, etc.

Quitándole el ruido a twitter moviendo los hashtags

Últimamente he estado pensando en el principal problema de twitter (aparte de la estabilidad): el ruido. Es dificil separar la información “real” del ruido que llegamos a tener en nuestra timeline de un solo vistazo. Esta es una propuesta pequeñita de mejora:

Una de las principales fuentes de ruido son los hashtags (palabras “marcadas” que se utilizan para ayudar en la búsqueda). Hay gente que termina dedicando incluso más caracteres a los hashtags que al post en si.

Mi propuesta de 15 minutos no implica cambiar el funcionamiento de la publicación de hashtags, sino separarlos (por medio de programación) del contenido del post. Se entiende mejor con un ejemplo (click en las imágenes para ver a tamaño completo):

Este es un tweet normal:

Y este es uno con los hashtags separados del contenido del post.

A mi me parece que la diferencia, aunque en un sólo tweet se ve pequeña, sería de gran ayuda al leer Twitter por medio del sitio web.

No pretendo que esto llegue hasta twitter (ni que fuera la gran cosa), pero si a alguien le interesa, sería más o menos sencillo de implementar en un userscript de greasemonkey.

Antes de que digan cualquier cosa, si, ya estamos pensando en que cuando aparezca mi sección diga “tweet & roll” en el header.

Uso de enum en Grails

Cesar May 18th, 2009 programacion 3 comentarios

O “El diablo está en los detalles”.

Recientemente he estado usando Grails como framework para desarrollo Web con Java y, francamente, estoy bastante sorprendido. Realmente es un boost a la productividad, tanto que una vez que terminé mi diagrama UML del dominio de mi primera aplicación en Grails y comencé a programar las clases de dominio, no podía creer que esas mismas clases fueran las que se usarían para persistir en la base de datos. No hay que crear archivos hbm.xml ni configurar Spring. Sólo crear la clase del dominio, y ya.

Después de varias sesiones nocturnas de un par de horas leyendo sobre Grails, y viendo el código que genera el scaffolder incluído, empecé a crear mis controladores, vistas, refactorizando el modelo. Todo iba bien hasta que me topé con la necesidad de usar datos enumerados (enums).

La solución en sí es sencilla. El problema es que no existe documentación suficiente para utilizar enums con Grails. Después de consultar distintas fuentes e invertir buena parte de mi Domingo en dar con la solución, aquí les presento el código con el que pueden lograr utilizar enums en Grails. Leer el resto de este post.

Posts anteriores